summaryrefslogtreecommitdiff
path: root/app/graphql/resolvers/issues/base_resolver.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/graphql/resolvers/issues/base_resolver.rb')
-rw-r--r--app/graphql/resolvers/issues/base_resolver.rb186
1 files changed, 186 insertions, 0 deletions
diff --git a/app/graphql/resolvers/issues/base_resolver.rb b/app/graphql/resolvers/issues/base_resolver.rb
new file mode 100644
index 00000000000..9a2c4572abb
--- /dev/null
+++ b/app/graphql/resolvers/issues/base_resolver.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Issues
+ # rubocop:disable Graphql/ResolverType
+ class BaseResolver < Resolvers::BaseResolver
+ include SearchArguments
+
+ argument :assignee_id, GraphQL::Types::String,
+ required: false,
+ description: 'ID of a user assigned to the issues. Wildcard values "NONE" and "ANY" are supported.'
+ argument :assignee_username, GraphQL::Types::String,
+ required: false,
+ description: 'Username of a user assigned to the issue.',
+ deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' }
+ argument :assignee_usernames, [GraphQL::Types::String],
+ required: false,
+ description: 'Usernames of users assigned to the issue.'
+ argument :author_username, GraphQL::Types::String,
+ required: false,
+ description: 'Username of the author of the issue.'
+ argument :closed_after, Types::TimeType,
+ required: false,
+ description: 'Issues closed after this date.'
+ argument :closed_before, Types::TimeType,
+ required: false,
+ description: 'Issues closed before this date.'
+ argument :confidential,
+ GraphQL::Types::Boolean,
+ required: false,
+ description: 'Filter for confidential issues. If "false", excludes confidential issues.' \
+ ' If "true", returns only confidential issues.'
+ argument :created_after, Types::TimeType,
+ required: false,
+ description: 'Issues created after this date.'
+ argument :created_before, Types::TimeType,
+ required: false,
+ description: 'Issues created before this date.'
+ argument :crm_contact_id, GraphQL::Types::String,
+ required: false,
+ description: 'ID of a contact assigned to the issues.'
+ argument :crm_organization_id, GraphQL::Types::String,
+ required: false,
+ description: 'ID of an organization assigned to the issues.'
+ argument :iid, GraphQL::Types::String,
+ required: false,
+ description: 'IID of the issue. For example, "1".'
+ argument :iids, [GraphQL::Types::String],
+ required: false,
+ description: 'List of IIDs of issues. For example, `["1", "2"]`.'
+ argument :label_name, [GraphQL::Types::String, { null: true }],
+ required: false,
+ description: 'Labels applied to this issue.'
+ argument :milestone_title, [GraphQL::Types::String, { null: true }],
+ required: false,
+ description: 'Milestone applied to this issue.'
+ argument :milestone_wildcard_id, ::Types::MilestoneWildcardIdEnum,
+ required: false,
+ description: 'Filter issues by milestone ID wildcard.'
+ argument :my_reaction_emoji, GraphQL::Types::String,
+ required: false,
+ description: 'Filter by reaction emoji applied by the current user.' \
+ ' Wildcard values "NONE" and "ANY" are supported.'
+ argument :not, Types::Issues::NegatedIssueFilterInputType,
+ description: 'Negated arguments.',
+ required: false
+ argument :or, Types::Issues::UnionedIssueFilterInputType,
+ description: 'List of arguments with inclusive OR.',
+ required: false
+ argument :types, [Types::IssueTypeEnum],
+ as: :issue_types,
+ description: 'Filter issues by the given issue types.',
+ required: false
+ argument :updated_after, Types::TimeType,
+ required: false,
+ description: 'Issues updated after this date.'
+ argument :updated_before, Types::TimeType,
+ required: false,
+ description: 'Issues updated before this date.'
+
+ class << self
+ def resolver_complexity(args, child_complexity:)
+ complexity = super
+ complexity += 2 if args[:labelName]
+
+ complexity
+ end
+
+ def accept_release_tag
+ argument :release_tag, [GraphQL::Types::String],
+ required: false,
+ description: "Release tag associated with the issue's milestone."
+ argument :release_tag_wildcard_id, Types::ReleaseTagWildcardIdEnum,
+ required: false,
+ description: 'Filter issues by release tag ID wildcard.'
+ end
+ end
+
+ def ready?(**args)
+ if args[:or].present? && or_issuable_queries_disabled?
+ raise ::Gitlab::Graphql::Errors::ArgumentError,
+ "'or' arguments are only allowed when the `or_issuable_queries` feature flag is enabled."
+ end
+
+ args[:not] = args[:not].to_h if args[:not]
+ args[:or] = args[:or].to_h if args[:or]
+
+ params_not_mutually_exclusive(args, mutually_exclusive_assignee_username_args)
+ params_not_mutually_exclusive(args, mutually_exclusive_milestone_args)
+ params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args)
+ params_not_mutually_exclusive(args, mutually_exclusive_release_tag_args)
+
+ super
+ end
+
+ private
+
+ def or_issuable_queries_disabled?
+ if respond_to?(:resource_parent, true)
+ ::Feature.disabled?(:or_issuable_queries, resource_parent)
+ else
+ ::Feature.disabled?(:or_issuable_queries)
+ end
+ end
+
+ def prepare_finder_params(args)
+ params = super(args)
+ params[:not] = params[:not].to_h if params[:not]
+ params[:or] = params[:or].to_h if params[:or]
+ params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
+
+ prepare_author_username_params(params)
+ prepare_assignee_username_params(params)
+ prepare_release_tag_params(params)
+
+ params
+ end
+
+ def prepare_release_tag_params(args)
+ release_tag_wildcard = args.delete(:release_tag_wildcard_id)
+ return if release_tag_wildcard.blank?
+
+ args[:release_tag] ||= release_tag_wildcard
+ end
+
+ def prepare_author_username_params(args)
+ args[:or][:author_username] = args[:or].delete(:author_usernames) if args.dig(:or, :author_usernames).present?
+ end
+
+ def prepare_assignee_username_params(args)
+ args[:assignee_username] = args.delete(:assignee_usernames) if args[:assignee_usernames].present?
+
+ if args.dig(:or, :assignee_usernames).present?
+ args[:or][:assignee_username] = args[:or].delete(:assignee_usernames)
+ end
+
+ return unless args.dig(:not, :assignee_usernames).present?
+
+ args[:not][:assignee_username] = args[:not].delete(:assignee_usernames)
+ end
+
+ def mutually_exclusive_release_tag_args
+ [:release_tag, :release_tag_wildcard_id]
+ end
+
+ def mutually_exclusive_milestone_args
+ [:milestone_title, :milestone_wildcard_id]
+ end
+
+ def mutually_exclusive_assignee_username_args
+ [:assignee_usernames, :assignee_username]
+ end
+
+ def params_not_mutually_exclusive(args, mutually_exclusive_args)
+ return unless args.slice(*mutually_exclusive_args).compact.size > 1
+
+ arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ')
+ raise ::Gitlab::Graphql::Errors::ArgumentError,
+ "only one of [#{arg_str}] arguments is allowed at the same time."
+ end
+ end
+ # rubocop:enable Graphql/ResolverType
+ end
+end
+
+Resolvers::Issues::BaseResolver.prepend_mod