diff options
author | Nick Thomas <nick@gitlab.com> | 2018-10-03 02:00:38 +0300 |
---|---|---|
committer | Nick Thomas <nick@gitlab.com> | 2018-10-05 13:34:43 +0300 |
commit | 25bd49e4f57fe15f9d61dc9376a5b7dc35b30f64 (patch) | |
tree | faef4e9d73e9845413462013c868eace19a11abf /app | |
parent | ae014e189773f7299c12c1050334b3e8fe7b15d8 (diff) |
Backport project template API to CE
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/api.js | 51 | ||||
-rw-r--r-- | app/assets/javascripts/blob/file_template_mediator.js | 42 | ||||
-rw-r--r-- | app/assets/javascripts/blob/template_selector.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/blob/template_selectors/dockerfile_selector.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/blob/template_selectors/gitignore_selector.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/blob/template_selectors/license_selector.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/blob_edit/blob_bundle.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/blob_edit/edit_blob.js | 24 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/modules/file_templates/actions.js | 12 | ||||
-rw-r--r-- | app/finders/license_template_finder.rb | 40 | ||||
-rw-r--r-- | app/finders/template_finder.rb | 19 | ||||
-rw-r--r-- | app/helpers/blob_helper.rb | 27 | ||||
-rw-r--r-- | app/models/license_template.rb | 8 |
14 files changed, 122 insertions, 123 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index ecc2440c7e6..3f7a1ef1bfc 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -15,12 +15,9 @@ const Api = { mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes', mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions', groupLabelsPath: '/groups/:namespace_path/-/labels', - templatesPath: '/api/:version/templates/:key', - licensePath: '/api/:version/templates/licenses/:key', - gitignorePath: '/api/:version/templates/gitignores/:key', - gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key', - dockerfilePath: '/api/:version/templates/dockerfiles/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', + projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key', + projectTemplatesPath: '/api/:version/projects/:id/templates/:type', usersPath: '/api/:version/users.json', userStatusPath: '/api/:version/user/status', commitPath: '/api/:version/projects/:id/repository/commits', @@ -196,29 +193,29 @@ const Api = { return axios.get(url); }, - // Return text for a specific license - licenseText(key, data, callback) { - const url = Api.buildUrl(Api.licensePath).replace(':key', key); - return axios - .get(url, { - params: data, - }) - .then(res => callback(res.data)); - }, + projectTemplate(id, type, key, options, callback) { + const url = Api.buildUrl(this.projectTemplatePath) + .replace(':id', encodeURIComponent(id)) + .replace(':type', type) + .replace(':key', encodeURIComponent(key)); - gitignoreText(key, callback) { - const url = Api.buildUrl(Api.gitignorePath).replace(':key', key); - return axios.get(url).then(({ data }) => callback(data)); - }, + return axios.get(url, { params: options }).then(res => { + if (callback) callback(res.data); - gitlabCiYml(key, callback) { - const url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key); - return axios.get(url).then(({ data }) => callback(data)); + return res; + }); }, - dockerfileYml(key, callback) { - const url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); - return axios.get(url).then(({ data }) => callback(data)); + projectTemplates(id, type, params = {}, callback) { + const url = Api.buildUrl(this.projectTemplatesPath) + .replace(':id', encodeURIComponent(id)) + .replace(':type', type); + + return axios.get(url, { params }).then(res => { + if (callback) callback(res.data); + + return res; + }); }, issueTemplate(namespacePath, projectPath, key, type, callback) { @@ -276,12 +273,6 @@ const Api = { }); }, - templates(key, params = {}) { - const url = Api.buildUrl(this.templatesPath).replace(':key', key); - - return axios.get(url, { params }); - }, - buildUrl(url) { let urlRoot = ''; if (gon.relative_url_root != null) { diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js index ff1cbcad145..addacf29f1e 100644 --- a/app/assets/javascripts/blob/file_template_mediator.js +++ b/app/assets/javascripts/blob/file_template_mediator.js @@ -1,4 +1,4 @@ -/* eslint-disable class-methods-use-this */ +import Api from '~/api'; import $ from 'jquery'; import Flash from '../flash'; @@ -9,9 +9,10 @@ import GitignoreSelector from './template_selectors/gitignore_selector'; import LicenseSelector from './template_selectors/license_selector'; export default class FileTemplateMediator { - constructor({ editor, currentAction }) { + constructor({ editor, currentAction, projectId }) { this.editor = editor; this.currentAction = currentAction; + this.projectId = projectId; this.initTemplateSelectors(); this.initTemplateTypeSelector(); @@ -33,15 +34,14 @@ export default class FileTemplateMediator { initTemplateTypeSelector() { this.typeSelector = new FileTemplateTypeSelector({ mediator: this, - dropdownData: this.templateSelectors - .map((templateSelector) => { - const cfg = templateSelector.config; - - return { - name: cfg.name, - key: cfg.key, - }; - }), + dropdownData: this.templateSelectors.map(templateSelector => { + const cfg = templateSelector.config; + + return { + name: cfg.name, + key: cfg.key, + }; + }), }); } @@ -89,7 +89,7 @@ export default class FileTemplateMediator { } listenForPreviewMode() { - this.$navLinks.on('click', 'a', (e) => { + this.$navLinks.on('click', 'a', e => { const urlPieces = e.target.href.split('#'); const hash = urlPieces[1]; if (hash === 'preview') { @@ -105,7 +105,7 @@ export default class FileTemplateMediator { e.preventDefault(); } - this.templateSelectors.forEach((selector) => { + this.templateSelectors.forEach(selector => { if (selector.config.key === item.key) { selector.show(); } else { @@ -126,8 +126,8 @@ export default class FileTemplateMediator { selector.renderLoading(); // in case undo menu is already already there this.destroyUndoMenu(); - this.fetchFileTemplate(selector.config.endpoint, query, data) - .then((file) => { + this.fetchFileTemplate(selector.config.type, query, data) + .then(file => { this.showUndoMenu(); this.setEditorContent(file); this.setFilename(selector.config.name); @@ -138,7 +138,7 @@ export default class FileTemplateMediator { displayMatchedTemplateSelector() { const currentInput = this.getFilename(); - this.templateSelectors.forEach((selector) => { + this.templateSelectors.forEach(selector => { const match = selector.config.pattern.test(currentInput); if (match) { @@ -149,15 +149,11 @@ export default class FileTemplateMediator { }); } - fetchFileTemplate(apiCall, query, data) { - return new Promise((resolve) => { + fetchFileTemplate(type, query, data = {}) { + return new Promise(resolve => { const resolveFile = file => resolve(file); - if (!data) { - apiCall(query, resolveFile); - } else { - apiCall(query, data, resolveFile); - } + Api.projectTemplate(this.projectId, type, query, data, resolveFile); }); } diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js index 9dfdb06007d..9db1fa70ffb 100644 --- a/app/assets/javascripts/blob/template_selector.js +++ b/app/assets/javascripts/blob/template_selector.js @@ -66,9 +66,6 @@ export default class TemplateSelector { // be added by all subclasses. } - // To be implemented on the extending class - // e.g. Api.gitlabCiYml(query.name, file => this.setEditorContent(file)); - setEditorContent(file, { skipFocus } = {}) { if (!file) return; diff --git a/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js b/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js index 9c41e429c8d..43f7aead8b9 100644 --- a/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js +++ b/app/assets/javascripts/blob/template_selectors/ci_yaml_selector.js @@ -1,5 +1,3 @@ -import Api from '../../api'; - import FileTemplateSelector from '../file_template_selector'; export default class BlobCiYamlSelector extends FileTemplateSelector { @@ -9,7 +7,7 @@ export default class BlobCiYamlSelector extends FileTemplateSelector { key: 'gitlab-ci-yaml', name: '.gitlab-ci.yml', pattern: /(.gitlab-ci.yml)/, - endpoint: Api.gitlabCiYml, + type: 'gitlab_ci_ymls', dropdown: '.js-gitlab-ci-yml-selector', wrapper: '.js-gitlab-ci-yml-selector-wrap', }; diff --git a/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js b/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js index 45fb614fe00..4718b642617 100644 --- a/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js +++ b/app/assets/javascripts/blob/template_selectors/dockerfile_selector.js @@ -1,5 +1,3 @@ -import Api from '../../api'; - import FileTemplateSelector from '../file_template_selector'; export default class DockerfileSelector extends FileTemplateSelector { @@ -9,7 +7,7 @@ export default class DockerfileSelector extends FileTemplateSelector { key: 'dockerfile', name: 'Dockerfile', pattern: /(Dockerfile)/, - endpoint: Api.dockerfileYml, + type: 'dockerfiles', dropdown: '.js-dockerfile-selector', wrapper: '.js-dockerfile-selector-wrap', }; diff --git a/app/assets/javascripts/blob/template_selectors/gitignore_selector.js b/app/assets/javascripts/blob/template_selectors/gitignore_selector.js index a894953cc86..a8067ec5c84 100644 --- a/app/assets/javascripts/blob/template_selectors/gitignore_selector.js +++ b/app/assets/javascripts/blob/template_selectors/gitignore_selector.js @@ -1,5 +1,3 @@ -import Api from '../../api'; - import FileTemplateSelector from '../file_template_selector'; export default class BlobGitignoreSelector extends FileTemplateSelector { @@ -9,7 +7,7 @@ export default class BlobGitignoreSelector extends FileTemplateSelector { key: 'gitignore', name: '.gitignore', pattern: /(.gitignore)/, - endpoint: Api.gitignoreText, + type: 'gitignores', dropdown: '.js-gitignore-selector', wrapper: '.js-gitignore-selector-wrap', }; diff --git a/app/assets/javascripts/blob/template_selectors/license_selector.js b/app/assets/javascripts/blob/template_selectors/license_selector.js index b7c4da0f62e..ac1fe95eee5 100644 --- a/app/assets/javascripts/blob/template_selectors/license_selector.js +++ b/app/assets/javascripts/blob/template_selectors/license_selector.js @@ -1,5 +1,3 @@ -import Api from '../../api'; - import FileTemplateSelector from '../file_template_selector'; export default class BlobLicenseSelector extends FileTemplateSelector { @@ -9,7 +7,7 @@ export default class BlobLicenseSelector extends FileTemplateSelector { key: 'license', name: 'LICENSE', pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i, - endpoint: Api.licenseText, + type: 'licenses', dropdown: '.js-license-selector', wrapper: '.js-license-selector-wrap', }; diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index a603d89b84a..4e4598870fa 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -15,8 +15,9 @@ export default () => { const assetsPath = editBlobForm.data('assetsPrefix'); const blobLanguage = editBlobForm.data('blobLanguage'); const currentAction = $('.js-file-title').data('currentAction'); + const projectId = editBlobForm.data('project-id'); - new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction); + new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction, projectId); new NewCommitForm(editBlobForm); } diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index 82a3d494b67..ec2b130ab7d 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -7,11 +7,11 @@ import { __ } from '~/locale'; import TemplateSelectorMediator from '../blob/file_template_mediator'; export default class EditBlob { - constructor(assetsPath, aceMode, currentAction) { + constructor(assetsPath, aceMode, currentAction, projectId) { this.configureAceEditor(aceMode, assetsPath); this.initModePanesAndLinks(); this.initSoftWrap(); - this.initFileSelectors(currentAction); + this.initFileSelectors(currentAction, projectId); } configureAceEditor(aceMode, assetsPath) { @@ -30,10 +30,11 @@ export default class EditBlob { } } - initFileSelectors(currentAction) { + initFileSelectors(currentAction, projectId) { this.fileTemplateMediator = new TemplateSelectorMediator({ currentAction, editor: this.editor, + projectId, }); } @@ -60,14 +61,15 @@ export default class EditBlob { if (paneId === '#preview') { this.$toggleButton.hide(); - axios.post(currentLink.data('previewUrl'), { - content: this.editor.getValue(), - }) - .then(({ data }) => { - currentPane.empty().append(data); - currentPane.renderGFM(); - }) - .catch(() => createFlash(__('An error occurred previewing the blob'))); + axios + .post(currentLink.data('previewUrl'), { + content: this.editor.getValue(), + }) + .then(({ data }) => { + currentPane.empty().append(data); + currentPane.renderGFM(); + }) + .catch(() => createFlash(__('An error occurred previewing the blob'))); } this.$toggleButton.show(); diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js index cc9f6c8638c..b7090e09daf 100644 --- a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js +++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js @@ -23,12 +23,12 @@ export const receiveTemplateTypesError = ({ commit, dispatch }) => { export const receiveTemplateTypesSuccess = ({ commit }, templates) => commit(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, templates); -export const fetchTemplateTypes = ({ dispatch, state }, page = 1) => { +export const fetchTemplateTypes = ({ dispatch, state, rootState }, page = 1) => { if (!Object.keys(state.selectedTemplateType).length) return Promise.reject(); dispatch('requestTemplateTypes'); - return Api.templates(state.selectedTemplateType.key, { page }) + return Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, { page }) .then(({ data, headers }) => { const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10); @@ -74,12 +74,16 @@ export const receiveTemplateError = ({ dispatch }, template) => { ); }; -export const fetchTemplate = ({ dispatch, state }, template) => { +export const fetchTemplate = ({ dispatch, state, rootState }, template) => { if (template.content) { return dispatch('setFileTemplate', template); } - return Api.templates(`${state.selectedTemplateType.key}/${template.key || template.name}`) + return Api.projectTemplate( + rootState.currentProjectId, + state.selectedTemplateType.key, + template.key || template.name, + ) .then(({ data }) => { dispatch('setFileTemplate', data); }) diff --git a/app/finders/license_template_finder.rb b/app/finders/license_template_finder.rb index 196922709f7..d735a4c1d69 100644 --- a/app/finders/license_template_finder.rb +++ b/app/finders/license_template_finder.rb @@ -5,33 +5,47 @@ # Used to find license templates, which may come from a variety of external # sources # -# Arguments: +# Params can be any of the following: # popular: boolean. When set to true, only "popular" licenses are shown. When # false, all licenses except popular ones are shown. When nil (the # default), *all* licenses will be shown. +# name: string. If set, return a single license matching that name (or nil) class LicenseTemplateFinder - attr_reader :params + include Gitlab::Utils::StrongMemoize - def initialize(params = {}) + attr_reader :project, :params + + def initialize(project, params = {}) + @project = project @params = params end def execute - Licensee::License.all(featured: popular_only?).map do |license| - LicenseTemplate.new( - id: license.key, - name: license.name, - nickname: license.nickname, - category: (license.featured? ? :Popular : :Other), - content: license.content, - url: license.url, - meta: license.meta - ) + if params[:name] + vendored_licenses.find { |template| template.key == params[:name] } + else + vendored_licenses end end private + def vendored_licenses + strong_memoize(:vendored_licenses) do + Licensee::License.all(featured: popular_only?).map do |license| + LicenseTemplate.new( + key: license.key, + name: license.name, + nickname: license.nickname, + category: (license.featured? ? :Popular : :Other), + content: license.content, + url: license.url, + meta: license.meta + ) + end + end + end + def popular_only? params.fetch(:popular, nil) end diff --git a/app/finders/template_finder.rb b/app/finders/template_finder.rb index c92ee9ca9ac..3e483716064 100644 --- a/app/finders/template_finder.rb +++ b/app/finders/template_finder.rb @@ -1,29 +1,32 @@ # frozen_string_literal: true class TemplateFinder - VENDORED_TEMPLATES = { + include Gitlab::Utils::StrongMemoize + + VENDORED_TEMPLATES = HashWithIndifferentAccess.new( dockerfiles: ::Gitlab::Template::DockerfileTemplate, gitignores: ::Gitlab::Template::GitignoreTemplate, gitlab_ci_ymls: ::Gitlab::Template::GitlabCiYmlTemplate - }.freeze + ).freeze class << self - def build(type, params = {}) - if type == :licenses - LicenseTemplateFinder.new(params) # rubocop: disable CodeReuse/Finder + def build(type, project, params = {}) + if type.to_s == 'licenses' + LicenseTemplateFinder.new(project, params) # rubocop: disable CodeReuse/Finder else - new(type, params) + new(type, project, params) end end end - attr_reader :type, :params + attr_reader :type, :project, :params attr_reader :vendored_templates private :vendored_templates - def initialize(type, params = {}) + def initialize(type, project, params = {}) @type = type + @project = project @params = params @vendored_templates = VENDORED_TEMPLATES.fetch(type) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 9cbd5b5f785..883e5ddff57 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -159,10 +159,6 @@ module BlobHelper end end - def licenses_for_select - @licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses).execute) - end - def ref_project @ref_project ||= @target_project || @project end @@ -173,29 +169,34 @@ module BlobHelper categories.each_with_object({}) do |category, hash| hash[category] = grouped[category].map do |item| - { name: item.name, id: item.id } + { name: item.name, id: item.key } end end end private :template_dropdown_names - def gitignore_names - @gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores).execute) + def licenses_for_select(project = @project) + @licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses, project).execute) + end + + def gitignore_names(project = @project) + @gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores, project).execute) end - def gitlab_ci_ymls - @gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls).execute) + def gitlab_ci_ymls(project = @project) + @gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls, project).execute) end - def dockerfile_names - @dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles).execute) + def dockerfile_names(project = @project) + @dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles, project).execute) end - def blob_editor_paths + def blob_editor_paths(project = @project) { 'relative-url-root' => Rails.application.config.relative_url_root, 'assets-prefix' => Gitlab::Application.config.assets.prefix, - 'blob-language' => @blob && @blob.language.try(:ace_mode) + 'blob-language' => @blob && @blob.language.try(:ace_mode), + 'project-id' => project.id } end diff --git a/app/models/license_template.rb b/app/models/license_template.rb index 693a6a89fd2..73e403f98b4 100644 --- a/app/models/license_template.rb +++ b/app/models/license_template.rb @@ -12,12 +12,10 @@ class LicenseTemplate (fullname|name\sof\s(author|copyright\sowner)) [\>\}\]]}xi.freeze - attr_reader :id, :name, :category, :nickname, :url, :meta + attr_reader :key, :name, :category, :nickname, :url, :meta - alias_method :key, :id - - def initialize(id:, name:, category:, content:, nickname: nil, url: nil, meta: {}) - @id = id + def initialize(key:, name:, category:, content:, nickname: nil, url: nil, meta: {}) + @key = key @name = name @category = category @content = content |