summaryrefslogtreecommitdiff
path: root/app/graphql/resolvers
diff options
context:
space:
mode:
Diffstat (limited to 'app/graphql/resolvers')
-rw-r--r--app/graphql/resolvers/board_list_issues_resolver.rb19
-rw-r--r--app/graphql/resolvers/board_lists_resolver.rb23
-rw-r--r--app/graphql/resolvers/ci/pipeline_stages_resolver.rb21
-rw-r--r--app/graphql/resolvers/ci_configuration/sast_resolver.rb17
-rw-r--r--app/graphql/resolvers/concerns/issue_resolver_fields.rb77
-rw-r--r--app/graphql/resolvers/concerns/resolves_merge_requests.rb3
-rw-r--r--app/graphql/resolvers/design_management/designs_resolver.rb9
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb10
-rw-r--r--app/graphql/resolvers/group_milestones_resolver.rb28
-rw-r--r--app/graphql/resolvers/issue_status_counts_resolver.rb13
-rw-r--r--app/graphql/resolvers/issues_resolver.rb61
-rw-r--r--app/graphql/resolvers/merge_requests_resolver.rb6
-rw-r--r--app/graphql/resolvers/milestone_resolver.rb71
-rw-r--r--app/graphql/resolvers/milestones_resolver.rb55
-rw-r--r--app/graphql/resolvers/project_milestones_resolver.rb20
-rw-r--r--app/graphql/resolvers/project_pipeline_resolver.rb2
-rw-r--r--app/graphql/resolvers/projects/jira_projects_resolver.rb9
-rw-r--r--app/graphql/resolvers/todo_resolver.rb16
18 files changed, 300 insertions, 160 deletions
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb
new file mode 100644
index 00000000000..a7cc367379d
--- /dev/null
+++ b/app/graphql/resolvers/board_list_issues_resolver.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class BoardListIssuesResolver < BaseResolver
+ type Types::IssueType, null: true
+
+ alias_method :list, :object
+
+ def resolve(**args)
+ service = Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], { board_id: list.board.id, id: list.id })
+ Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(service.execute)
+ end
+
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/235681
+ def self.complexity_multiplier(args)
+ 0.005
+ end
+ end
+end
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
index f8d62ba86af..b1d43934f24 100644
--- a/app/graphql/resolvers/board_lists_resolver.rb
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -6,12 +6,16 @@ module Resolvers
type Types::BoardListType, null: true
+ argument :id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'Find a list by its global ID'
+
alias_method :board, :object
- def resolve(lookahead: nil)
+ def resolve(lookahead: nil, id: nil)
authorize!(board)
- lists = board_lists
+ lists = board_lists(id)
if load_preferences?(lookahead)
List.preload_preferences_for_user(lists, context[:current_user])
@@ -22,8 +26,13 @@ module Resolvers
private
- def board_lists
- service = Boards::Lists::ListService.new(board.resource_parent, context[:current_user])
+ def board_lists(id)
+ service = Boards::Lists::ListService.new(
+ board.resource_parent,
+ context[:current_user],
+ list_id: extract_list_id(id)
+ )
+
service.execute(board, create_default_lists: false)
end
@@ -34,5 +43,11 @@ module Resolvers
def load_preferences?(lookahead)
lookahead&.selection(:edges)&.selection(:node)&.selects?(:collapsed)
end
+
+ def extract_list_id(gid)
+ return unless gid.present?
+
+ GitlabSchema.parse_gid(gid, expected_type: ::List).model_id
+ end
end
end
diff --git a/app/graphql/resolvers/ci/pipeline_stages_resolver.rb b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
new file mode 100644
index 00000000000..f9817d8b97b
--- /dev/null
+++ b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class PipelineStagesResolver < BaseResolver
+ include LooksAhead
+
+ alias_method :pipeline, :object
+
+ def resolve_with_lookahead
+ apply_lookahead(pipeline.stages)
+ end
+
+ def preloads
+ {
+ statuses: [:needs]
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci_configuration/sast_resolver.rb b/app/graphql/resolvers/ci_configuration/sast_resolver.rb
deleted file mode 100644
index e8c42076ea2..00000000000
--- a/app/graphql/resolvers/ci_configuration/sast_resolver.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require "json"
-
-module Resolvers
- module CiConfiguration
- class SastResolver < BaseResolver
- SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json'
-
- type ::Types::CiConfiguration::Sast::Type, null: true
-
- def resolve(**args)
- Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH)))
- end
- end
- end
-end
diff --git a/app/graphql/resolvers/concerns/issue_resolver_fields.rb b/app/graphql/resolvers/concerns/issue_resolver_fields.rb
new file mode 100644
index 00000000000..bf2f510dd89
--- /dev/null
+++ b/app/graphql/resolvers/concerns/issue_resolver_fields.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+module IssueResolverFields
+ extend ActiveSupport::Concern
+
+ prepended do
+ argument :iid, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'IID of the issue. For example, "1"'
+ argument :iids, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'List of IIDs of issues. For example, [1, 2]'
+ argument :label_name, GraphQL::STRING_TYPE.to_list_type,
+ required: false,
+ description: 'Labels applied to this issue'
+ argument :milestone_title, GraphQL::STRING_TYPE.to_list_type,
+ required: false,
+ description: 'Milestone applied to this issue'
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of a user assigned to the issue'
+ argument :assignee_id, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'ID of a user assigned to the issues, "none" and "any" values supported'
+ argument :created_before, Types::TimeType,
+ required: false,
+ description: 'Issues created before this date'
+ argument :created_after, Types::TimeType,
+ required: false,
+ description: 'Issues created after this date'
+ argument :updated_before, Types::TimeType,
+ required: false,
+ description: 'Issues updated before this date'
+ argument :updated_after, Types::TimeType,
+ required: false,
+ description: 'Issues updated after this date'
+ argument :closed_before, Types::TimeType,
+ required: false,
+ description: 'Issues closed before this date'
+ argument :closed_after, Types::TimeType,
+ required: false,
+ description: 'Issues closed after this date'
+ argument :search, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Search query for issue title or description'
+ argument :types, [Types::IssueTypeEnum],
+ as: :issue_types,
+ description: 'Filter issues by the given issue types',
+ required: false
+ end
+
+ def resolve(**args)
+ # The project could have been loaded in batch by `BatchLoader`.
+ # At this point we need the `id` of the project to query for issues, so
+ # make sure it's loaded and not `nil` before continuing.
+ parent = object.respond_to?(:sync) ? object.sync : object
+ return Issue.none if parent.nil?
+
+ # Will need to be made group & namespace aware with
+ # https://gitlab.com/gitlab-org/gitlab-foss/issues/54520
+ args[:iids] ||= [args.delete(:iid)].compact if args[:iid]
+ args[:attempt_project_search_optimizations] = true if args[:search].present?
+
+ finder = IssuesFinder.new(current_user, args)
+
+ continue_issue_resolve(parent, finder, **args)
+ end
+
+ class_methods do
+ def resolver_complexity(args, child_complexity:)
+ complexity = super
+ complexity += 2 if args[:labelName]
+
+ complexity
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index 7ed88be52b9..0c01efd4f9a 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -38,6 +38,9 @@ module ResolvesMergeRequests
assignees: [:assignees],
labels: [:labels],
author: [:author],
+ merged_at: [:metrics],
+ commit_count: [:metrics],
+ approved_by: [:approver_users],
milestone: [:milestone],
head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }]
}
diff --git a/app/graphql/resolvers/design_management/designs_resolver.rb b/app/graphql/resolvers/design_management/designs_resolver.rb
index 81f94d5cb30..955ea6304e0 100644
--- a/app/graphql/resolvers/design_management/designs_resolver.rb
+++ b/app/graphql/resolvers/design_management/designs_resolver.rb
@@ -27,19 +27,20 @@ module Resolvers
current_user,
ids: design_ids(ids),
filenames: filenames,
- visible_at_version: version(at_version),
- order: :id
+ visible_at_version: version(at_version)
).execute
end
private
def version(at_version)
- GitlabSchema.object_from_id(at_version)&.sync if at_version
+ return unless at_version
+
+ GitlabSchema.object_from_id(at_version, expected_type: ::DesignManagement::Version)&.sync
end
def design_ids(ids)
- ids&.map { |id| GlobalID.parse(id).model_id }
+ ids&.map { |id| GlobalID.parse(id, expected_type: ::DesignManagement::Design).model_id }
end
def issue
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
new file mode 100644
index 00000000000..ac51011eea8
--- /dev/null
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class GroupIssuesResolver < IssuesResolver
+ argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ default_value: false,
+ description: 'Include issues belonging to subgroups.'
+ end
+end
diff --git a/app/graphql/resolvers/group_milestones_resolver.rb b/app/graphql/resolvers/group_milestones_resolver.rb
new file mode 100644
index 00000000000..8d34cea4fa1
--- /dev/null
+++ b/app/graphql/resolvers/group_milestones_resolver.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class GroupMilestonesResolver < MilestonesResolver
+ argument :include_descendants, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Also return milestones in all subgroups and subprojects'
+
+ private
+
+ def parent_id_parameters(args)
+ return { group_ids: parent.id } unless args[:include_descendants].present?
+
+ {
+ group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id),
+ project_ids: group_projects.with_issues_or_mrs_available_for_user(current_user)
+ }
+ end
+
+ def group_projects
+ GroupProjectsFinder.new(
+ group: parent,
+ current_user: current_user,
+ options: { include_subgroups: true }
+ ).execute
+ end
+ end
+end
diff --git a/app/graphql/resolvers/issue_status_counts_resolver.rb b/app/graphql/resolvers/issue_status_counts_resolver.rb
new file mode 100644
index 00000000000..466ca538467
--- /dev/null
+++ b/app/graphql/resolvers/issue_status_counts_resolver.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class IssueStatusCountsResolver < BaseResolver
+ prepend IssueResolverFields
+
+ type Types::IssueStatusCountsType, null: true
+
+ def continue_issue_resolve(parent, finder, **args)
+ Gitlab::IssuablesCountForState.new(finder, parent)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 9d0535a208f..e2874f6643c 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -2,49 +2,11 @@
module Resolvers
class IssuesResolver < BaseResolver
- argument :iid, GraphQL::STRING_TYPE,
- required: false,
- description: 'IID of the issue. For example, "1"'
+ prepend IssueResolverFields
- argument :iids, [GraphQL::STRING_TYPE],
- required: false,
- description: 'List of IIDs of issues. For example, [1, 2]'
argument :state, Types::IssuableStateEnum,
required: false,
description: 'Current state of this issue'
- argument :label_name, GraphQL::STRING_TYPE.to_list_type,
- required: false,
- description: 'Labels applied to this issue'
- argument :milestone_title, GraphQL::STRING_TYPE.to_list_type,
- required: false,
- description: 'Milestones applied to this issue'
- argument :assignee_username, GraphQL::STRING_TYPE,
- required: false,
- description: 'Username of a user assigned to the issues'
- argument :assignee_id, GraphQL::STRING_TYPE,
- required: false,
- description: 'ID of a user assigned to the issues, "none" and "any" values supported'
- argument :created_before, Types::TimeType,
- required: false,
- description: 'Issues created before this date'
- argument :created_after, Types::TimeType,
- required: false,
- description: 'Issues created after this date'
- argument :updated_before, Types::TimeType,
- required: false,
- description: 'Issues updated before this date'
- argument :updated_after, Types::TimeType,
- required: false,
- description: 'Issues updated after this date'
- argument :closed_before, Types::TimeType,
- required: false,
- description: 'Issues closed before this date'
- argument :closed_after, Types::TimeType,
- required: false,
- description: 'Issues closed after this date'
- argument :search, GraphQL::STRING_TYPE,
- required: false,
- description: 'Search query for issue title or description'
argument :sort, Types::IssueSortEnum,
description: 'Sort issues by this criteria',
required: false,
@@ -56,19 +18,7 @@ module Resolvers
label_priority_asc label_priority_desc
milestone_due_asc milestone_due_desc].freeze
- def resolve(**args)
- # The project could have been loaded in batch by `BatchLoader`.
- # At this point we need the `id` of the project to query for issues, so
- # make sure it's loaded and not `nil` before continuing.
- parent = object.respond_to?(:sync) ? object.sync : object
- return Issue.none if parent.nil?
-
- # Will need to be be made group & namespace aware with
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/54520
- args[:iids] ||= [args.delete(:iid)].compact if args[:iid]
- args[:attempt_project_search_optimizations] = true if args[:search].present?
-
- finder = IssuesFinder.new(current_user, args)
+ def continue_issue_resolve(parent, finder, **args)
issues = Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all
if non_stable_cursor_sort?(args[:sort])
@@ -80,13 +30,6 @@ module Resolvers
end
end
- def self.resolver_complexity(args, child_complexity:)
- complexity = super
- complexity += 2 if args[:labelName]
-
- complexity
- end
-
def non_stable_cursor_sort?(sort)
NON_STABLE_CURSOR_SORTS.include?(sort)
end
diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb
index 3aa52341eec..d15a1ede6fe 100644
--- a/app/graphql/resolvers/merge_requests_resolver.rb
+++ b/app/graphql/resolvers/merge_requests_resolver.rb
@@ -28,6 +28,12 @@ module Resolvers
required: false,
as: :label_name,
description: 'Array of label names. All resolved merge requests will have all of these labels.'
+ argument :merged_after, Types::TimeType,
+ required: false,
+ description: 'Merge requests merged after this date'
+ argument :merged_before, Types::TimeType,
+ required: false,
+ description: 'Merge requests merged before this date'
def self.single
::Resolvers::MergeRequestResolver
diff --git a/app/graphql/resolvers/milestone_resolver.rb b/app/graphql/resolvers/milestone_resolver.rb
deleted file mode 100644
index bcfbc63c31f..00000000000
--- a/app/graphql/resolvers/milestone_resolver.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- class MilestoneResolver < BaseResolver
- include Gitlab::Graphql::Authorize::AuthorizeResource
- include TimeFrameArguments
-
- argument :state, Types::MilestoneStateEnum,
- required: false,
- description: 'Filter milestones by state'
-
- argument :include_descendants, GraphQL::BOOLEAN_TYPE,
- required: false,
- description: 'Return also milestones in all subgroups and subprojects'
-
- type Types::MilestoneType, null: true
-
- def resolve(**args)
- validate_timeframe_params!(args)
-
- authorize!
-
- MilestonesFinder.new(milestones_finder_params(args)).execute
- end
-
- private
-
- def milestones_finder_params(args)
- {
- state: args[:state] || 'all',
- start_date: args[:start_date],
- end_date: args[:end_date]
- }.merge(parent_id_parameter(args))
- end
-
- def parent
- @parent ||= object.respond_to?(:sync) ? object.sync : object
- end
-
- def parent_id_parameter(args)
- if parent.is_a?(Group)
- group_parameters(args)
- elsif parent.is_a?(Project)
- { project_ids: parent.id }
- end
- end
-
- # MilestonesFinder does not check for current_user permissions,
- # so for now we need to keep it here.
- def authorize!
- Ability.allowed?(context[:current_user], :read_milestone, parent) || raise_resource_not_available_error!
- end
-
- def group_parameters(args)
- return { group_ids: parent.id } unless args[:include_descendants].present?
-
- {
- group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id),
- project_ids: group_projects.with_issues_or_mrs_available_for_user(current_user)
- }
- end
-
- def group_projects
- GroupProjectsFinder.new(
- group: parent,
- current_user: current_user,
- options: { include_subgroups: true }
- ).execute
- end
- end
-end
diff --git a/app/graphql/resolvers/milestones_resolver.rb b/app/graphql/resolvers/milestones_resolver.rb
new file mode 100644
index 00000000000..5f80506c01b
--- /dev/null
+++ b/app/graphql/resolvers/milestones_resolver.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class MilestonesResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ include TimeFrameArguments
+
+ argument :ids, [GraphQL::ID_TYPE],
+ required: false,
+ description: 'Array of global milestone IDs, e.g., "gid://gitlab/Milestone/1"'
+
+ argument :state, Types::MilestoneStateEnum,
+ required: false,
+ description: 'Filter milestones by state'
+
+ type Types::MilestoneType, null: true
+
+ def resolve(**args)
+ validate_timeframe_params!(args)
+
+ authorize!
+
+ MilestonesFinder.new(milestones_finder_params(args)).execute
+ end
+
+ private
+
+ def milestones_finder_params(args)
+ {
+ ids: parse_gids(args[:ids]),
+ state: args[:state] || 'all',
+ start_date: args[:start_date],
+ end_date: args[:end_date]
+ }.merge(parent_id_parameters(args))
+ end
+
+ def parent
+ synchronized_object
+ end
+
+ def parent_id_parameters(args)
+ raise NotImplementedError
+ end
+
+ # MilestonesFinder does not check for current_user permissions,
+ # so for now we need to keep it here.
+ def authorize!
+ Ability.allowed?(context[:current_user], :read_milestone, parent) || raise_resource_not_available_error!
+ end
+
+ def parse_gids(gids)
+ gids&.map { |gid| GitlabSchema.parse_gid(gid, expected_type: Milestone).model_id }
+ end
+ end
+end
diff --git a/app/graphql/resolvers/project_milestones_resolver.rb b/app/graphql/resolvers/project_milestones_resolver.rb
new file mode 100644
index 00000000000..976fc300b87
--- /dev/null
+++ b/app/graphql/resolvers/project_milestones_resolver.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ProjectMilestonesResolver < MilestonesResolver
+ argument :include_ancestors, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: "Also return milestones in the project's parent group and its ancestors"
+
+ private
+
+ def parent_id_parameters(args)
+ return { project_ids: parent.id } unless args[:include_ancestors].present? && parent.group.present?
+
+ {
+ group_ids: parent.group.self_and_ancestors.select(:id),
+ project_ids: parent.id
+ }
+ end
+ end
+end
diff --git a/app/graphql/resolvers/project_pipeline_resolver.rb b/app/graphql/resolvers/project_pipeline_resolver.rb
index 5bafe3dd140..181c1e77109 100644
--- a/app/graphql/resolvers/project_pipeline_resolver.rb
+++ b/app/graphql/resolvers/project_pipeline_resolver.rb
@@ -10,7 +10,7 @@ module Resolvers
def resolve(iid:)
BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args|
- args[:key].ci_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) }
+ args[:key].all_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) }
end
end
end
diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb
index 2dc712128cc..ed382ac82d0 100644
--- a/app/graphql/resolvers/projects/jira_projects_resolver.rb
+++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb
@@ -16,7 +16,14 @@ module Resolvers
response = jira_projects(name: name)
if response.success?
- response.payload[:projects]
+ projects_array = response.payload[:projects]
+
+ GraphQL::Pagination::ArrayConnection.new(
+ projects_array,
+ # override default max_page_size to whatever the size of the response is,
+ # see https://gitlab.com/gitlab-org/gitlab/-/issues/231394
+ args.merge({ max_page_size: projects_array.size })
+ )
else
raise Gitlab::Graphql::Errors::BaseError, response.message
end
diff --git a/app/graphql/resolvers/todo_resolver.rb b/app/graphql/resolvers/todo_resolver.rb
index cff65321dc0..bd5f8f274cd 100644
--- a/app/graphql/resolvers/todo_resolver.rb
+++ b/app/graphql/resolvers/todo_resolver.rb
@@ -4,7 +4,7 @@ module Resolvers
class TodoResolver < BaseResolver
type Types::TodoType, null: true
- alias_method :user, :object
+ alias_method :target, :object
argument :action, [Types::TodoActionEnum],
required: false,
@@ -31,9 +31,10 @@ module Resolvers
description: 'The type of the todo'
def resolve(**args)
- return Todo.none if user != context[:current_user]
+ return Todo.none unless current_user.present? && target.present?
+ return Todo.none if target.is_a?(User) && target != current_user
- TodosFinder.new(user, todo_finder_params(args)).execute
+ TodosFinder.new(current_user, todo_finder_params(args)).execute
end
private
@@ -46,6 +47,15 @@ module Resolvers
author_id: args[:author_id],
action_id: args[:action],
project_id: args[:project_id]
+ }.merge(target_params)
+ end
+
+ def target_params
+ return {} unless TodosFinder::TODO_TYPES.include?(target.class.name)
+
+ {
+ type: target.class.name,
+ target_id: target.id
}
end
end