2023-04-23 21:23:45 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Mutations
|
|
|
|
module Issues
|
|
|
|
class BulkUpdate < BaseMutation
|
|
|
|
graphql_name 'IssuesBulkUpdate'
|
|
|
|
|
|
|
|
include Gitlab::Graphql::Authorize::AuthorizeResource
|
|
|
|
|
|
|
|
MAX_ISSUES = 100
|
|
|
|
|
|
|
|
description 'Allows updating several properties for a set of issues. ' \
|
|
|
|
'Does nothing if the `bulk_update_issues_mutation` feature flag is disabled.'
|
|
|
|
|
|
|
|
argument :parent_id, ::Types::GlobalIDType[::IssueParent],
|
|
|
|
required: true,
|
2023-05-27 22:25:52 +05:30
|
|
|
description: 'Global ID of the parent to which the bulk update will be scoped. ' \
|
|
|
|
'The parent can be a project **(FREE)** or a group **(PREMIUM)**. ' \
|
2023-04-23 21:23:45 +05:30
|
|
|
'Example `IssueParentID` are `"gid://gitlab/Project/1"` and `"gid://gitlab/Group/1"`.'
|
|
|
|
|
|
|
|
argument :ids, [::Types::GlobalIDType[::Issue]],
|
|
|
|
required: true,
|
|
|
|
description: 'Global ID array of the issues that will be updated. ' \
|
|
|
|
"IDs that the user can\'t update will be ignored. A max of #{MAX_ISSUES} can be provided."
|
|
|
|
|
|
|
|
argument :assignee_ids, [::Types::GlobalIDType[::User]],
|
|
|
|
required: false,
|
|
|
|
description: 'Global ID array of the users that will be assigned to the given issues. ' \
|
|
|
|
'Existing assignees will be replaced with the ones on this list.'
|
|
|
|
|
|
|
|
argument :milestone_id, ::Types::GlobalIDType[::Milestone],
|
|
|
|
required: false,
|
|
|
|
description: 'Global ID of the milestone that will be assigned to the issues.'
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
argument :state_event, Types::IssueStateEventEnum,
|
|
|
|
description: 'Close or reopen an issue.',
|
|
|
|
required: false
|
|
|
|
|
|
|
|
argument :add_label_ids, [::Types::GlobalIDType[::Label]],
|
|
|
|
description: 'Global ID array of the labels that will be added to the issues. ',
|
|
|
|
required: false
|
|
|
|
|
|
|
|
argument :remove_label_ids, [::Types::GlobalIDType[::Label]],
|
|
|
|
description: 'Global ID array of the labels that will be removed from the issues. ',
|
|
|
|
required: false
|
|
|
|
|
|
|
|
argument :subscription_event, Types::IssuableSubscriptionEventEnum,
|
|
|
|
description: 'Subscribe to or unsubscribe from issue notifications.',
|
|
|
|
required: false
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
field :updated_issue_count, GraphQL::Types::Int,
|
|
|
|
null: true,
|
|
|
|
description: 'Number of issues that were successfully updated.'
|
|
|
|
|
|
|
|
def ready?(**args)
|
|
|
|
if Feature.disabled?(:bulk_update_issues_mutation)
|
|
|
|
raise Gitlab::Graphql::Errors::ResourceNotAvailable, '`bulk_update_issues_mutation` feature flag is disabled.'
|
|
|
|
end
|
|
|
|
|
|
|
|
if args[:ids].size > MAX_ISSUES
|
|
|
|
raise Gitlab::Graphql::Errors::ArgumentError,
|
|
|
|
format(_('No more than %{max_issues} issues can be updated at the same time'), max_issues: MAX_ISSUES)
|
|
|
|
end
|
|
|
|
|
|
|
|
super
|
|
|
|
end
|
|
|
|
|
|
|
|
def resolve(ids:, parent_id:, **attributes)
|
|
|
|
parent = find_parent!(parent_id)
|
|
|
|
|
|
|
|
result = Issuable::BulkUpdateService.new(
|
|
|
|
parent,
|
|
|
|
current_user,
|
|
|
|
prepared_params(attributes, ids)
|
|
|
|
).execute('issue')
|
|
|
|
|
|
|
|
if result.success?
|
|
|
|
{ updated_issue_count: result.payload[:count], errors: [] }
|
|
|
|
else
|
|
|
|
{ errors: result.errors }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def find_parent!(parent_id)
|
|
|
|
parent = GitlabSchema.find_by_gid(parent_id).sync
|
|
|
|
raise_resource_not_available_error! unless current_user.can?("read_#{parent.to_ability_name}", parent)
|
|
|
|
|
|
|
|
parent
|
|
|
|
end
|
|
|
|
|
|
|
|
def prepared_params(attributes, ids)
|
2023-05-27 22:25:52 +05:30
|
|
|
prepared = attributes.except(*global_id_arguments).merge(issuable_ids: model_ids_from(ids).uniq)
|
2023-04-23 21:23:45 +05:30
|
|
|
|
|
|
|
global_id_arguments.each do |argument|
|
|
|
|
next unless attributes.key?(argument)
|
|
|
|
|
|
|
|
prepared[argument] = model_ids_from(attributes[argument])
|
|
|
|
end
|
|
|
|
|
|
|
|
# Ruby 3+: This can be simplified to:
|
|
|
|
# prepared.transform_keys(param_mappings)
|
|
|
|
prepared.transform_keys { |key| param_mappings[key] || key }
|
|
|
|
end
|
|
|
|
|
|
|
|
def param_mappings
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
|
|
|
|
def global_id_arguments
|
2023-05-27 22:25:52 +05:30
|
|
|
%i[assignee_ids milestone_id add_label_ids remove_label_ids]
|
2023-04-23 21:23:45 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def model_ids_from(attributes)
|
|
|
|
return if attributes.nil?
|
|
|
|
return attributes.map(&:model_id) if attributes.is_a?(Array)
|
|
|
|
|
|
|
|
attributes.model_id
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
Mutations::Issues::BulkUpdate.prepend_mod
|