diff options
Diffstat (limited to 'app/graphql/mutations')
31 files changed, 379 insertions, 107 deletions
diff --git a/app/graphql/mutations/alert_management/alerts/set_assignees.rb b/app/graphql/mutations/alert_management/alerts/set_assignees.rb index 500e2b868b1..a8513417c1c 100644 --- a/app/graphql/mutations/alert_management/alerts/set_assignees.rb +++ b/app/graphql/mutations/alert_management/alerts/set_assignees.rb @@ -7,14 +7,14 @@ module Mutations graphql_name 'AlertSetAssignees' argument :assignee_usernames, - [GraphQL::Types::String], - required: true, - description: 'Usernames to assign to the alert. Replaces existing assignees by default.' + [GraphQL::Types::String], + required: true, + description: 'Usernames to assign to the alert. Replaces existing assignees by default.' argument :operation_mode, - Types::MutationOperationModeEnum, - required: false, - description: 'Operation to perform. Defaults to REPLACE.' + Types::MutationOperationModeEnum, + required: false, + description: 'Operation to perform. Defaults to REPLACE.' def resolve(args) alert = authorized_find!(project_path: args[:project_path], iid: args[:iid]) diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb index 771ace5510f..615c0f43a15 100644 --- a/app/graphql/mutations/alert_management/base.rb +++ b/app/graphql/mutations/alert_management/base.rb @@ -6,27 +6,27 @@ module Mutations include Gitlab::Utils::UsageData argument :project_path, GraphQL::Types::ID, - required: true, - description: "Project the alert to mutate is in." + required: true, + description: "Project the alert to mutate is in." argument :iid, GraphQL::Types::String, - required: true, - description: "IID of the alert to mutate." + required: true, + description: "IID of the alert to mutate." field :alert, - Types::AlertManagement::AlertType, - null: true, - description: "Alert after mutation." + Types::AlertManagement::AlertType, + null: true, + description: "Alert after mutation." field :todo, - Types::TodoType, - null: true, - description: "To-do item after mutation." + Types::TodoType, + null: true, + description: "To-do item after mutation." field :issue, - Types::IssueType, - null: true, - description: "Issue created after mutation." + Types::IssueType, + null: true, + description: "Issue created after mutation." authorize :update_alert_management_alert diff --git a/app/graphql/mutations/alert_management/http_integration/create.rb b/app/graphql/mutations/alert_management/http_integration/create.rb index f8d1a383706..fccef8cd3ad 100644 --- a/app/graphql/mutations/alert_management/http_integration/create.rb +++ b/app/graphql/mutations/alert_management/http_integration/create.rb @@ -9,16 +9,16 @@ module Mutations include FindsProject argument :project_path, GraphQL::Types::ID, - required: true, - description: 'Project to create the integration in.' + required: true, + description: 'Project to create the integration in.' argument :name, GraphQL::Types::String, - required: true, - description: 'Name of the integration.' + required: true, + description: 'Name of the integration.' argument :active, GraphQL::Types::Boolean, - required: true, - description: 'Whether the integration is receiving alerts.' + required: true, + description: 'Whether the integration is receiving alerts.' def resolve(args) project = authorized_find!(args[:project_path]) diff --git a/app/graphql/mutations/alert_management/http_integration/destroy.rb b/app/graphql/mutations/alert_management/http_integration/destroy.rb index dc5c73ecff6..9da50a4c4ce 100644 --- a/app/graphql/mutations/alert_management/http_integration/destroy.rb +++ b/app/graphql/mutations/alert_management/http_integration/destroy.rb @@ -7,8 +7,8 @@ module Mutations graphql_name 'HttpIntegrationDestroy' argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration], - required: true, - description: "ID of the integration to remove." + required: true, + description: "ID of the integration to remove." def resolve(id:) integration = authorized_find!(id: id) diff --git a/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb b/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb index 2f25d315d2e..9434ac1637e 100644 --- a/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb +++ b/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb @@ -5,9 +5,9 @@ module Mutations module HttpIntegration class HttpIntegrationBase < BaseMutation field :integration, - Types::AlertManagement::HttpIntegrationType, - null: true, - description: "HTTP integration." + Types::AlertManagement::HttpIntegrationType, + null: true, + description: "HTTP integration." authorize :admin_operations diff --git a/app/graphql/mutations/alert_management/http_integration/reset_token.rb b/app/graphql/mutations/alert_management/http_integration/reset_token.rb index 83ad7762408..bed3cf08674 100644 --- a/app/graphql/mutations/alert_management/http_integration/reset_token.rb +++ b/app/graphql/mutations/alert_management/http_integration/reset_token.rb @@ -7,8 +7,8 @@ module Mutations graphql_name 'HttpIntegrationResetToken' argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration], - required: true, - description: "ID of the integration to mutate." + required: true, + description: "ID of the integration to mutate." def resolve(id:) integration = authorized_find!(id: id) diff --git a/app/graphql/mutations/alert_management/http_integration/update.rb b/app/graphql/mutations/alert_management/http_integration/update.rb index 78424e317b8..06d0b7163b0 100644 --- a/app/graphql/mutations/alert_management/http_integration/update.rb +++ b/app/graphql/mutations/alert_management/http_integration/update.rb @@ -7,16 +7,16 @@ module Mutations graphql_name 'HttpIntegrationUpdate' argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration], - required: true, - description: "ID of the integration to mutate." + required: true, + description: "ID of the integration to mutate." argument :name, GraphQL::Types::String, - required: false, - description: "Name of the integration." + required: false, + description: "Name of the integration." argument :active, GraphQL::Types::Boolean, - required: false, - description: "Whether the integration is receiving alerts." + required: false, + description: "Whether the integration is receiving alerts." def resolve(args) integration = authorized_find!(id: args[:id]) diff --git a/app/graphql/mutations/alert_management/prometheus_integration/create.rb b/app/graphql/mutations/alert_management/prometheus_integration/create.rb index b06a4f58df5..665ce96f0f9 100644 --- a/app/graphql/mutations/alert_management/prometheus_integration/create.rb +++ b/app/graphql/mutations/alert_management/prometheus_integration/create.rb @@ -9,16 +9,16 @@ module Mutations include FindsProject argument :project_path, GraphQL::Types::ID, - required: true, - description: 'Project to create the integration in.' + required: true, + description: 'Project to create the integration in.' argument :active, GraphQL::Types::Boolean, - required: true, - description: 'Whether the integration is receiving alerts.' + required: true, + description: 'Whether the integration is receiving alerts.' argument :api_url, GraphQL::Types::String, - required: false, - description: 'Endpoint at which Prometheus can be queried.' + required: false, + description: 'Endpoint at which Prometheus can be queried.' def resolve(args) project = authorized_find!(args[:project_path]) diff --git a/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb b/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb index 29834d63f35..28729ec70cd 100644 --- a/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb +++ b/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb @@ -5,9 +5,9 @@ module Mutations module PrometheusIntegration class PrometheusIntegrationBase < BaseMutation field :integration, - Types::AlertManagement::PrometheusIntegrationType, - null: true, - description: "Newly created integration." + Types::AlertManagement::PrometheusIntegrationType, + null: true, + description: "Newly created integration." authorize :admin_project diff --git a/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb b/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb index 71c02efdc03..15e6763b1ee 100644 --- a/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb +++ b/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb @@ -7,8 +7,8 @@ module Mutations graphql_name 'PrometheusIntegrationResetToken' argument :id, Types::GlobalIDType[::Integrations::Prometheus], - required: true, - description: "ID of the integration to mutate." + required: true, + description: "ID of the integration to mutate." def resolve(id:) integration = authorized_find!(id: id) diff --git a/app/graphql/mutations/alert_management/prometheus_integration/update.rb b/app/graphql/mutations/alert_management/prometheus_integration/update.rb index 50aafdc26a6..593624aaafd 100644 --- a/app/graphql/mutations/alert_management/prometheus_integration/update.rb +++ b/app/graphql/mutations/alert_management/prometheus_integration/update.rb @@ -7,16 +7,16 @@ module Mutations graphql_name 'PrometheusIntegrationUpdate' argument :id, Types::GlobalIDType[::Integrations::Prometheus], - required: true, - description: "ID of the integration to mutate." + required: true, + description: "ID of the integration to mutate." argument :active, GraphQL::Types::Boolean, - required: false, - description: "Whether the integration is receiving alerts." + required: false, + description: "Whether the integration is receiving alerts." argument :api_url, GraphQL::Types::String, - required: false, - description: "Endpoint at which Prometheus can be queried." + required: false, + description: "Endpoint at which Prometheus can be queried." def resolve(args) integration = authorized_find!(id: args[:id]) diff --git a/app/graphql/mutations/alert_management/update_alert_status.rb b/app/graphql/mutations/alert_management/update_alert_status.rb index be271a7d795..a0d06ebf221 100644 --- a/app/graphql/mutations/alert_management/update_alert_status.rb +++ b/app/graphql/mutations/alert_management/update_alert_status.rb @@ -6,8 +6,8 @@ module Mutations graphql_name 'UpdateAlertStatus' argument :status, Types::AlertManagement::StatusEnum, - required: true, - description: 'Status to set the alert.' + required: true, + description: 'Status to set the alert.' def resolve(project_path:, iid:, status:) alert = authorized_find!(project_path: project_path, iid: iid) diff --git a/app/graphql/mutations/award_emojis/base.rb b/app/graphql/mutations/award_emojis/base.rb index 65065de0de4..0223c978cf9 100644 --- a/app/graphql/mutations/award_emojis/base.rb +++ b/app/graphql/mutations/award_emojis/base.rb @@ -3,7 +3,7 @@ module Mutations module AwardEmojis class Base < BaseMutation - NOT_EMOJI_AWARDABLE = 'You cannot award emoji to this resource.' + NOT_EMOJI_AWARDABLE = 'You cannot add emoji reactions to this resource.' authorize :award_emoji @@ -20,7 +20,7 @@ module Mutations field :award_emoji, Types::AwardEmojis::AwardEmojiType, null: true, - description: 'Award emoji after mutation.' + description: 'Emoji reactions after mutation.' private diff --git a/app/graphql/mutations/ci/pipeline_schedule/create.rb b/app/graphql/mutations/ci/pipeline_schedule/create.rb index 71a366ed342..d21ac6fd727 100644 --- a/app/graphql/mutations/ci/pipeline_schedule/create.rb +++ b/app/graphql/mutations/ci/pipeline_schedule/create.rb @@ -51,28 +51,16 @@ module Mutations params = pipeline_schedule_attrs.merge(variables_attributes: variables.map(&:to_h)) - if ::Feature.enabled?(:ci_refactoring_pipeline_schedule_create_service, project) - response = ::Ci::PipelineSchedules::CreateService - .new(project, current_user, params) - .execute - - schedule = response.payload - - unless response.success? - return { - pipeline_schedule: nil, errors: response.errors - } - end - else - schedule = ::Ci::CreatePipelineScheduleService - .new(project, current_user, params) - .execute - - unless schedule.persisted? - return { - pipeline_schedule: nil, errors: schedule.errors.full_messages - } - end + response = ::Ci::PipelineSchedules::CreateService + .new(project, current_user, params) + .execute + + schedule = response.payload + + unless response.success? + return { + pipeline_schedule: nil, errors: response.errors + } end { diff --git a/app/graphql/mutations/ci/pipeline_trigger/base.rb b/app/graphql/mutations/ci/pipeline_trigger/base.rb new file mode 100644 index 00000000000..23be70e7754 --- /dev/null +++ b/app/graphql/mutations/ci/pipeline_trigger/base.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module PipelineTrigger + class Base < BaseMutation + authorize :admin_build + authorize :admin_trigger + + PipelineTriggerID = ::Types::GlobalIDType[::Ci::Trigger] + + argument :id, PipelineTriggerID, + required: true, + description: 'ID of the pipeline trigger token to mutate.' + end + end + end +end diff --git a/app/graphql/mutations/ci/pipeline_trigger/create.rb b/app/graphql/mutations/ci/pipeline_trigger/create.rb new file mode 100644 index 00000000000..042f9b26dd0 --- /dev/null +++ b/app/graphql/mutations/ci/pipeline_trigger/create.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module PipelineTrigger + class Create < BaseMutation + graphql_name 'PipelineTriggerCreate' + + include FindsProject + + authorize :admin_build + + argument :project_path, GraphQL::Types::ID, + required: true, + description: 'Full path of the project that the pipeline trigger token to mutate is in.' + + argument :description, GraphQL::Types::String, + required: true, + description: 'Description of the pipeline trigger token.' + + field :pipeline_trigger, Types::Ci::PipelineTriggerType, + null: true, + description: 'Mutated pipeline trigger token.' + + def resolve(project_path:, description:) + project = authorized_find!(project_path) + + trigger = project.triggers.create(owner: current_user, description: description) + + { + pipeline_trigger: trigger, + errors: trigger.errors.full_messages + } + end + end + end + end +end diff --git a/app/graphql/mutations/ci/pipeline_trigger/delete.rb b/app/graphql/mutations/ci/pipeline_trigger/delete.rb new file mode 100644 index 00000000000..bc18f58ec43 --- /dev/null +++ b/app/graphql/mutations/ci/pipeline_trigger/delete.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module PipelineTrigger + class Delete < Base + graphql_name 'PipelineTriggerDelete' + + def resolve(id:) + trigger = authorized_find!(id: id) + + errors = trigger.destroy ? [] : ['Could not remove the trigger'] + + { errors: errors } + end + end + end + end +end diff --git a/app/graphql/mutations/ci/pipeline_trigger/update.rb b/app/graphql/mutations/ci/pipeline_trigger/update.rb new file mode 100644 index 00000000000..fa68593eb09 --- /dev/null +++ b/app/graphql/mutations/ci/pipeline_trigger/update.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module PipelineTrigger + class Update < Base + graphql_name 'PipelineTriggerUpdate' + + argument :description, GraphQL::Types::String, + required: true, + description: 'Description of the pipeline trigger token.' + + field :pipeline_trigger, Types::Ci::PipelineTriggerType, + null: true, + description: 'Mutated pipeline trigger token.' + + def resolve(id:, description:) + trigger = authorized_find!(id: id) + + trigger.description = description + + trigger.save + + { + pipeline_trigger: trigger, + errors: trigger.errors.full_messages + } + end + end + end + end +end diff --git a/app/graphql/mutations/concerns/mutations/validate_time_estimate.rb b/app/graphql/mutations/concerns/mutations/validate_time_estimate.rb new file mode 100644 index 00000000000..82a56fd04f3 --- /dev/null +++ b/app/graphql/mutations/concerns/mutations/validate_time_estimate.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Mutations + module ValidateTimeEstimate + private + + def validate_time_estimate(time_estimate) + return unless time_estimate + + parsed_time_estimate = Gitlab::TimeTrackingFormatter.parse(time_estimate, keep_zero: true) + + if parsed_time_estimate.nil? + raise Gitlab::Graphql::Errors::ArgumentError, + 'timeEstimate must be formatted correctly, for example `1h 30m`' + elsif parsed_time_estimate < 0 + raise Gitlab::Graphql::Errors::ArgumentError, + 'timeEstimate must be greater than or equal to zero. ' \ + 'Remember that every new timeEstimate overwrites the previous value.' + end + 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 f009abdba70..7aa78509bea 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -47,7 +47,7 @@ module Mutations argument :award_emoji_widget, ::Types::WorkItems::Widgets::AwardEmojiUpdateInputType, required: false, - description: 'Input for award emoji widget.' + description: 'Input for emoji reactions widget.' end end end diff --git a/app/graphql/mutations/environments/create.rb b/app/graphql/mutations/environments/create.rb index f18ce0eba97..76a5bf2f551 100644 --- a/app/graphql/mutations/environments/create.rb +++ b/app/graphql/mutations/environments/create.rb @@ -40,6 +40,11 @@ module Mutations required: false, description: 'Kubernetes namespace of the environment.' + argument :flux_resource_path, + GraphQL::Types::String, + required: false, + description: 'Flux resource path of the environment.' + field :environment, Types::EnvironmentType, null: true, diff --git a/app/graphql/mutations/environments/update.rb b/app/graphql/mutations/environments/update.rb index 07ab22685cc..44b9398c233 100644 --- a/app/graphql/mutations/environments/update.rb +++ b/app/graphql/mutations/environments/update.rb @@ -33,6 +33,11 @@ module Mutations required: false, description: 'Kubernetes namespace of the environment.' + argument :flux_resource_path, + GraphQL::Types::String, + required: false, + description: 'Flux resource path of the environment.' + field :environment, Types::EnvironmentType, null: true, diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb index 2a863893cf1..35deb9e0af8 100644 --- a/app/graphql/mutations/issues/update.rb +++ b/app/graphql/mutations/issues/update.rb @@ -6,6 +6,7 @@ module Mutations graphql_name 'UpdateIssue' include CommonMutationArguments + include ValidateTimeEstimate argument :title, GraphQL::Types::String, required: false, @@ -54,9 +55,7 @@ module Mutations raise Gitlab::Graphql::Errors::ArgumentError, 'labelIds is mutually exclusive with any of addLabelIds or removeLabelIds' end - if !time_estimate.nil? && Gitlab::TimeTrackingFormatter.parse(time_estimate, keep_zero: true).nil? - raise Gitlab::Graphql::Errors::ArgumentError, 'timeEstimate must be formatted correctly, for example `1h 30m`' - end + validate_time_estimate(time_estimate) super end diff --git a/app/graphql/mutations/merge_requests/update.rb b/app/graphql/mutations/merge_requests/update.rb index da4db7342a3..470292df86c 100644 --- a/app/graphql/mutations/merge_requests/update.rb +++ b/app/graphql/mutations/merge_requests/update.rb @@ -5,6 +5,8 @@ module Mutations class Update < Base graphql_name 'MergeRequestUpdate' + include ValidateTimeEstimate + description 'Update attributes of a merge request' argument :title, GraphQL::Types::String, @@ -45,10 +47,7 @@ module Mutations end def ready?(time_estimate: nil, **args) - if !time_estimate.nil? && Gitlab::TimeTrackingFormatter.parse(time_estimate, keep_zero: true).nil? - raise Gitlab::Graphql::Errors::ArgumentError, - 'timeEstimate must be formatted correctly, for example `1h 30m`' - end + validate_time_estimate(time_estimate) super end diff --git a/app/graphql/mutations/metrics/dashboard/annotations/create.rb b/app/graphql/mutations/metrics/dashboard/annotations/create.rb index 296efa19bb7..59ddffe3aad 100644 --- a/app/graphql/mutations/metrics/dashboard/annotations/create.rb +++ b/app/graphql/mutations/metrics/dashboard/annotations/create.rb @@ -61,14 +61,10 @@ module Mutations end end - def resolve(args) - annotation_response = ::Metrics::Dashboard::Annotations::CreateService.new(context[:current_user], annotation_create_params(args)).execute - - annotation = annotation_response[:annotation] - + def resolve(_args) { - annotation: annotation.valid? ? annotation : nil, - errors: errors_on_object(annotation) + annotation: nil, + errors: [] } end diff --git a/app/graphql/mutations/metrics/dashboard/annotations/delete.rb b/app/graphql/mutations/metrics/dashboard/annotations/delete.rb index 32047cda213..61fcf8e0b13 100644 --- a/app/graphql/mutations/metrics/dashboard/annotations/delete.rb +++ b/app/graphql/mutations/metrics/dashboard/annotations/delete.rb @@ -13,19 +13,11 @@ module Mutations required: true, description: 'Global ID of the annotation to delete.' + # rubocop:disable Lint/UnusedMethodArgument def resolve(id:) - raise_resource_not_available_error! if Feature.enabled?(:remove_monitor_metrics) - - annotation = authorized_find!(id: id) - - result = ::Metrics::Dashboard::Annotations::DeleteService.new(context[:current_user], annotation).execute - - errors = Array.wrap(result[:message]) - - { - errors: errors - } + raise_resource_not_available_error! end + # rubocop:enable Lint/UnusedMethodArgument end end end diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb index 96bee693a1e..4e71bed52c6 100644 --- a/app/graphql/mutations/namespace/package_settings/update.rb +++ b/app/graphql/mutations/namespace/package_settings/update.rb @@ -8,6 +8,8 @@ module Mutations include Mutations::ResolvesNamespace + NUGET_DUPLICATES_FF_ERROR = '`nuget_duplicates_option` feature flag is disabled.' + description <<~DESC These settings can be adjusted by the group Owner or Maintainer. [Issue 370471](https://gitlab.com/gitlab-org/gitlab/-/issues/370471) proposes limiting @@ -41,6 +43,16 @@ module Mutations required: false, description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicate_exception_regex) + argument :nuget_duplicates_allowed, + GraphQL::Types::Boolean, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :nuget_duplicates_allowed) + + argument :nuget_duplicate_exception_regex, + Types::UntrustedRegexp, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :nuget_duplicate_exception_regex) + argument :maven_package_requests_forwarding, GraphQL::Types::Boolean, required: false, @@ -79,6 +91,10 @@ module Mutations def resolve(namespace_path:, **args) namespace = authorized_find!(namespace_path: namespace_path) + if nuget_duplicate_settings_present?(args) && Feature.disabled?(:nuget_duplicates_option, namespace) + raise_resource_not_available_error! NUGET_DUPLICATES_FF_ERROR + end + result = ::Namespaces::PackageSettings::UpdateService .new(container: namespace, current_user: current_user, params: args) .execute @@ -94,6 +110,10 @@ module Mutations def find_object(namespace_path:) resolve_namespace(full_path: namespace_path) end + + def nuget_duplicate_settings_present?(args) + args.key?(:nuget_duplicates_allowed) || args.key?(:nuget_duplicate_exception_regex) + end end end end diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 9f7b7b5db97..7ce508e5ef1 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -14,6 +14,7 @@ module Mutations authorize :create_work_item MUTUALLY_EXCLUSIVE_ARGUMENTS_ERROR = 'Please provide either projectPath or namespacePath argument, but not both.' + DISABLED_FF_ERROR = 'namespace_level_work_items feature flag is disabled. Only project paths allowed.' argument :confidential, GraphQL::Types::Boolean, required: false, @@ -59,6 +60,7 @@ module Mutations def resolve(project_path: nil, namespace_path: nil, **attributes) container_path = project_path || namespace_path container = authorized_find!(container_path) + check_feature_available!(container) params = global_id_compatibility_params(attributes).merge(author_id: current_user.id) type = ::WorkItems::Type.find(attributes[:work_item_type_id]) @@ -81,6 +83,12 @@ module Mutations private + def check_feature_available!(container) + return unless container.is_a?(::Group) && Feature.disabled?(:namespace_level_work_items, container) + + raise Gitlab::Graphql::Errors::ArgumentError, DISABLED_FF_ERROR + end + def global_id_compatibility_params(params) params[:work_item_type_id] = params[:work_item_type_id]&.model_id diff --git a/app/graphql/mutations/work_items/linked_items/add.rb b/app/graphql/mutations/work_items/linked_items/add.rb new file mode 100644 index 00000000000..b346b074e85 --- /dev/null +++ b/app/graphql/mutations/work_items/linked_items/add.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + module LinkedItems + class Add < Base + graphql_name 'WorkItemAddLinkedItems' + description 'Add linked items to the work item.' + + argument :link_type, ::Types::WorkItems::RelatedLinkTypeEnum, + required: false, description: 'Type of link. Defaults to `RELATED`.' + + private + + def update_links(work_item, params) + Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/419555') + + gids = params.delete(:work_items_ids) + work_items = begin + GitlabSchema.parse_gids(gids, expected_type: ::WorkItem).map(&:find) + rescue ActiveRecord::RecordNotFound => e + raise Gitlab::Graphql::Errors::ArgumentError, e + end + + ::WorkItems::RelatedWorkItemLinks::CreateService + .new(work_item, current_user, { target_issuable: work_items, link_type: params[:link_type] }) + .execute + end + end + end + end +end diff --git a/app/graphql/mutations/work_items/linked_items/base.rb b/app/graphql/mutations/work_items/linked_items/base.rb new file mode 100644 index 00000000000..1d8d74b02ac --- /dev/null +++ b/app/graphql/mutations/work_items/linked_items/base.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + module LinkedItems + class Base < BaseMutation + # Limit maximum number of items that can be linked at a time to avoid overloading the DB + # See https://gitlab.com/gitlab-org/gitlab/-/issues/419555 + MAX_WORK_ITEMS = 3 + + argument :id, ::Types::GlobalIDType[::WorkItem], + required: true, description: 'Global ID of the work item.' + argument :work_items_ids, [::Types::GlobalIDType[::WorkItem]], + required: true, + description: "Global IDs of the items to link. Maximum number of IDs you can provide: #{MAX_WORK_ITEMS}." + + field :work_item, Types::WorkItemType, + null: true, description: 'Updated work item.' + + field :message, GraphQL::Types::String, + null: true, description: 'Linked items update result message.' + + authorize :read_work_item + + def ready?(**args) + if args[:work_items_ids].size > MAX_WORK_ITEMS + raise Gitlab::Graphql::Errors::ArgumentError, + format( + _('No more than %{max_work_items} work items can be linked at the same time.'), + max_work_items: MAX_WORK_ITEMS + ) + end + + super + end + + def resolve(**args) + work_item = authorized_find!(id: args.delete(:id)) + raise_resource_not_available_error! unless work_item.project.linked_work_items_feature_flag_enabled? + + service_response = update_links(work_item, args) + + { + work_item: work_item, + errors: service_response[:status] == :error ? Array.wrap(service_response[:message]) : [], + message: service_response[:status] == :success ? service_response[:message] : '' + } + end + + private + + def update_links(work_item, params) + raise NotImplementedError + end + end + end + end +end diff --git a/app/graphql/mutations/work_items/subscribe.rb b/app/graphql/mutations/work_items/subscribe.rb new file mode 100644 index 00000000000..a29c3416c3d --- /dev/null +++ b/app/graphql/mutations/work_items/subscribe.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + class Subscribe < BaseMutation + graphql_name 'WorkItemSubscribe' + + argument :id, ::Types::GlobalIDType[::WorkItem], + required: true, + description: 'Global ID of the work item.' + + argument :subscribed, + GraphQL::Types::Boolean, + required: true, + description: 'Desired state of the subscription.' + + field :work_item, Types::WorkItemType, + null: true, + description: 'Work item after mutation.' + + authorize :update_subscription + + def resolve(args) + work_item = authorized_find!(id: args[:id]) + + update_subscription(work_item, args[:subscribed]) + + { + work_item: work_item, + errors: [] + } + end + + private + + def update_subscription(work_item, subscribed_state) + work_item.set_subscription(current_user, subscribed_state, work_item.project) + end + end + end +end |