diff options
Diffstat (limited to 'app/graphql/mutations')
24 files changed, 287 insertions, 151 deletions
diff --git a/app/graphql/mutations/award_emojis/toggle.rb b/app/graphql/mutations/award_emojis/toggle.rb index 5da2731d562..a419a8df64e 100644 --- a/app/graphql/mutations/award_emojis/toggle.rb +++ b/app/graphql/mutations/award_emojis/toggle.rb @@ -5,9 +5,10 @@ module Mutations class Toggle < Base graphql_name 'AwardEmojiToggle' - field :toggled_on, GraphQL::Types::Boolean, null: false, - description: 'Indicates the status of the emoji. ' \ - 'True if the toggle awarded the emoji, and false if the toggle removed the emoji.' + field :toggled_on, GraphQL::Types::Boolean, + null: false, + description: 'Indicates the status of the emoji. ' \ + 'True if the toggle awarded the emoji, and false if the toggle removed the emoji.' def resolve(args) awardable = authorized_find!(id: args[:awardable_id]) diff --git a/app/graphql/mutations/ci/job/retry.rb b/app/graphql/mutations/ci/job/retry.rb index 50e9c51c9e7..bfb9b902cc5 100644 --- a/app/graphql/mutations/ci/job/retry.rb +++ b/app/graphql/mutations/ci/job/retry.rb @@ -11,13 +11,20 @@ module Mutations null: true, description: 'Job after the mutation.' + argument :variables, [::Types::Ci::VariableInputType], + required: false, + default_value: [], + replace_null_with_default: true, + description: 'Variables to use when retrying a manual job.' + authorize :update_build - def resolve(id:) + def resolve(id:, variables:) job = authorized_find!(id: id) project = job.project + variables = variables.map(&:to_h) - response = ::Ci::RetryJobService.new(project, current_user).execute(job) + response = ::Ci::RetryJobService.new(project, current_user).execute(job, variables: variables) if response.success? { diff --git a/app/graphql/mutations/ci/pipeline/cancel.rb b/app/graphql/mutations/ci/pipeline/cancel.rb index 3ec6eee9f54..c52e3b4f4b8 100644 --- a/app/graphql/mutations/ci/pipeline/cancel.rb +++ b/app/graphql/mutations/ci/pipeline/cancel.rb @@ -13,7 +13,6 @@ module Mutations if pipeline.cancelable? pipeline.cancel_running - pipeline.cancel { success: true, errors: [] } else diff --git a/app/graphql/mutations/ci/runner/bulk_delete.rb b/app/graphql/mutations/ci/runner/bulk_delete.rb new file mode 100644 index 00000000000..4c1c2967799 --- /dev/null +++ b/app/graphql/mutations/ci/runner/bulk_delete.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module Runner + class BulkDelete < BaseMutation + graphql_name 'BulkRunnerDelete' + + RunnerID = ::Types::GlobalIDType[::Ci::Runner] + + argument :ids, [RunnerID], + required: false, + description: 'IDs of the runners to delete.' + + field :deleted_count, + ::GraphQL::Types::Int, + null: true, + description: 'Number of records effectively deleted. ' \ + 'Only present if operation was performed synchronously.' + + field :deleted_ids, + [RunnerID], + null: true, + description: 'IDs of records effectively deleted. ' \ + 'Only present if operation was performed synchronously.' + + def resolve(**runner_attrs) + raise_resource_not_available_error! unless Ability.allowed?(current_user, :delete_runners) + + if ids = runner_attrs[:ids] + runners = find_all_runners_by_ids(model_ids_of(ids)) + + result = ::Ci::Runners::BulkDeleteRunnersService.new(runners: runners).execute + result.payload.slice(:deleted_count, :deleted_ids).merge(errors: []) + else + { errors: [] } + end + end + + private + + def model_ids_of(ids) + ids.map do |gid| + gid.model_id.to_i + end.compact + end + + def find_all_runners_by_ids(ids) + return ::Ci::Runner.none if ids.blank? + + ::Ci::Runner.id_in(ids) + end + end + end + end +end diff --git a/app/graphql/mutations/ci/runner/update.rb b/app/graphql/mutations/ci/runner/update.rb index b6d8c20c40b..1c6cf6989bf 100644 --- a/app/graphql/mutations/ci/runner/update.rb +++ b/app/graphql/mutations/ci/runner/update.rb @@ -39,15 +39,17 @@ module Mutations required: false, description: 'Indicates the runner is not allowed to receive jobs.' - argument :locked, GraphQL::Types::Boolean, required: false, - description: 'Indicates the runner is locked.' + argument :locked, GraphQL::Types::Boolean, + required: false, + description: 'Indicates the runner is locked.' argument :run_untagged, GraphQL::Types::Boolean, required: false, description: 'Indicates the runner is able to run untagged jobs.' - argument :tag_list, [GraphQL::Types::String], required: false, - description: 'Tags associated with the runner.' + argument :tag_list, [GraphQL::Types::String], + required: false, + description: 'Tags associated with the runner.' field :runner, Types::Ci::RunnerType, diff --git a/app/graphql/mutations/ci/runners_registration_token/reset.rb b/app/graphql/mutations/ci/runners_registration_token/reset.rb index 8c49b682ab0..c9fe7ea47f0 100644 --- a/app/graphql/mutations/ci/runners_registration_token/reset.rb +++ b/app/graphql/mutations/ci/runners_registration_token/reset.rb @@ -49,7 +49,10 @@ module Mutations end def reset_token(scope) - ::Ci::Runners::ResetRegistrationTokenService.new(scope, current_user).execute if scope + return unless scope + + result = ::Ci::Runners::ResetRegistrationTokenService.new(scope, current_user).execute + result.payload[:new_registration_token] if result.success? end end end diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb index cbe1cfb4099..1f90f394521 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -15,15 +15,21 @@ module Mutations argument :title, GraphQL::Types::String, required: false, description: copy_field_description(Types::WorkItemType, :title) + argument :confidential, GraphQL::Types::Boolean, + required: false, + description: 'Sets the work item confidentiality.' argument :description_widget, ::Types::WorkItems::Widgets::DescriptionInputType, required: false, description: 'Input for description widget.' - argument :weight_widget, ::Types::WorkItems::Widgets::WeightInputType, + argument :assignees_widget, ::Types::WorkItems::Widgets::AssigneesInputType, required: false, - description: 'Input for weight widget.' + description: 'Input for assignees widget.' argument :hierarchy_widget, ::Types::WorkItems::Widgets::HierarchyUpdateInputType, required: false, description: 'Input for hierarchy widget.' + argument :start_and_due_date_widget, ::Types::WorkItems::Widgets::StartAndDueDateUpdateInputType, + required: false, + description: 'Input for start and due date widget.' end end end diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb index 1d8f7b22f88..2a45291be22 100644 --- a/app/graphql/mutations/container_repositories/destroy.rb +++ b/app/graphql/mutations/container_repositories/destroy.rb @@ -21,7 +21,9 @@ module Mutations container_repository = authorized_find!(id: id) container_repository.delete_scheduled! + # rubocop:disable CodeReuse/Worker DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id) + # rubocop:enable CodeReuse/Worker track_event(:delete_repository, :container) { diff --git a/app/graphql/mutations/design_management/move.rb b/app/graphql/mutations/design_management/move.rb index b19d9b4ce61..cf7b7a7b99b 100644 --- a/app/graphql/mutations/design_management/move.rb +++ b/app/graphql/mutations/design_management/move.rb @@ -7,18 +7,21 @@ module Mutations DesignID = ::Types::GlobalIDType[::DesignManagement::Design] - argument :id, DesignID, required: true, as: :current_design, - description: "ID of the design to move." + argument :id, DesignID, + required: true, as: :current_design, + description: "ID of the design to move." - argument :previous, DesignID, required: false, as: :previous_design, - description: "ID of the immediately preceding design." + argument :previous, DesignID, + required: false, as: :previous_design, + description: "ID of the immediately preceding design." - argument :next, DesignID, required: false, as: :next_design, - description: "ID of the immediately following design." + argument :next, DesignID, + required: false, as: :next_design, + description: "ID of the immediately following design." field :design_collection, Types::DesignManagement::DesignCollectionType, - null: true, - description: "Current state of the collection." + null: true, + description: "Current state of the collection." def resolve(**args) service = ::DesignManagement::MoveDesignsService.new(current_user, parameters(**args)) diff --git a/app/graphql/mutations/issues/move.rb b/app/graphql/mutations/issues/move.rb index fb22a2d891c..63bc9dabbf9 100644 --- a/app/graphql/mutations/issues/move.rb +++ b/app/graphql/mutations/issues/move.rb @@ -19,8 +19,8 @@ module Mutations begin moved_issue = ::Issues::MoveService.new(project: source_project, current_user: current_user).execute(issue, target_project) - rescue ::Issues::MoveService::MoveError => error - errors = error.message + rescue ::Issues::MoveService::MoveError => e + errors = e.message end { diff --git a/app/graphql/mutations/issues/set_confidential.rb b/app/graphql/mutations/issues/set_confidential.rb index abfd6fec0bd..b795d66c16f 100644 --- a/app/graphql/mutations/issues/set_confidential.rb +++ b/app/graphql/mutations/issues/set_confidential.rb @@ -24,7 +24,7 @@ module Mutations check_spam_action_response!(issue) { - issue: issue, + issue: issue.reset, errors: errors_on_object(issue) } end diff --git a/app/graphql/mutations/issues/set_severity.rb b/app/graphql/mutations/issues/set_severity.rb index 872a0e7b33d..4a24bfd18ef 100644 --- a/app/graphql/mutations/issues/set_severity.rb +++ b/app/graphql/mutations/issues/set_severity.rb @@ -5,8 +5,9 @@ module Mutations class SetSeverity < Base graphql_name 'IssueSetSeverity' - argument :severity, Types::IssuableSeverityEnum, required: true, - description: 'Set the incident severity level.' + argument :severity, Types::IssuableSeverityEnum, + required: true, + description: 'Set the incident severity level.' authorize :admin_issue diff --git a/app/graphql/mutations/merge_requests/remove_attention_request.rb b/app/graphql/mutations/merge_requests/remove_attention_request.rb deleted file mode 100644 index 3b12b09528b..00000000000 --- a/app/graphql/mutations/merge_requests/remove_attention_request.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module Mutations - module MergeRequests - class RemoveAttentionRequest < Base - graphql_name 'MergeRequestRemoveAttentionRequest' - - argument :user_id, ::Types::GlobalIDType[::User], - loads: Types::UserType, - required: true, - description: <<~DESC - User ID of the user for attention request removal. - DESC - - def resolve(project_path:, iid:, user:) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled? - - merge_request = authorized_find!(project_path: project_path, iid: iid) - - result = ::MergeRequests::RemoveAttentionRequestedService.new( - project: merge_request.project, - current_user: current_user, - merge_request: merge_request, - user: user - ).execute - - { - merge_request: merge_request, - errors: Array(result[:message]) - } - end - - private - - def feature_enabled? - current_user&.mr_attention_requests_enabled? - end - end - end -end diff --git a/app/graphql/mutations/merge_requests/request_attention.rb b/app/graphql/mutations/merge_requests/request_attention.rb deleted file mode 100644 index 5f5565285f6..00000000000 --- a/app/graphql/mutations/merge_requests/request_attention.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module Mutations - module MergeRequests - class RequestAttention < Base - graphql_name 'MergeRequestRequestAttention' - - argument :user_id, ::Types::GlobalIDType[::User], - loads: Types::UserType, - required: true, - description: <<~DESC - User ID of the user to request attention. - DESC - - def resolve(project_path:, iid:, user:) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled? - - merge_request = authorized_find!(project_path: project_path, iid: iid) - - result = ::MergeRequests::RequestAttentionService.new( - project: merge_request.project, - current_user: current_user, - merge_request: merge_request, - user: user - ).execute - - { - merge_request: merge_request, - errors: Array(result[:message]) - } - end - - private - - def feature_enabled? - current_user&.mr_attention_requests_enabled? - end - end - end -end diff --git a/app/graphql/mutations/merge_requests/set_reviewers.rb b/app/graphql/mutations/merge_requests/set_reviewers.rb new file mode 100644 index 00000000000..8d3f8601597 --- /dev/null +++ b/app/graphql/mutations/merge_requests/set_reviewers.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Mutations + module MergeRequests + class SetReviewers < Base + graphql_name 'MergeRequestSetReviewers' + + argument :reviewer_usernames, + [GraphQL::Types::String], + required: true, + description: 'Usernames of reviewers to assign. Replaces existing reviewers by default.' + + argument :operation_mode, + Types::MutationOperationModeEnum, + required: false, + default_value: Types::MutationOperationModeEnum.default_mode, + description: 'Operation to perform. Defaults to REPLACE.' + + def resolve(project_path:, iid:, reviewer_usernames:, operation_mode:) + resource = authorized_find!(project_path: project_path, iid: iid) + + ::MergeRequests::UpdateReviewersService.new( + project: resource.project, + current_user: current_user, + params: { reviewer_ids: reviewer_ids(resource, reviewer_usernames, operation_mode) } + ).execute(resource) + + { + resource.class.name.underscore.to_sym => resource, + errors: errors_on_object(resource) + } + end + + private + + def reviewer_ids(resource, usernames, mode) + new_reviewers = UsersFinder.new(current_user, username: usernames).execute.to_a + new_reviewer_ids = user_ids(new_reviewers) + + case mode + when 'REPLACE' then new_reviewer_ids + when 'APPEND' then user_ids(resource.reviewers) | new_reviewer_ids + when 'REMOVE' then user_ids(resource.reviewers) - new_reviewer_ids + end + end + + def user_ids(users) + users.map(&:id) + end + end + end +end diff --git a/app/graphql/mutations/merge_requests/toggle_attention_requested.rb b/app/graphql/mutations/merge_requests/toggle_attention_requested.rb deleted file mode 100644 index 8913ca48531..00000000000 --- a/app/graphql/mutations/merge_requests/toggle_attention_requested.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Mutations - module MergeRequests - class ToggleAttentionRequested < Base - graphql_name 'MergeRequestToggleAttentionRequested' - - argument :user_id, ::Types::GlobalIDType[::User], - loads: Types::UserType, - required: true, - description: <<~DESC - User ID for the user to toggle attention requested. - DESC - - def resolve(project_path:, iid:, user:) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless current_user&.mr_attention_requests_enabled? - - merge_request = authorized_find!(project_path: project_path, iid: iid) - - result = ::MergeRequests::ToggleAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute - - { - merge_request: merge_request, - errors: Array(result[:message]) - } - end - end - end -end diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb index 1b673204213..f48e62af767 100644 --- a/app/graphql/mutations/notes/create/base.rb +++ b/app/graphql/mutations/notes/create/base.rb @@ -21,7 +21,13 @@ module Mutations argument :confidential, GraphQL::Types::Boolean, required: false, - description: 'Confidentiality flag of a note. Default is false.' + description: 'Confidentiality flag of a note. Default is false.', + deprecated: { reason: :renamed, replacement: 'internal', milestone: '15.3' } + + argument :internal, + GraphQL::Types::Boolean, + required: false, + description: 'Internal flag for a note. Default is false.' def resolve(args) noteable = authorized_find!(id: args[:noteable_id]) @@ -49,7 +55,7 @@ module Mutations { noteable: noteable, note: args[:body], - confidential: args[:confidential] + internal: args[:internal] || args[:confidential] } end diff --git a/app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb b/app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb index e5bb5b6d573..3ccc90c16ae 100644 --- a/app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb +++ b/app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb @@ -7,14 +7,16 @@ module Mutations include FindsProject argument :project_path, GraphQL::Types::ID, - required: true, - description: 'Full path of the project.' + required: true, + description: 'Full path of the project.' - field :success_path, GraphQL::Types::String, null: true, - description: 'Redirect path to use when the response is successful.' + field :success_path, GraphQL::Types::String, + null: true, + description: 'Redirect path to use when the response is successful.' - field :branch, GraphQL::Types::String, null: true, - description: 'Branch that has the new/modified `.gitlab-ci.yml` file.' + field :branch, GraphQL::Types::String, + null: true, + description: 'Branch that has the new/modified `.gitlab-ci.yml` file.' authorize :push_code diff --git a/app/graphql/mutations/timelogs/base.rb b/app/graphql/mutations/timelogs/base.rb new file mode 100644 index 00000000000..9859f0e7d79 --- /dev/null +++ b/app/graphql/mutations/timelogs/base.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Mutations + module Timelogs + class Base < Mutations::BaseMutation + field :timelog, + Types::TimelogType, + null: true, + description: 'Timelog.' + + private + + def response(result) + { timelog: result.payload[:timelog], errors: result.errors } + end + end + end +end diff --git a/app/graphql/mutations/timelogs/create.rb b/app/graphql/mutations/timelogs/create.rb new file mode 100644 index 00000000000..bab7508454e --- /dev/null +++ b/app/graphql/mutations/timelogs/create.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Mutations + module Timelogs + class Create < Base + graphql_name 'TimelogCreate' + + argument :time_spent, + GraphQL::Types::String, + required: true, + description: 'Amount of time spent.' + + argument :spent_at, + Types::DateType, + required: true, + description: 'When the time was spent.' + + argument :summary, + GraphQL::Types::String, + required: true, + description: 'Summary of time spent.' + + argument :issuable_id, + ::Types::GlobalIDType[::Issuable], + required: true, + description: 'Global ID of the issuable (Issue, WorkItem or MergeRequest).' + + authorize :create_timelog + + def resolve(issuable_id:, time_spent:, spent_at:, summary:, **args) + issuable = authorized_find!(id: issuable_id) + parsed_time_spent = Gitlab::TimeTrackingFormatter.parse(time_spent) + + result = ::Timelogs::CreateService.new( + issuable, parsed_time_spent, spent_at, summary, current_user + ).execute + + response(result) + end + + private + + def find_object(id:) + GitlabSchema.object_from_id(id, expected_type: [::Issue, ::WorkItem, ::MergeRequest]).sync + end + end + end +end diff --git a/app/graphql/mutations/timelogs/delete.rb b/app/graphql/mutations/timelogs/delete.rb index 8fd41c27b88..61588d839a7 100644 --- a/app/graphql/mutations/timelogs/delete.rb +++ b/app/graphql/mutations/timelogs/delete.rb @@ -2,14 +2,9 @@ module Mutations module Timelogs - class Delete < Mutations::BaseMutation + class Delete < Base graphql_name 'TimelogDelete' - field :timelog, - Types::TimelogType, - null: true, - description: 'Deleted timelog.' - argument :id, ::Types::GlobalIDType[::Timelog], required: true, @@ -22,11 +17,13 @@ module Mutations result = ::Timelogs::DeleteService.new(timelog, current_user).execute # Return the result payload, not the loaded timelog, so that it returns null in case of unauthorized access - { timelog: result.payload, errors: result.errors } + response(result) end + private + def find_object(id:) - GitlabSchema.find_by_gid(id) + GitlabSchema.object_from_id(id, expected_type: ::Timelog).sync end end end diff --git a/app/graphql/mutations/uploads/delete.rb b/app/graphql/mutations/uploads/delete.rb new file mode 100644 index 00000000000..e2fb967cd2c --- /dev/null +++ b/app/graphql/mutations/uploads/delete.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Mutations + module Uploads + class Delete < BaseMutation + graphql_name 'UploadDelete' + description 'Deletes an upload.' + + include Mutations::ResolvesResourceParent + + authorize :destroy_upload + + argument :secret, GraphQL::Types::String, + required: true, + description: 'Secret part of upload path.' + + argument :filename, GraphQL::Types::String, + required: true, + description: 'Upload filename.' + + field :upload, Types::UploadType, + null: true, + description: 'Deleted upload.' + + def resolve(args) + parent = authorized_resource_parent_find!(args) + + result = ::Uploads::DestroyService.new(parent, current_user).execute(args[:secret], args[:filename]) + + { + upload: result[:status] == :success ? result[:upload] : nil, + errors: Array(result[:message]) + } + end + end + end +end diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 350153eaf19..ece00e04ed9 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -13,6 +13,9 @@ module Mutations authorize :create_work_item + argument :confidential, GraphQL::Types::Boolean, + required: false, + description: 'Sets the work item confidentiality.' argument :description, GraphQL::Types::String, required: false, description: copy_field_description(Types::WorkItemType, :description) diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb index 5d8c574877a..b4ed0a1a3ca 100644 --- a/app/graphql/mutations/work_items/update.rb +++ b/app/graphql/mutations/work_items/update.rb @@ -51,3 +51,5 @@ module Mutations end end end + +Mutations::WorkItems::Update.prepend_mod |