diff options
Diffstat (limited to 'app/graphql/mutations/issues')
-rw-r--r-- | app/graphql/mutations/issues/common_mutation_arguments.rb | 28 | ||||
-rw-r--r-- | app/graphql/mutations/issues/create.rb | 109 | ||||
-rw-r--r-- | app/graphql/mutations/issues/move.rb | 33 | ||||
-rw-r--r-- | app/graphql/mutations/issues/update.rb | 43 |
4 files changed, 182 insertions, 31 deletions
diff --git a/app/graphql/mutations/issues/common_mutation_arguments.rb b/app/graphql/mutations/issues/common_mutation_arguments.rb new file mode 100644 index 00000000000..4b5b246281f --- /dev/null +++ b/app/graphql/mutations/issues/common_mutation_arguments.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Mutations + module Issues + module CommonMutationArguments + extend ActiveSupport::Concern + + included do + argument :description, GraphQL::STRING_TYPE, + required: false, + description: copy_field_description(Types::IssueType, :description) + + argument :due_date, GraphQL::Types::ISO8601Date, + required: false, + description: copy_field_description(Types::IssueType, :due_date) + + argument :confidential, GraphQL::BOOLEAN_TYPE, + required: false, + description: copy_field_description(Types::IssueType, :confidential) + + argument :locked, GraphQL::BOOLEAN_TYPE, + as: :discussion_locked, + required: false, + description: copy_field_description(Types::IssueType, :discussion_locked) + end + end + end +end diff --git a/app/graphql/mutations/issues/create.rb b/app/graphql/mutations/issues/create.rb new file mode 100644 index 00000000000..1454916bc77 --- /dev/null +++ b/app/graphql/mutations/issues/create.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +module Mutations + module Issues + class Create < BaseMutation + include ResolvesProject + graphql_name 'CreateIssue' + + authorize :create_issue + + include CommonMutationArguments + + argument :project_path, GraphQL::ID_TYPE, + required: true, + description: 'Project full path the issue is associated with' + + argument :iid, GraphQL::INT_TYPE, + required: false, + description: 'The IID (internal ID) of a project issue. Only admins and project owners can modify' + + argument :title, GraphQL::STRING_TYPE, + required: true, + description: copy_field_description(Types::IssueType, :title) + + argument :milestone_id, ::Types::GlobalIDType[::Milestone], + required: false, + description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null' + + argument :labels, [GraphQL::STRING_TYPE], + required: false, + description: copy_field_description(Types::IssueType, :labels) + + argument :label_ids, [::Types::GlobalIDType[::Label]], + required: false, + description: 'The IDs of labels to be added to the issue' + + argument :created_at, Types::TimeType, + required: false, + description: 'Timestamp when the issue was created. Available only for admins and project owners' + + argument :merge_request_to_resolve_discussions_of, ::Types::GlobalIDType[::MergeRequest], + required: false, + description: 'The IID of a merge request for which to resolve discussions' + + argument :discussion_to_resolve, GraphQL::STRING_TYPE, + required: false, + description: 'The ID of a discussion to resolve. Also pass `merge_request_to_resolve_discussions_of`' + + argument :assignee_ids, [::Types::GlobalIDType[::User]], + required: false, + description: 'The array of user IDs to assign to the issue' + + field :issue, + Types::IssueType, + null: true, + description: 'The issue after mutation' + + def ready?(**args) + if args.slice(*mutually_exclusive_label_args).size > 1 + arg_str = mutually_exclusive_label_args.map { |x| x.to_s.camelize(:lower) }.join(' or ') + raise Gitlab::Graphql::Errors::ArgumentError, "one and only one of #{arg_str} is required." + end + + if args[:discussion_to_resolve].present? && args[:merge_request_to_resolve_discussions_of].blank? + raise Gitlab::Graphql::Errors::ArgumentError, + 'to resolve a discussion please also provide `merge_request_to_resolve_discussions_of` parameter' + end + + super + end + + def resolve(project_path:, **attributes) + project = authorized_find!(full_path: project_path) + params = build_create_issue_params(attributes.merge(author_id: current_user.id)) + + issue = ::Issues::CreateService.new(project, current_user, params).execute + + if issue.spam? + issue.errors.add(:base, 'Spam detected.') + end + + { + issue: issue.valid? ? issue : nil, + errors: errors_on_object(issue) + } + end + + private + + def build_create_issue_params(params) + params[:milestone_id] &&= params[:milestone_id]&.model_id + params[:assignee_ids] &&= params[:assignee_ids].map { |assignee_id| assignee_id&.model_id } + params[:label_ids] &&= params[:label_ids].map { |label_id| label_id&.model_id } + + params + end + + def mutually_exclusive_label_args + [:labels, :label_ids] + end + + def find_object(full_path:) + resolve_project(full_path: full_path) + end + end + end +end + +Mutations::Issues::Create.prepend_if_ee('::EE::Mutations::Issues::Create') diff --git a/app/graphql/mutations/issues/move.rb b/app/graphql/mutations/issues/move.rb new file mode 100644 index 00000000000..e6971c9df8c --- /dev/null +++ b/app/graphql/mutations/issues/move.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Mutations + module Issues + class Move < Base + graphql_name 'IssueMove' + + argument :target_project_path, + GraphQL::ID_TYPE, + required: true, + description: 'The project to move the issue to' + + def resolve(project_path:, iid:, target_project_path:) + Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/267762') + + issue = authorized_find!(project_path: project_path, iid: iid) + source_project = issue.project + target_project = resolve_project(full_path: target_project_path).sync + + begin + moved_issue = ::Issues::MoveService.new(source_project, current_user).execute(issue, target_project) + rescue ::Issues::MoveService::MoveError => error + errors = error.message + end + + { + issue: moved_issue, + errors: Array.wrap(errors) + } + end + end + end +end diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb index cc03d32731b..9b216b31f9b 100644 --- a/app/graphql/mutations/issues/update.rb +++ b/app/graphql/mutations/issues/update.rb @@ -5,46 +5,27 @@ module Mutations class Update < Base graphql_name 'UpdateIssue' - argument :title, - GraphQL::STRING_TYPE, - required: false, - description: copy_field_description(Types::IssueType, :title) + include CommonMutationArguments - argument :description, - GraphQL::STRING_TYPE, - required: false, - description: copy_field_description(Types::IssueType, :description) - - argument :due_date, - Types::TimeType, - required: false, - description: copy_field_description(Types::IssueType, :due_date) - - argument :confidential, - GraphQL::BOOLEAN_TYPE, + argument :title, GraphQL::STRING_TYPE, required: false, - description: copy_field_description(Types::IssueType, :confidential) + description: copy_field_description(Types::IssueType, :title) - argument :locked, - GraphQL::BOOLEAN_TYPE, - as: :discussion_locked, + argument :milestone_id, GraphQL::ID_TYPE, required: false, - description: copy_field_description(Types::IssueType, :discussion_locked) + description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null' - argument :add_label_ids, - [GraphQL::ID_TYPE], + argument :add_label_ids, [GraphQL::ID_TYPE], required: false, - description: 'The IDs of labels to be added to the issue.' + description: 'The IDs of labels to be added to the issue' - argument :remove_label_ids, - [GraphQL::ID_TYPE], + argument :remove_label_ids, [GraphQL::ID_TYPE], required: false, - description: 'The IDs of labels to be removed from the issue.' + description: 'The IDs of labels to be removed from the issue' - argument :milestone_id, - GraphQL::ID_TYPE, - required: false, - description: 'The ID of the milestone to be assigned, milestone will be removed if set to null.' + argument :state_event, Types::IssueStateEventEnum, + description: 'Close or reopen an issue', + required: false def resolve(project_path:, iid:, **args) issue = authorized_find!(project_path: project_path, iid: iid) |