diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-13 09:08:01 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-13 09:08:01 +0000 |
commit | 17b91a3c6ab73fff087e91665e9afb8046cbf045 (patch) | |
tree | 04655a8630478d9846571875f69469f018d4bdcc /app | |
parent | b3db40398ce9ad335270617e834fde96d46f90ea (diff) | |
download | gitlab-ce-17b91a3c6ab73fff087e91665e9afb8046cbf045.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/lib/utils/url_utility.js | 37 | ||||
-rw-r--r-- | app/controllers/projects/jobs_controller.rb | 4 | ||||
-rw-r--r-- | app/graphql/mutations/snippets/base.rb | 30 | ||||
-rw-r--r-- | app/graphql/mutations/snippets/create.rb | 77 | ||||
-rw-r--r-- | app/graphql/mutations/snippets/destroy.rb | 33 | ||||
-rw-r--r-- | app/graphql/mutations/snippets/update.rb | 54 | ||||
-rw-r--r-- | app/graphql/resolvers/base_resolver.rb | 10 | ||||
-rw-r--r-- | app/graphql/types/mutation_type.rb | 3 | ||||
-rw-r--r-- | app/graphql/types/snippet_type.rb | 4 | ||||
-rw-r--r-- | app/graphql/types/visibility_levels_enum.rb | 9 | ||||
-rw-r--r-- | app/presenters/snippet_presenter.rb | 2 | ||||
-rw-r--r-- | app/views/admin/runners/index.html.haml | 4 |
12 files changed, 257 insertions, 10 deletions
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 202a44d5694..6a61d92d9e8 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -1,4 +1,6 @@ -import { join as joinPaths } from 'path'; +const PATH_SEPARATOR = '/'; +const PATH_SEPARATOR_LEADING_REGEX = new RegExp(`^${PATH_SEPARATOR}+`); +const PATH_SEPARATOR_ENDING_REGEX = new RegExp(`${PATH_SEPARATOR}+$`); // Returns a decoded url parameter value // - Treats '+' as '%20' @@ -6,6 +8,37 @@ function decodeUrlParameter(val) { return decodeURIComponent(val.replace(/\+/g, '%20')); } +function cleanLeadingSeparator(path) { + return path.replace(PATH_SEPARATOR_LEADING_REGEX, ''); +} + +function cleanEndingSeparator(path) { + return path.replace(PATH_SEPARATOR_ENDING_REGEX, ''); +} + +/** + * Safely joins the given paths which might both start and end with a `/` + * + * Example: + * - `joinPaths('abc/', '/def') === 'abc/def'` + * - `joinPaths(null, 'abc/def', 'zoo) === 'abc/def/zoo'` + * + * @param {...String} paths + * @returns {String} + */ +export function joinPaths(...paths) { + return paths.reduce((acc, path) => { + if (!path) { + return acc; + } + if (!acc) { + return path; + } + + return [cleanEndingSeparator(acc), PATH_SEPARATOR, cleanLeadingSeparator(path)].join(''); + }, ''); +} + // Returns an array containing the value(s) of the // of the key passed as an argument export function getParameterValues(sParam, url = window.location) { @@ -212,5 +245,3 @@ export function objectToQuery(obj) { .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`) .join('&'); } - -export { joinPaths }; diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 9480900b57a..796f3ff603f 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -12,7 +12,7 @@ class Projects::JobsController < Projects::ApplicationController before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize] before_action :verify_api_request!, only: :terminal_websocket_authorize before_action only: [:show] do - push_frontend_feature_flag(:job_log_json, project) + push_frontend_feature_flag(:job_log_json, project, default_enabled: true) end layout 'project' @@ -53,7 +53,7 @@ class Projects::JobsController < Projects::ApplicationController format.json do # TODO: when the feature flag is removed we should not pass # content_format to serialize method. - content_format = Feature.enabled?(:job_log_json, @project) ? :json : :html + content_format = Feature.enabled?(:job_log_json, @project, default_enabled: true) ? :json : :html build_trace = Ci::BuildTrace.new( build: @build, diff --git a/app/graphql/mutations/snippets/base.rb b/app/graphql/mutations/snippets/base.rb new file mode 100644 index 00000000000..9dc6d49774e --- /dev/null +++ b/app/graphql/mutations/snippets/base.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Mutations + module Snippets + class Base < BaseMutation + field :snippet, + Types::SnippetType, + null: true, + description: 'The snippet after mutation' + + private + + def find_object(id:) + GitlabSchema.object_from_id(id) + end + + def authorized_resource?(snippet) + Ability.allowed?(context[:current_user], ability_for(snippet), snippet) + end + + def ability_for(snippet) + "#{ability_name}_#{snippet.to_ability_name}".to_sym + end + + def ability_name + raise NotImplementedError + end + end + end +end diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb new file mode 100644 index 00000000000..fe1f543ea1a --- /dev/null +++ b/app/graphql/mutations/snippets/create.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Mutations + module Snippets + class Create < BaseMutation + include Mutations::ResolvesProject + + graphql_name 'CreateSnippet' + + field :snippet, + Types::SnippetType, + null: true, + description: 'The snippet after mutation' + + argument :title, GraphQL::STRING_TYPE, + required: true, + description: 'Title of the snippet' + + argument :file_name, GraphQL::STRING_TYPE, + required: false, + description: 'File name of the snippet' + + argument :content, GraphQL::STRING_TYPE, + required: true, + description: 'Content of the snippet' + + argument :description, GraphQL::STRING_TYPE, + required: false, + description: 'Description of the snippet' + + argument :visibility_level, Types::VisibilityLevelsEnum, + description: 'The visibility level of the snippet', + required: true + + argument :project_path, GraphQL::ID_TYPE, + required: false, + description: 'The project full path the snippet is associated with' + + def resolve(args) + project_path = args.delete(:project_path) + + if project_path.present? + project = find_project!(project_path: project_path) + elsif !can_create_personal_snippet? + raise_resource_not_avaiable_error! + end + + snippet = CreateSnippetService.new(project, + context[:current_user], + args).execute + + { + snippet: snippet.valid? ? snippet : nil, + errors: errors_on_object(snippet) + } + end + + private + + def find_project!(project_path:) + authorized_find!(full_path: project_path) + end + + def find_object(full_path:) + resolve_project(full_path: full_path) + end + + def authorized_resource?(project) + Ability.allowed?(context[:current_user], :create_project_snippet, project) + end + + def can_create_personal_snippet? + Ability.allowed?(context[:current_user], :create_personal_snippet) + end + end + end +end diff --git a/app/graphql/mutations/snippets/destroy.rb b/app/graphql/mutations/snippets/destroy.rb new file mode 100644 index 00000000000..115fcfd6488 --- /dev/null +++ b/app/graphql/mutations/snippets/destroy.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Mutations + module Snippets + class Destroy < Base + graphql_name 'DestroySnippet' + + ERROR_MSG = 'Error deleting the snippet' + + argument :id, + GraphQL::ID_TYPE, + required: true, + description: 'The global id of the snippet to destroy' + + def resolve(id:) + snippet = authorized_find!(id: id) + + result = snippet.destroy + errors = result ? [] : [ERROR_MSG] + + { + errors: errors + } + end + + private + + def ability_name + "admin" + end + end + end +end diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb new file mode 100644 index 00000000000..27c232bc7f8 --- /dev/null +++ b/app/graphql/mutations/snippets/update.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Mutations + module Snippets + class Update < Base + graphql_name 'UpdateSnippet' + + argument :id, + GraphQL::ID_TYPE, + required: true, + description: 'The global id of the snippet to update' + + argument :title, GraphQL::STRING_TYPE, + required: false, + description: 'Title of the snippet' + + argument :file_name, GraphQL::STRING_TYPE, + required: false, + description: 'File name of the snippet' + + argument :content, GraphQL::STRING_TYPE, + required: false, + description: 'Content of the snippet' + + argument :description, GraphQL::STRING_TYPE, + required: false, + description: 'Description of the snippet' + + argument :visibility_level, Types::VisibilityLevelsEnum, + description: 'The visibility level of the snippet', + required: false + + def resolve(args) + snippet = authorized_find!(id: args.delete(:id)) + + result = UpdateSnippetService.new(snippet.project, + context[:current_user], + snippet, + args).execute + + { + snippet: result ? snippet : snippet.reset, + errors: errors_on_object(snippet) + } + end + + private + + def ability_name + "update" + end + end + end +end diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb index 85d6b377934..62dcc41dd9c 100644 --- a/app/graphql/resolvers/base_resolver.rb +++ b/app/graphql/resolvers/base_resolver.rb @@ -2,6 +2,8 @@ module Resolvers class BaseResolver < GraphQL::Schema::Resolver + extend ::Gitlab::Utils::Override + def self.single @single ||= Class.new(self) do def resolve(**args) @@ -36,5 +38,13 @@ module Resolvers # complexity difference is minimal in this case. [args[:iid], args[:iids]].any? ? 0 : 0.01 end + + override :object + def object + super.tap do |obj| + # If the field this resolver is used in is wrapped in a presenter, go back to it's subject + break obj.subject if obj.is_a?(Gitlab::View::Presenter::Base) + end + end end end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index e8f4ec06177..998dfdc7815 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -25,6 +25,9 @@ module Types mount_mutation Mutations::Todos::MarkDone mount_mutation Mutations::Todos::Restore mount_mutation Mutations::Todos::MarkAllDone + mount_mutation Mutations::Snippets::Destroy + mount_mutation Mutations::Snippets::Update + mount_mutation Mutations::Snippets::Create end end diff --git a/app/graphql/types/snippet_type.rb b/app/graphql/types/snippet_type.rb index 3b4dce1d486..3f780528945 100644 --- a/app/graphql/types/snippet_type.rb +++ b/app/graphql/types/snippet_type.rb @@ -44,8 +44,8 @@ module Types description: 'Description of the snippet', null: true - field :visibility, GraphQL::STRING_TYPE, - description: 'Visibility of the snippet', + field :visibility_level, Types::VisibilityLevelsEnum, + description: 'Visibility Level of the snippet', null: false field :created_at, Types::TimeType, diff --git a/app/graphql/types/visibility_levels_enum.rb b/app/graphql/types/visibility_levels_enum.rb new file mode 100644 index 00000000000..d5ace24455e --- /dev/null +++ b/app/graphql/types/visibility_levels_enum.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Types + class VisibilityLevelsEnum < BaseEnum + Gitlab::VisibilityLevel.string_options.each do |name, int_value| + value name.downcase, value: int_value + end + end +end diff --git a/app/presenters/snippet_presenter.rb b/app/presenters/snippet_presenter.rb index ca8ae8d60c4..37c9ebd3305 100644 --- a/app/presenters/snippet_presenter.rb +++ b/app/presenters/snippet_presenter.rb @@ -30,6 +30,6 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated end def ability_name(ability_prefix) - "#{ability_prefix}_#{snippet.class.underscore}".to_sym + "#{ability_prefix}_#{snippet.to_ability_name}".to_sym end end diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 2bf2b5fce8d..f8ef7a45f7f 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -10,7 +10,7 @@ %br %div - %span= _('Each Runner can be in one of the following states:') + %span= _('Each Runner can be in one of the following states and/or belong to one of the following types:') %ul %li %span.badge.badge-success shared @@ -120,7 +120,7 @@ .runners-content.content-list .table-holder .gl-responsive-table-row.table-row-header{ role: 'row' } - .table-section.section-10{ role: 'rowheader' }= _('Type') + .table-section.section-10{ role: 'rowheader' }= _('Type/State') .table-section.section-10{ role: 'rowheader' }= _('Runner token') .table-section.section-20{ role: 'rowheader' }= _('Description') .table-section.section-10{ role: 'rowheader' }= _('Version') |