diff options
Diffstat (limited to 'app/graphql/mutations')
21 files changed, 307 insertions, 50 deletions
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb index d57a097a9e2..5f98b222099 100644 --- a/app/graphql/mutations/base_mutation.rb +++ b/app/graphql/mutations/base_mutation.rb @@ -39,14 +39,16 @@ module Mutations true end - def load_application_object(argument, lookup_as_type, id, context) - ::Gitlab::Graphql::Lazy.new { super }.catch(::GraphQL::UnauthorizedError) do |e| - Gitlab::ErrorTracking.track_exception(e) - # The default behaviour is to abort processing and return nil for the - # entire mutation field, but not set any top-level errors. We prefer to - # at least say that something went wrong. - raise_resource_not_available_error! - end + def load_application_object(argument, id, context) + ::Gitlab::Graphql::Lazy.new { super } + end + + def unauthorized_object(error) + # The default behavior is to abort processing and return nil for the + # entire mutation field, but not set any top-level errors. We prefer to + # at least say that something went wrong. + Gitlab::ErrorTracking.track_exception(error) + raise_resource_not_available_error! end def self.authorizes_object? diff --git a/app/graphql/mutations/ci/pipeline/destroy.rb b/app/graphql/mutations/ci/pipeline/destroy.rb index 3f933818ce1..935cf45c4ab 100644 --- a/app/graphql/mutations/ci/pipeline/destroy.rb +++ b/app/graphql/mutations/ci/pipeline/destroy.rb @@ -12,12 +12,25 @@ module Mutations pipeline = authorized_find!(id: id) project = pipeline.project + return undergoing_refresh_error(project) if project.refreshing_build_artifacts_size? + result = ::Ci::DestroyPipelineService.new(project, current_user).execute(pipeline) { success: result.success?, errors: result.errors } end + + private + + def undergoing_refresh_error(project) + Gitlab::ProjectStatsRefreshConflictsLogger.warn_request_rejected_during_stats_refresh(project.id) + + { + success: false, + errors: ['Action temporarily disabled. The project this pipeline belongs to is undergoing stats refresh.'] + } + end end end end diff --git a/app/graphql/mutations/ci/runner/update.rb b/app/graphql/mutations/ci/runner/update.rb index faccd1273e5..b6d8c20c40b 100644 --- a/app/graphql/mutations/ci/runner/update.rb +++ b/app/graphql/mutations/ci/runner/update.rb @@ -18,6 +18,10 @@ module Mutations required: false, description: 'Description of the runner.' + argument :maintenance_note, GraphQL::Types::String, + required: false, + description: 'Runner\'s maintenance notes.' + argument :maximum_timeout, GraphQL::Types::Int, required: false, description: 'Maximum timeout (in seconds) for jobs processed by the runner.' diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb new file mode 100644 index 00000000000..6a91a097a17 --- /dev/null +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + module UpdateArguments + extend ActiveSupport::Concern + + included do + argument :id, ::Types::GlobalIDType[::WorkItem], + required: true, + description: 'Global ID of the work item.' + argument :state_event, Types::WorkItems::StateEventEnum, + description: 'Close or reopen a work item.', + required: false + argument :title, GraphQL::Types::String, + required: false, + description: copy_field_description(Types::WorkItemType, :title) + end + end + end +end diff --git a/app/graphql/mutations/incident_management/timeline_event/create.rb b/app/graphql/mutations/incident_management/timeline_event/create.rb index cbc708a2530..1907954cada 100644 --- a/app/graphql/mutations/incident_management/timeline_event/create.rb +++ b/app/graphql/mutations/incident_management/timeline_event/create.rb @@ -23,7 +23,9 @@ module Mutations authorize!(incident) - response ::IncidentManagement::TimelineEvents::CreateService.new(incident, current_user, args).execute + response ::IncidentManagement::TimelineEvents::CreateService.new( + incident, current_user, args.merge(editable: true) + ).execute end private diff --git a/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb b/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb index 73a20b8a380..31ae29d896b 100644 --- a/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb +++ b/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb @@ -21,7 +21,8 @@ module Mutations current_user, promoted_from_note: note, note: note.note, - occurred_at: note.created_at + occurred_at: note.created_at, + editable: true ).execute end diff --git a/app/graphql/mutations/issues/set_crm_contacts.rb b/app/graphql/mutations/issues/set_crm_contacts.rb index 4df65e4769c..cc718b4ae33 100644 --- a/app/graphql/mutations/issues/set_crm_contacts.rb +++ b/app/graphql/mutations/issues/set_crm_contacts.rb @@ -48,7 +48,7 @@ module Mutations private def feature_enabled?(project) - Feature.enabled?(:customer_relations, project.group) && project.group&.crm_enabled? + project.group&.crm_enabled? end end end diff --git a/app/graphql/mutations/merge_requests/set_draft.rb b/app/graphql/mutations/merge_requests/set_draft.rb index ab4ca73e5dc..f83c1a0caf4 100644 --- a/app/graphql/mutations/merge_requests/set_draft.rb +++ b/app/graphql/mutations/merge_requests/set_draft.rb @@ -27,8 +27,8 @@ module Mutations private - def wip_event(wip) - wip ? 'wip' : 'unwip' + def wip_event(draft) + draft ? 'draft' : 'ready' end end end diff --git a/app/graphql/mutations/packages/cleanup/policy/update.rb b/app/graphql/mutations/packages/cleanup/policy/update.rb new file mode 100644 index 00000000000..e7ab7439949 --- /dev/null +++ b/app/graphql/mutations/packages/cleanup/policy/update.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Mutations + module Packages + module Cleanup + module Policy + class Update < Mutations::BaseMutation + graphql_name 'UpdatePackagesCleanupPolicy' + + include FindsProject + + authorize :admin_package + + argument :project_path, + GraphQL::Types::ID, + required: true, + description: 'Project path where the packages cleanup policy is located.' + + argument :keep_n_duplicated_package_files, + Types::Packages::Cleanup::KeepDuplicatedPackageFilesEnum, + required: false, + description: copy_field_description( + Types::Packages::Cleanup::PolicyType, + :keep_n_duplicated_package_files + ) + + field :packages_cleanup_policy, + Types::Packages::Cleanup::PolicyType, + null: true, + description: 'Packages cleanup policy after mutation.' + + def resolve(project_path:, **args) + project = authorized_find!(project_path) + + result = ::Packages::Cleanup::UpdatePolicyService + .new(project: project, current_user: current_user, params: args) + .execute + + { + packages_cleanup_policy: result.payload[:packages_cleanup_policy], + errors: result.errors + } + end + end + end + end + end +end diff --git a/app/graphql/mutations/packages/destroy_files.rb b/app/graphql/mutations/packages/destroy_files.rb new file mode 100644 index 00000000000..3900a2c46ae --- /dev/null +++ b/app/graphql/mutations/packages/destroy_files.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Mutations + module Packages + class DestroyFiles < ::Mutations::BaseMutation + graphql_name 'DestroyPackageFiles' + + include FindsProject + + MAXIMUM_FILES = 100 + + authorize :destroy_package + + argument :project_path, + GraphQL::Types::ID, + required: true, + description: 'Project path where the packages cleanup policy is located.' + + argument :ids, + [::Types::GlobalIDType[::Packages::PackageFile]], + required: true, + description: 'IDs of the Package file.' + + def resolve(project_path:, ids:) + project = authorized_find!(project_path) + raise_resource_not_available_error! "Cannot delete more than #{MAXIMUM_FILES} files" if ids.size > MAXIMUM_FILES + + package_files = ::Packages::PackageFile.where(id: parse_gids(ids)) # rubocop:disable CodeReuse/ActiveRecord + + ensure_file_access!(project, package_files) + + result = ::Packages::MarkPackageFilesForDestructionService.new(package_files).execute + + errors = result.error? ? Array.wrap(result[:message]) : [] + + { errors: errors } + end + + private + + def ensure_file_access!(project, package_files) + project_ids = package_files.map(&:project_id).uniq + + unless project_ids.size == 1 && project_ids.include?(project.id) + raise_resource_not_available_error! 'All files must be in the requested project' + end + end + + def parse_gids(gids) + gids.map { |gid| GitlabSchema.parse_gid(gid, expected_type: ::Packages::PackageFile).model_id } + end + end + end +end diff --git a/app/graphql/mutations/releases/create.rb b/app/graphql/mutations/releases/create.rb index 037ade2589c..70a0e71c869 100644 --- a/app/graphql/mutations/releases/create.rb +++ b/app/graphql/mutations/releases/create.rb @@ -14,6 +14,10 @@ module Mutations required: true, as: :tag, description: 'Name of the tag to associate with the release.' + argument :tag_message, GraphQL::Types::String, + required: false, + description: 'Message to use if creating a new annotated tag.' + argument :ref, GraphQL::Types::String, required: false, description: 'Commit SHA or branch name to use if creating a new tag.' diff --git a/app/graphql/mutations/security/ci_configuration/configure_sast.rb b/app/graphql/mutations/security/ci_configuration/configure_sast.rb index 7ce0bf83a4b..cc3c1d6033b 100644 --- a/app/graphql/mutations/security/ci_configuration/configure_sast.rb +++ b/app/graphql/mutations/security/ci_configuration/configure_sast.rb @@ -16,7 +16,7 @@ module Mutations description: 'SAST CI configuration for the project.' def configure_analyzer(project, **args) - ::Security::CiConfiguration::SastCreateService.new(project, current_user, args[:configuration]).execute + ::Security::CiConfiguration::SastCreateService.new(project, current_user, args[:configuration].to_h).execute end end end diff --git a/app/graphql/mutations/terraform/state/delete.rb b/app/graphql/mutations/terraform/state/delete.rb index f08219cb395..f52ace07393 100644 --- a/app/graphql/mutations/terraform/state/delete.rb +++ b/app/graphql/mutations/terraform/state/delete.rb @@ -8,9 +8,9 @@ module Mutations def resolve(id:) state = authorized_find!(id: id) - state.destroy + response = ::Terraform::States::TriggerDestroyService.new(state, current_user: current_user).execute - { errors: errors_on_object(state) } + { errors: response.errors } end end end diff --git a/app/graphql/mutations/user_preferences/update.rb b/app/graphql/mutations/user_preferences/update.rb index b71c952b0f2..c92c6d725b7 100644 --- a/app/graphql/mutations/user_preferences/update.rb +++ b/app/graphql/mutations/user_preferences/update.rb @@ -14,15 +14,6 @@ module Mutations null: true, description: 'User preferences after mutation.' - def ready?(**args) - if disabled_sort_value?(args) - raise Gitlab::Graphql::Errors::ArgumentError, - 'Feature flag `incident_escalations` must be enabled to use this sort order.' - end - - super - end - def resolve(**attributes) user_preferences = current_user.user_preference user_preferences.update(attributes) @@ -32,14 +23,6 @@ module Mutations errors: errors_on_object(user_preferences) } end - - private - - def disabled_sort_value?(args) - return false unless [:escalation_status_asc, :escalation_status_desc].include?(args[:issues_sort]) - - Feature.disabled?(:incident_escalations) - end end end end diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 2e136d409ab..2ae26ed0e1a 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -8,8 +8,7 @@ module Mutations include Mutations::SpamProtection include FindsProject - description "Creates a work item." \ - " Available only when feature flag `work_items` is enabled. The feature is experimental and is subject to change without notice." + description "Creates a work item. Available only when feature flag `work_items` is enabled." authorize :create_work_item diff --git a/app/graphql/mutations/work_items/create_from_task.rb b/app/graphql/mutations/work_items/create_from_task.rb index 4da709401a6..5ebe8b2c6d7 100644 --- a/app/graphql/mutations/work_items/create_from_task.rb +++ b/app/graphql/mutations/work_items/create_from_task.rb @@ -8,7 +8,7 @@ module Mutations include Mutations::SpamProtection description "Creates a work item from a task in another work item's description." \ - " Available only when feature flag `work_items` is enabled. This feature is experimental and is subject to change without notice." + " Available only when feature flag `work_items` is enabled." authorize :update_work_item diff --git a/app/graphql/mutations/work_items/delete.rb b/app/graphql/mutations/work_items/delete.rb index 1830ab5443c..240a8b4c11e 100644 --- a/app/graphql/mutations/work_items/delete.rb +++ b/app/graphql/mutations/work_items/delete.rb @@ -5,7 +5,7 @@ module Mutations class Delete < BaseMutation graphql_name 'WorkItemDelete' description "Deletes a work item." \ - " Available only when feature flag `work_items` is enabled. The feature is experimental and is subject to change without notice." + " Available only when feature flag `work_items` is enabled." authorize :delete_work_item diff --git a/app/graphql/mutations/work_items/delete_task.rb b/app/graphql/mutations/work_items/delete_task.rb index 87620a28fa1..b1bfed0cbf1 100644 --- a/app/graphql/mutations/work_items/delete_task.rb +++ b/app/graphql/mutations/work_items/delete_task.rb @@ -6,8 +6,7 @@ module Mutations graphql_name 'WorkItemDeleteTask' description "Deletes a task in a work item's description." \ - ' Available only when feature flag `work_items` is enabled. This feature is experimental and' \ - ' is subject to change without notice.' + ' Available only when feature flag `work_items` is enabled.' authorize :update_work_item diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb index 20319301482..c495da00f41 100644 --- a/app/graphql/mutations/work_items/update.rb +++ b/app/graphql/mutations/work_items/update.rb @@ -5,22 +5,13 @@ module Mutations class Update < BaseMutation graphql_name 'WorkItemUpdate' description "Updates a work item by Global ID." \ - " Available only when feature flag `work_items` is enabled. The feature is experimental and is subject to change without notice." + " Available only when feature flag `work_items` is enabled." include Mutations::SpamProtection + include Mutations::WorkItems::UpdateArguments authorize :update_work_item - argument :id, ::Types::GlobalIDType[::WorkItem], - required: true, - description: 'Global ID of the work item.' - argument :state_event, Types::WorkItems::StateEventEnum, - description: 'Close or reopen a work item.', - required: false - argument :title, GraphQL::Types::String, - required: false, - description: copy_field_description(Types::WorkItemType, :title) - field :work_item, Types::WorkItemType, null: true, description: 'Updated work item.' diff --git a/app/graphql/mutations/work_items/update_task.rb b/app/graphql/mutations/work_items/update_task.rb new file mode 100644 index 00000000000..35fbe672b66 --- /dev/null +++ b/app/graphql/mutations/work_items/update_task.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + class UpdateTask < BaseMutation + graphql_name 'WorkItemUpdateTask' + description "Updates a work item's task by Global ID." \ + " Available only when feature flag `work_items` is enabled." + + include Mutations::SpamProtection + + authorize :read_work_item + + argument :id, ::Types::GlobalIDType[::WorkItem], + required: true, + description: 'Global ID of the work item.' + argument :task_data, ::Types::WorkItems::UpdatedTaskInputType, + required: true, + description: 'Arguments necessary to update a task.' + + field :task, Types::WorkItemType, + null: true, + description: 'Updated task.' + field :work_item, Types::WorkItemType, + null: true, + description: 'Updated work item.' + + def resolve(id:, task_data:) + task_data_hash = task_data.to_h + work_item = authorized_find!(id: id) + task = authorized_find_task!(task_data_hash[:id]) + + unless work_item.project.work_items_feature_flag_enabled? + return { errors: ['`work_items` feature flag disabled for this project'] } + end + + spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) + + ::WorkItems::UpdateService.new( + project: task.project, + current_user: current_user, + params: task_data_hash.except(:id), + spam_params: spam_params + ).execute(task) + + check_spam_action_response!(task) + + response = { errors: errors_on_object(task) } + + if task.valid? + work_item.expire_etag_cache + + response.merge(work_item: work_item, task: task) + else + response + end + end + + private + + def authorized_find_task!(task_id) + task = task_id.find + + if current_user.can?(:update_work_item, task) + task + else + # Fail early if user cannot update task + raise_resource_not_available_error! + end + end + + def find_object(id:) + id.find + end + end + end +end diff --git a/app/graphql/mutations/work_items/update_widgets.rb b/app/graphql/mutations/work_items/update_widgets.rb new file mode 100644 index 00000000000..d19da0abaac --- /dev/null +++ b/app/graphql/mutations/work_items/update_widgets.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + class UpdateWidgets < BaseMutation + graphql_name 'WorkItemUpdateWidgets' + description "Updates the attributes of a work item's widgets by global ID." \ + " Available only when feature flag `work_items` is enabled." + + include Mutations::SpamProtection + + authorize :update_work_item + + argument :id, ::Types::GlobalIDType[::WorkItem], + required: true, + description: 'Global ID of the work item.' + + argument :description_widget, ::Types::WorkItems::Widgets::DescriptionInputType, + required: false, + description: 'Input for description widget.' + + field :work_item, Types::WorkItemType, + null: true, + description: 'Updated work item.' + + def resolve(id:, **widget_attributes) + work_item = authorized_find!(id: id) + + unless work_item.project.work_items_feature_flag_enabled? + return { errors: ['`work_items` feature flag disabled for this project'] } + end + + spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) + + ::WorkItems::UpdateService.new( + project: work_item.project, + current_user: current_user, + # Cannot use prepare to use `.to_h` on each input due to + # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87472#note_945199865 + widget_params: widget_attributes.transform_values { |values| values.to_h }, + spam_params: spam_params + ).execute(work_item) + + check_spam_action_response!(work_item) + + { + work_item: work_item.valid? ? work_item : nil, + errors: errors_on_object(work_item) + } + end + + private + + def find_object(id:) + GitlabSchema.find_by_gid(id) + end + end + end +end |