summaryrefslogtreecommitdiff
path: root/app/graphql
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-20 18:42:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-20 18:42:06 +0000
commit6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch)
tree78be5963ec075d80116a932011d695dd33910b4e /app/graphql
parent1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff)
downloadgitlab-ce-6e4e1050d9dba2b7b2523fdd1768823ab85feef4.tar.gz
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/gitlab_schema.rb16
-rw-r--r--app/graphql/mutations/boards/issues/issue_move_list.rb91
-rw-r--r--app/graphql/mutations/boards/lists/base.rb28
-rw-r--r--app/graphql/mutations/boards/lists/create.rb73
-rw-r--r--app/graphql/mutations/boards/lists/update.rb52
-rw-r--r--app/graphql/mutations/concerns/mutations/assignable.rb52
-rw-r--r--app/graphql/mutations/concerns/mutations/resolves_subscription.rb25
-rw-r--r--app/graphql/mutations/design_management/move.rb46
-rw-r--r--app/graphql/mutations/issues/base.rb2
-rw-r--r--app/graphql/mutations/issues/set_assignees.rb15
-rw-r--r--app/graphql/mutations/issues/set_subscription.rb11
-rw-r--r--app/graphql/mutations/issues/update.rb21
-rw-r--r--app/graphql/mutations/merge_requests/create.rb17
-rw-r--r--app/graphql/mutations/merge_requests/set_assignees.rb39
-rw-r--r--app/graphql/mutations/merge_requests/set_subscription.rb17
-rw-r--r--app/graphql/mutations/notes/update/base.rb2
-rw-r--r--app/graphql/mutations/notes/update/note.rb7
-rw-r--r--app/graphql/mutations/snippets/create.rb8
-rw-r--r--app/graphql/mutations/snippets/update.rb8
-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
-rw-r--r--app/graphql/types/alert_management/alert_type.rb26
-rw-r--r--app/graphql/types/board_list_type.rb27
-rw-r--r--app/graphql/types/ci/group_type.rb17
-rw-r--r--app/graphql/types/ci/job_type.rb15
-rw-r--r--app/graphql/types/ci/pipeline_config_source_enum.rb11
-rw-r--r--app/graphql/types/ci/pipeline_type.rb13
-rw-r--r--app/graphql/types/ci/stage_type.rb15
-rw-r--r--app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb25
-rw-r--r--app/graphql/types/ci_configuration/sast/entity_type.rb34
-rw-r--r--app/graphql/types/ci_configuration/sast/options_entity_type.rb19
-rw-r--r--app/graphql/types/ci_configuration/sast/type.rb22
-rw-r--r--app/graphql/types/commit_type.rb3
-rw-r--r--app/graphql/types/countable_connection_type.rb24
-rw-r--r--app/graphql/types/environment_type.rb5
-rw-r--r--app/graphql/types/group_type.rb6
-rw-r--r--app/graphql/types/issuable_state_enum.rb1
-rw-r--r--app/graphql/types/issue_connection_type.rb13
-rw-r--r--app/graphql/types/issue_status_counts_type.rb23
-rw-r--r--app/graphql/types/issue_type.rb6
-rw-r--r--app/graphql/types/issue_type_enum.rb12
-rw-r--r--app/graphql/types/merge_request_type.rb13
-rw-r--r--app/graphql/types/mutation_type.rb6
-rw-r--r--app/graphql/types/project_type.rb18
-rw-r--r--app/graphql/types/projects/services/jira_service_type.rb2
-rw-r--r--app/graphql/types/prometheus_alert_type.rb20
-rw-r--r--app/graphql/types/query_type.rb9
-rw-r--r--app/graphql/types/snippet_type.rb3
-rw-r--r--app/graphql/types/snippets/blob_action_enum.rb (renamed from app/graphql/types/snippets/file_input_action_enum.rb)6
-rw-r--r--app/graphql/types/snippets/blob_action_input_type.rb (renamed from app/graphql/types/snippets/file_input_type.rb)6
-rw-r--r--app/graphql/types/time_type.rb2
-rw-r--r--app/graphql/types/tree/blob_type.rb2
-rw-r--r--app/graphql/types/tree/tree_entry_type.rb2
-rw-r--r--app/graphql/types/user_status_type.rb15
-rw-r--r--app/graphql/types/user_type.rb6
71 files changed, 1050 insertions, 367 deletions
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index 592167a633b..d8967da9f57 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -48,6 +48,13 @@ class GitlabSchema < GraphQL::Schema
super(query_str, **kwargs)
end
+ def get_type(type_name)
+ # This is a backwards compatibility hack to work around an accidentally
+ # released argument typed as EEIterationID
+ type_name = type_name.gsub(/^EE/, '') if type_name.end_with?('ID')
+ super(type_name)
+ end
+
def id_from_object(object, _type = nil, _ctx = nil)
unless object.respond_to?(:to_global_id)
# This is an error in our schema and needs to be solved. So raise a
@@ -77,6 +84,8 @@ class GitlabSchema < GraphQL::Schema
# will be called.
# * All other classes will use `GlobalID#find`
def find_by_gid(gid)
+ return unless gid
+
if gid.model_class < ApplicationRecord
Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
elsif gid.model_class.respond_to?(:lazy_find)
@@ -142,6 +151,13 @@ class GitlabSchema < GraphQL::Schema
end
end
end
+
+ # This is a backwards compatibility hack to work around an accidentally
+ # released argument typed as EE{Type}ID
+ def get_type(type_name)
+ type_name = type_name.gsub(/^EE/, '') if type_name.end_with?('ID')
+ super(type_name)
+ end
end
GitlabSchema.prepend_if_ee('EE::GitlabSchema') # rubocop: disable Cop/InjectEnterpriseEditionModule
diff --git a/app/graphql/mutations/boards/issues/issue_move_list.rb b/app/graphql/mutations/boards/issues/issue_move_list.rb
new file mode 100644
index 00000000000..d4bf47af4cf
--- /dev/null
+++ b/app/graphql/mutations/boards/issues/issue_move_list.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module Issues
+ class IssueMoveList < Mutations::Issues::Base
+ graphql_name 'IssueMoveList'
+
+ argument :board_id, GraphQL::ID_TYPE,
+ required: true,
+ loads: Types::BoardType,
+ description: 'Global ID of the board that the issue is in'
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: 'Project the issue to mutate is in'
+
+ argument :iid, GraphQL::STRING_TYPE,
+ required: true,
+ description: 'IID of the issue to mutate'
+
+ argument :from_list_id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'ID of the board list that the issue will be moved from'
+
+ argument :to_list_id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'ID of the board list that the issue will be moved to'
+
+ argument :move_before_id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'ID of issue before which the current issue will be positioned at'
+
+ argument :move_after_id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'ID of issue after which the current issue will be positioned at'
+
+ def ready?(**args)
+ if move_arguments(args).blank?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'At least one of the arguments fromListId, toListId, afterId or beforeId is required'
+ end
+
+ if move_list_arguments(args).one?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'Both fromListId and toListId must be present'
+ end
+
+ super
+ end
+
+ def resolve(board:, **args)
+ raise_resource_not_available_error! unless board
+ authorize_board!(board)
+
+ issue = authorized_find!(project_path: args[:project_path], iid: args[:iid])
+ move_params = { id: issue.id, board_id: board.id }.merge(move_arguments(args))
+
+ move_issue(board, issue, move_params)
+
+ {
+ issue: issue.reset,
+ errors: issue.errors.full_messages
+ }
+ end
+
+ private
+
+ def move_issue(board, issue, move_params)
+ service = ::Boards::Issues::MoveService.new(board.resource_parent, current_user, move_params)
+
+ service.execute(issue)
+ end
+
+ def move_list_arguments(args)
+ args.slice(:from_list_id, :to_list_id)
+ end
+
+ def move_arguments(args)
+ args.slice(:from_list_id, :to_list_id, :move_after_id, :move_before_id)
+ end
+
+ def authorize_board!(board)
+ return if Ability.allowed?(current_user, :read_board, board.resource_parent)
+
+ raise_resource_not_available_error!
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/lists/base.rb b/app/graphql/mutations/boards/lists/base.rb
new file mode 100644
index 00000000000..34b271ba3b8
--- /dev/null
+++ b/app/graphql/mutations/boards/lists/base.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module Lists
+ class Base < BaseMutation
+ include Mutations::ResolvesIssuable
+
+ argument :board_id, ::Types::GlobalIDType[::Board],
+ required: true,
+ description: 'The Global ID of the issue board to mutate'
+
+ field :list,
+ Types::BoardListType,
+ null: true,
+ description: 'List of the issue board'
+
+ authorize :admin_list
+
+ private
+
+ def find_object(id:)
+ GitlabSchema.object_from_id(id, expected_type: ::Board)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb
new file mode 100644
index 00000000000..4f545709ee9
--- /dev/null
+++ b/app/graphql/mutations/boards/lists/create.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module Lists
+ class Create < Base
+ graphql_name 'BoardListCreate'
+
+ argument :backlog, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Create the backlog list'
+
+ argument :label_id, ::Types::GlobalIDType[::Label],
+ required: false,
+ description: 'ID of an existing label'
+
+ def ready?(**args)
+ if args.slice(*mutually_exclusive_args).size != 1
+ arg_str = mutually_exclusive_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
+
+ super
+ end
+
+ def resolve(**args)
+ board = authorized_find!(id: args[:board_id])
+ params = create_list_params(args)
+
+ authorize_list_type_resource!(board, params)
+
+ list = create_list(board, params)
+
+ {
+ list: list.valid? ? list : nil,
+ errors: errors_on_object(list)
+ }
+ end
+
+ private
+
+ def authorize_list_type_resource!(board, params)
+ return unless params[:label_id]
+
+ labels = ::Labels::AvailableLabelsService.new(current_user, board.resource_parent, params)
+ .filter_labels_ids_in_param(:label_id)
+
+ unless labels.present?
+ raise Gitlab::Graphql::Errors::ArgumentError, 'Label not found!'
+ end
+ end
+
+ def create_list(board, params)
+ create_list_service =
+ ::Boards::Lists::CreateService.new(board.resource_parent, current_user, params)
+
+ create_list_service.execute(board)
+ end
+
+ def create_list_params(args)
+ params = args.slice(*mutually_exclusive_args).with_indifferent_access
+ params[:label_id] = GitlabSchema.parse_gid(params[:label_id]).model_id if params[:label_id]
+
+ params
+ end
+
+ def mutually_exclusive_args
+ [:backlog, :label_id]
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/lists/update.rb b/app/graphql/mutations/boards/lists/update.rb
new file mode 100644
index 00000000000..7efed3058b3
--- /dev/null
+++ b/app/graphql/mutations/boards/lists/update.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module Lists
+ class Update < BaseMutation
+ graphql_name 'UpdateBoardList'
+
+ argument :list_id, GraphQL::ID_TYPE,
+ required: true,
+ loads: Types::BoardListType,
+ description: 'Global ID of the list.'
+
+ argument :position, GraphQL::INT_TYPE,
+ required: false,
+ description: 'Position of list within the board'
+
+ argument :collapsed, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Indicates if list is collapsed for this user'
+
+ field :list,
+ Types::BoardListType,
+ null: true,
+ description: 'Mutated list'
+
+ def resolve(list: nil, **args)
+ raise_resource_not_available_error! unless can_read_list?(list)
+ update_result = update_list(list, args)
+
+ {
+ list: update_result[:list],
+ errors: list.errors.full_messages
+ }
+ end
+
+ private
+
+ def update_list(list, args)
+ service = ::Boards::Lists::UpdateService.new(list.board, current_user, args)
+ service.execute(list)
+ end
+
+ def can_read_list?(list)
+ return false unless list.present?
+
+ Ability.allowed?(current_user, :read_list, list.board)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/concerns/mutations/assignable.rb b/app/graphql/mutations/concerns/mutations/assignable.rb
new file mode 100644
index 00000000000..f6f4b744f4e
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/assignable.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Assignable
+ extend ActiveSupport::Concern
+
+ included do
+ argument :assignee_usernames,
+ [GraphQL::STRING_TYPE],
+ required: true,
+ description: 'The usernames to assign to the resource. Replaces existing assignees by default.'
+
+ argument :operation_mode,
+ Types::MutationOperationModeEnum,
+ required: false,
+ description: 'The operation to perform. Defaults to REPLACE.'
+ end
+
+ def resolve(project_path:, iid:, assignee_usernames:, operation_mode: Types::MutationOperationModeEnum.enum[:replace])
+ resource = authorized_find!(project_path: project_path, iid: iid)
+
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/36098') if resource.is_a?(MergeRequest)
+
+ update_service_class.new(
+ resource.project,
+ current_user,
+ assignee_ids: assignee_ids(resource, assignee_usernames, operation_mode)
+ ).execute(resource)
+
+ {
+ resource.class.name.underscore.to_sym => resource,
+ errors: errors_on_object(resource)
+ }
+ end
+
+ private
+
+ def assignee_ids(resource, usernames, operation_mode)
+ assignee_ids = []
+ assignee_ids += resource.assignees.map(&:id) if Types::MutationOperationModeEnum.enum.values_at(:remove, :append).include?(operation_mode)
+ user_ids = UsersFinder.new(current_user, username: usernames).execute.map(&:id)
+
+ if operation_mode == Types::MutationOperationModeEnum.enum[:remove]
+ assignee_ids -= user_ids
+ else
+ assignee_ids |= user_ids
+ end
+
+ assignee_ids
+ end
+ end
+end
diff --git a/app/graphql/mutations/concerns/mutations/resolves_subscription.rb b/app/graphql/mutations/concerns/mutations/resolves_subscription.rb
new file mode 100644
index 00000000000..e8c5d0d404d
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/resolves_subscription.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ResolvesSubscription
+ extend ActiveSupport::Concern
+ included do
+ argument :subscribed_state,
+ GraphQL::BOOLEAN_TYPE,
+ required: true,
+ description: 'The desired state of the subscription'
+ end
+
+ def resolve(project_path:, iid:, subscribed_state:)
+ resource = authorized_find!(project_path: project_path, iid: iid)
+ project = resource.project
+
+ resource.set_subscription(current_user, subscribed_state, project)
+
+ {
+ resource.class.name.underscore.to_sym => resource,
+ errors: errors_on_object(resource)
+ }
+ end
+ end
+end
diff --git a/app/graphql/mutations/design_management/move.rb b/app/graphql/mutations/design_management/move.rb
new file mode 100644
index 00000000000..0b654447844
--- /dev/null
+++ b/app/graphql/mutations/design_management/move.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Mutations
+ module DesignManagement
+ class Move < ::Mutations::BaseMutation
+ graphql_name "DesignManagementMove"
+
+ DesignID = ::Types::GlobalIDType[::DesignManagement::Design]
+
+ argument :id, DesignID, required: true, as: :current_design,
+ description: "ID of the design to move"
+
+ argument :previous, DesignID, required: false, as: :previous_design,
+ description: "ID of the immediately preceding design"
+
+ argument :next, DesignID, required: false, as: :next_design,
+ description: "ID of the immediately following design"
+
+ field :design_collection, Types::DesignManagement::DesignCollectionType,
+ null: true,
+ description: "The current state of the collection"
+
+ def ready(*)
+ raise ::Gitlab::Graphql::Errors::ResourceNotAvailable unless ::Feature.enabled?(:reorder_designs, default_enabled: true)
+ end
+
+ def resolve(**args)
+ service = ::DesignManagement::MoveDesignsService.new(current_user, parameters(args))
+
+ { design_collection: service.collection, errors: service.execute.errors }
+ end
+
+ private
+
+ def parameters(**args)
+ args.transform_values { |id| GitlabSchema.find_by_gid(id) }.transform_values(&:sync).tap do |hash|
+ hash.each { |k, design| not_found(args[k]) unless current_user.can?(:read_design, design) }
+ end
+ end
+
+ def not_found(gid)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, "Resource not available: #{gid}"
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/base.rb b/app/graphql/mutations/issues/base.rb
index 7c545c3eb00..529d48f3cd0 100644
--- a/app/graphql/mutations/issues/base.rb
+++ b/app/graphql/mutations/issues/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::STRING_TYPE,
required: true,
- description: "The iid of the issue to mutate"
+ description: "The IID of the issue to mutate"
field :issue,
Types::IssueType,
diff --git a/app/graphql/mutations/issues/set_assignees.rb b/app/graphql/mutations/issues/set_assignees.rb
new file mode 100644
index 00000000000..a4d1c755b53
--- /dev/null
+++ b/app/graphql/mutations/issues/set_assignees.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class SetAssignees < Base
+ graphql_name 'IssueSetAssignees'
+
+ include Assignable
+
+ def update_service_class
+ ::Issues::UpdateService
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/set_subscription.rb b/app/graphql/mutations/issues/set_subscription.rb
new file mode 100644
index 00000000000..a04c8f5ba2d
--- /dev/null
+++ b/app/graphql/mutations/issues/set_subscription.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class SetSubscription < Base
+ graphql_name 'IssueSetSubscription'
+
+ include ResolvesSubscription
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb
index 7f6d9b0f988..cc03d32731b 100644
--- a/app/graphql/mutations/issues/update.rb
+++ b/app/graphql/mutations/issues/update.rb
@@ -25,6 +25,27 @@ module Mutations
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)
+
+ argument :add_label_ids,
+ [GraphQL::ID_TYPE],
+ required: false,
+ description: 'The IDs of labels to be added to the issue.'
+
+ argument :remove_label_ids,
+ [GraphQL::ID_TYPE],
+ required: false,
+ 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.'
+
def resolve(project_path:, iid:, **args)
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
diff --git a/app/graphql/mutations/merge_requests/create.rb b/app/graphql/mutations/merge_requests/create.rb
index e210987f259..fd2cd58a5ee 100644
--- a/app/graphql/mutations/merge_requests/create.rb
+++ b/app/graphql/mutations/merge_requests/create.rb
@@ -27,6 +27,10 @@ module Mutations
required: false,
description: copy_field_description(Types::MergeRequestType, :description)
+ argument :labels, [GraphQL::STRING_TYPE],
+ required: false,
+ description: copy_field_description(Types::MergeRequestType, :labels)
+
field :merge_request,
Types::MergeRequestType,
null: true,
@@ -34,18 +38,11 @@ module Mutations
authorize :create_merge_request_from
- def resolve(project_path:, title:, source_branch:, target_branch:, description: nil)
+ def resolve(project_path:, **attributes)
project = authorized_find!(full_path: project_path)
+ params = attributes.merge(author_id: current_user.id)
- attributes = {
- title: title,
- source_branch: source_branch,
- target_branch: target_branch,
- author_id: current_user.id,
- description: description
- }
-
- merge_request = ::MergeRequests::CreateService.new(project, current_user, attributes).execute
+ merge_request = ::MergeRequests::CreateService.new(project, current_user, params).execute
{
merge_request: merge_request.valid? ? merge_request : nil,
diff --git a/app/graphql/mutations/merge_requests/set_assignees.rb b/app/graphql/mutations/merge_requests/set_assignees.rb
index de244b62d0f..548c6b55a85 100644
--- a/app/graphql/mutations/merge_requests/set_assignees.rb
+++ b/app/graphql/mutations/merge_requests/set_assignees.rb
@@ -5,43 +5,10 @@ module Mutations
class SetAssignees < Base
graphql_name 'MergeRequestSetAssignees'
- argument :assignee_usernames,
- [GraphQL::STRING_TYPE],
- required: true,
- description: <<~DESC
- The usernames to assign to the merge request. Replaces existing assignees by default.
- DESC
+ include Assignable
- argument :operation_mode,
- Types::MutationOperationModeEnum,
- required: false,
- description: <<~DESC
- The operation to perform. Defaults to REPLACE.
- DESC
-
- def resolve(project_path:, iid:, assignee_usernames:, operation_mode: Types::MutationOperationModeEnum.enum[:replace])
- Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/36098')
-
- merge_request = authorized_find!(project_path: project_path, iid: iid)
- project = merge_request.project
-
- assignee_ids = []
- assignee_ids += merge_request.assignees.map(&:id) if Types::MutationOperationModeEnum.enum.values_at(:remove, :append).include?(operation_mode)
- user_ids = UsersFinder.new(current_user, username: assignee_usernames).execute.map(&:id)
-
- if operation_mode == Types::MutationOperationModeEnum.enum[:remove]
- assignee_ids -= user_ids
- else
- assignee_ids |= user_ids
- end
-
- ::MergeRequests::UpdateService.new(project, current_user, assignee_ids: assignee_ids)
- .execute(merge_request)
-
- {
- merge_request: merge_request,
- errors: errors_on_object(merge_request)
- }
+ def update_service_class
+ ::MergeRequests::UpdateService
end
end
end
diff --git a/app/graphql/mutations/merge_requests/set_subscription.rb b/app/graphql/mutations/merge_requests/set_subscription.rb
index 1535481ab37..7d3c40185c9 100644
--- a/app/graphql/mutations/merge_requests/set_subscription.rb
+++ b/app/graphql/mutations/merge_requests/set_subscription.rb
@@ -5,22 +5,7 @@ module Mutations
class SetSubscription < Base
graphql_name 'MergeRequestSetSubscription'
- argument :subscribed_state,
- GraphQL::BOOLEAN_TYPE,
- required: true,
- description: 'The desired state of the subscription'
-
- def resolve(project_path:, iid:, subscribed_state:)
- merge_request = authorized_find!(project_path: project_path, iid: iid)
- project = merge_request.project
-
- merge_request.set_subscription(current_user, subscribed_state, project)
-
- {
- merge_request: merge_request,
- errors: errors_on_object(merge_request)
- }
- end
+ include ResolvesSubscription
end
end
end
diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb
index 9a53337f253..8a2a78a29ec 100644
--- a/app/graphql/mutations/notes/update/base.rb
+++ b/app/graphql/mutations/notes/update/base.rb
@@ -40,7 +40,7 @@ module Mutations
end
def note_params(_note, args)
- { note: args[:body] }.compact
+ { note: args[:body], confidential: args[:confidential] }.compact
end
end
end
diff --git a/app/graphql/mutations/notes/update/note.rb b/app/graphql/mutations/notes/update/note.rb
index 03a174fc8d9..ca97dad6ded 100644
--- a/app/graphql/mutations/notes/update/note.rb
+++ b/app/graphql/mutations/notes/update/note.rb
@@ -8,9 +8,14 @@ module Mutations
argument :body,
GraphQL::STRING_TYPE,
- required: true,
+ required: false,
description: copy_field_description(Types::Notes::NoteType, :body)
+ argument :confidential,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'The confidentiality flag of a note. Default is false.'
+
private
def pre_update_checks!(note, _args)
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index 89c21486a74..a068fd806f5 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -40,8 +40,8 @@ module Mutations
required: false,
description: 'The paths to files uploaded in the snippet description'
- argument :files, [Types::Snippets::FileInputType],
- description: "The snippet files to create",
+ argument :blob_actions, [Types::Snippets::BlobActionInputType],
+ description: 'Actions to perform over the snippet repository and blobs',
required: false
def resolve(args)
@@ -85,9 +85,9 @@ module Mutations
def create_params(args)
args.tap do |create_args|
- # We need to rename `files` into `snippet_actions` because
+ # We need to rename `blob_actions` into `snippet_actions` because
# it's the expected key param
- create_args[:snippet_actions] = create_args.delete(:files)&.map(&:to_h)
+ create_args[:snippet_actions] = create_args.delete(:blob_actions)&.map(&:to_h)
# We need to rename `uploaded_files` into `files` because
# it's the expected key param
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index 8890158b0df..6ff632ec008 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -30,8 +30,8 @@ module Mutations
description: 'The visibility level of the snippet',
required: false
- argument :files, [Types::Snippets::FileInputType],
- description: 'The snippet files to update',
+ argument :blob_actions, [Types::Snippets::BlobActionInputType],
+ description: 'Actions to perform over the snippet repository and blobs',
required: false
def resolve(args)
@@ -56,9 +56,9 @@ module Mutations
def update_params(args)
args.tap do |update_args|
- # We need to rename `files` into `snippet_actions` because
+ # We need to rename `blob_actions` into `snippet_actions` because
# it's the expected key param
- update_args[:snippet_actions] = update_args.delete(:files)&.map(&:to_h)
+ update_args[:snippet_actions] = update_args.delete(:blob_actions)&.map(&:to_h)
end
end
end
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
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb
index 089d2426158..1a0b0685ffe 100644
--- a/app/graphql/types/alert_management/alert_type.rb
+++ b/app/graphql/types/alert_management/alert_type.rb
@@ -71,7 +71,7 @@ module Types
description: 'Number of events of this alert',
method: :events
- field :details,
+ field :details, # rubocop:disable Graphql/JSONType
GraphQL::Types::JSON,
null: true,
description: 'Alert details'
@@ -94,8 +94,28 @@ module Types
field :metrics_dashboard_url,
GraphQL::STRING_TYPE,
null: true,
- description: 'URL for metrics embed for the alert',
- resolve: -> (alert, _args, _context) { alert.present.metrics_dashboard_url }
+ description: 'URL for metrics embed for the alert'
+
+ field :runbook,
+ GraphQL::STRING_TYPE,
+ null: true,
+ description: 'Runbook for the alert as defined in alert details'
+
+ field :todos,
+ Types::TodoType.connection_type,
+ null: true,
+ description: 'Todos of the current user for the alert',
+ resolver: Resolvers::TodoResolver
+
+ field :details_url,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'The URL of the alert detail page'
+
+ field :prometheus_alert,
+ Types::PrometheusAlertType,
+ null: true,
+ description: 'The alert condition for Prometheus'
def notes
object.ordered_notes
diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb
index e94ff898807..70c0794fc90 100644
--- a/app/graphql/types/board_list_type.rb
+++ b/app/graphql/types/board_list_type.rb
@@ -3,6 +3,8 @@
module Types
# rubocop: disable Graphql/AuthorizeTypes
class BoardListType < BaseObject
+ include Gitlab::Utils::StrongMemoize
+
graphql_name 'BoardList'
description 'Represents a list for an issue board'
@@ -19,6 +21,31 @@ module Types
field :collapsed, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if list is collapsed for this user',
resolve: -> (list, _args, ctx) { list.collapsed?(ctx[:current_user]) }
+ field :issues_count, GraphQL::INT_TYPE, null: true,
+ description: 'Count of issues in the list'
+
+ field :issues, ::Types::IssueType.connection_type, null: true,
+ description: 'Board issues',
+ resolver: ::Resolvers::BoardListIssuesResolver
+
+ def issues_count
+ metadata[:size]
+ end
+
+ def total_weight
+ metadata[:total_weight]
+ end
+
+ def metadata
+ strong_memoize(:metadata) do
+ list = self.object
+ user = context[:current_user]
+
+ Boards::Issues::ListService
+ .new(list.board.resource_parent, user, board_id: list.board_id, id: list.id)
+ .metadata
+ end
+ end
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/ci/group_type.rb b/app/graphql/types/ci/group_type.rb
new file mode 100644
index 00000000000..04c0eb93068
--- /dev/null
+++ b/app/graphql/types/ci/group_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class GroupType < BaseObject
+ graphql_name 'CiGroup'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job group'
+ field :size, GraphQL::INT_TYPE, null: true,
+ description: 'Size of the group'
+ field :jobs, Ci::JobType.connection_type, null: true,
+ description: 'Jobs in group'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
new file mode 100644
index 00000000000..4c18f3ffd52
--- /dev/null
+++ b/app/graphql/types/ci/job_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class JobType < BaseObject
+ graphql_name 'CiJob'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job'
+ field :needs, JobType.connection_type, null: true,
+ description: 'Builds that must complete before the jobs run'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_config_source_enum.rb b/app/graphql/types/ci/pipeline_config_source_enum.rb
new file mode 100644
index 00000000000..48f88c133b4
--- /dev/null
+++ b/app/graphql/types/ci/pipeline_config_source_enum.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class PipelineConfigSourceEnum < BaseEnum
+ ::Ci::PipelineEnums.config_sources.keys.each do |state_symbol|
+ value state_symbol.to_s.upcase, value: state_symbol.to_s
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index 32050766e5b..82a9f8495ce 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -5,6 +5,8 @@ module Types
class PipelineType < BaseObject
graphql_name 'Pipeline'
+ connection_type_class(Types::CountableConnectionType)
+
authorize :read_pipeline
expose_permissions Types::PermissionTypes::Ci::Pipeline
@@ -23,6 +25,8 @@ module Types
field :detailed_status, Types::Ci::DetailedStatusType, null: false,
description: 'Detailed status of the pipeline',
resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ field :config_source, PipelineConfigSourceEnum, null: true,
+ description: "Config source of the pipeline (#{::Ci::PipelineEnums.config_sources.keys.join(', ').upcase})"
field :duration, GraphQL::INT_TYPE, null: true,
description: 'Duration of the pipeline in seconds'
field :coverage, GraphQL::FLOAT_TYPE, null: true,
@@ -37,8 +41,13 @@ module Types
description: "Timestamp of the pipeline's completion"
field :committed_at, Types::TimeType, null: true,
description: "Timestamp of the pipeline's commit"
-
- # TODO: Add triggering user as a type
+ field :stages, Types::Ci::StageType.connection_type, null: true,
+ description: 'Stages of the pipeline',
+ extras: [:lookahead],
+ resolver: Resolvers::Ci::PipelineStagesResolver
+ field :user, Types::UserType, null: true,
+ description: 'Pipeline user',
+ resolve: -> (pipeline, _args, _context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, pipeline.user_id).find }
end
end
end
diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb
new file mode 100644
index 00000000000..278c4d4d748
--- /dev/null
+++ b/app/graphql/types/ci/stage_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class StageType < BaseObject
+ graphql_name 'CiStage'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the stage'
+ field :groups, Ci::GroupType.connection_type, null: true,
+ description: 'Group of jobs for the stage'
+ end
+ end
+end
diff --git a/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb b/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb
deleted file mode 100644
index ccd1c7dd0eb..00000000000
--- a/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module CiConfiguration
- module Sast
- # rubocop: disable Graphql/AuthorizeTypes
- class AnalyzersEntityType < BaseObject
- graphql_name 'SastCiConfigurationAnalyzersEntity'
- description 'Represents an analyzer entity in SAST CI configuration'
-
- field :name, GraphQL::STRING_TYPE, null: true,
- description: 'Name of the analyzer.'
-
- field :label, GraphQL::STRING_TYPE, null: true,
- description: 'Analyzer label used in the config UI.'
-
- field :enabled, GraphQL::BOOLEAN_TYPE, null: true,
- description: 'Indicates whether an analyzer is enabled.'
-
- field :description, GraphQL::STRING_TYPE, null: true,
- description: 'Analyzer description that is displayed on the form.'
- end
- end
- end
-end
diff --git a/app/graphql/types/ci_configuration/sast/entity_type.rb b/app/graphql/types/ci_configuration/sast/entity_type.rb
deleted file mode 100644
index b61b582ad20..00000000000
--- a/app/graphql/types/ci_configuration/sast/entity_type.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module CiConfiguration
- module Sast
- # rubocop: disable Graphql/AuthorizeTypes
- class EntityType < BaseObject
- graphql_name 'SastCiConfigurationEntity'
- description 'Represents an entity in SAST CI configuration'
-
- field :field, GraphQL::STRING_TYPE, null: true,
- description: 'CI keyword of entity.'
-
- field :label, GraphQL::STRING_TYPE, null: true,
- description: 'Label for entity used in the form.'
-
- field :type, GraphQL::STRING_TYPE, null: true,
- description: 'Type of the field value.'
-
- field :options, ::Types::CiConfiguration::Sast::OptionsEntityType.connection_type, null: true,
- description: 'Different possible values of the field.'
-
- field :default_value, GraphQL::STRING_TYPE, null: true,
- description: 'Default value that is used if value is empty.'
-
- field :description, GraphQL::STRING_TYPE, null: true,
- description: 'Entity description that is displayed on the form.'
-
- field :value, GraphQL::STRING_TYPE, null: true,
- description: 'Current value of the entity.'
- end
- end
- end
-end
diff --git a/app/graphql/types/ci_configuration/sast/options_entity_type.rb b/app/graphql/types/ci_configuration/sast/options_entity_type.rb
deleted file mode 100644
index 86d104a7fda..00000000000
--- a/app/graphql/types/ci_configuration/sast/options_entity_type.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module CiConfiguration
- module Sast
- # rubocop: disable Graphql/AuthorizeTypes
- class OptionsEntityType < BaseObject
- graphql_name 'SastCiConfigurationOptionsEntity'
- description 'Represents an entity for options in SAST CI configuration'
-
- field :label, GraphQL::STRING_TYPE, null: true,
- description: 'Label of option entity.'
-
- field :value, GraphQL::STRING_TYPE, null: true,
- description: 'Value of option entity.'
- end
- end
- end
-end
diff --git a/app/graphql/types/ci_configuration/sast/type.rb b/app/graphql/types/ci_configuration/sast/type.rb
deleted file mode 100644
index 35d11584ac7..00000000000
--- a/app/graphql/types/ci_configuration/sast/type.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module CiConfiguration
- module Sast
- # rubocop: disable Graphql/AuthorizeTypes
- class Type < BaseObject
- graphql_name 'SastCiConfiguration'
- description 'Represents a CI configuration of SAST'
-
- field :global, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
- description: 'List of global entities related to SAST configuration.'
-
- field :pipeline, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
- description: 'List of pipeline entities related to SAST configuration.'
-
- field :analyzers, ::Types::CiConfiguration::Sast::AnalyzersEntityType.connection_type, null: true,
- description: 'List of analyzers entities attached to SAST configuration.'
- end
- end
- end
-end
diff --git a/app/graphql/types/commit_type.rb b/app/graphql/types/commit_type.rb
index be5165da545..dd4b4c3b114 100644
--- a/app/graphql/types/commit_type.rb
+++ b/app/graphql/types/commit_type.rb
@@ -17,12 +17,15 @@ module Types
markdown_field :title_html, null: true
field :description, type: GraphQL::STRING_TYPE, null: true,
description: 'Description of the commit message'
+ markdown_field :description_html, null: true
field :message, type: GraphQL::STRING_TYPE, null: true,
description: 'Raw commit message'
field :authored_date, type: Types::TimeType, null: true,
description: 'Timestamp of when the commit was authored'
field :web_url, type: GraphQL::STRING_TYPE, null: false,
description: 'Web URL of the commit'
+ field :web_path, type: GraphQL::STRING_TYPE, null: false,
+ description: 'Web path of the commit'
field :signature_html, type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
description: 'Rendered HTML of the commit signature'
field :author_name, type: GraphQL::STRING_TYPE, null: true,
diff --git a/app/graphql/types/countable_connection_type.rb b/app/graphql/types/countable_connection_type.rb
new file mode 100644
index 00000000000..2538366b786
--- /dev/null
+++ b/app/graphql/types/countable_connection_type.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class CountableConnectionType < GraphQL::Types::Relay::BaseConnection
+ field :count, Integer, null: false,
+ description: 'Total count of collection'
+
+ def count
+ # rubocop: disable CodeReuse/ActiveRecord
+ relation = object.items
+
+ # sometimes relation is an Array
+ relation = relation.reorder(nil) if relation.respond_to?(:reorder)
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ if relation.try(:group_values)&.present?
+ relation.size.keys.size
+ else
+ relation.size
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb
index 34a90006d03..239b26f9c38 100644
--- a/app/graphql/types/environment_type.rb
+++ b/app/graphql/types/environment_type.rb
@@ -19,5 +19,10 @@ module Types
field :metrics_dashboard, Types::Metrics::DashboardType, null: true,
description: 'Metrics dashboard schema for the environment',
resolver: Resolvers::Metrics::DashboardResolver
+
+ field :latest_opened_most_severe_alert,
+ Types::AlertManagement::AlertType,
+ null: true,
+ description: 'The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned.'
end
end
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index fd7d9a9ba3d..cc8cd7c01f9 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -47,11 +47,11 @@ module Types
Types::IssueType.connection_type,
null: true,
description: 'Issues of the group',
- resolver: Resolvers::IssuesResolver
+ resolver: Resolvers::GroupIssuesResolver
field :milestones, Types::MilestoneType.connection_type, null: true,
- description: 'Find milestones',
- resolver: Resolvers::MilestoneResolver
+ description: 'Milestones of the group',
+ resolver: Resolvers::GroupMilestonesResolver
field :boards,
Types::BoardType.connection_type,
diff --git a/app/graphql/types/issuable_state_enum.rb b/app/graphql/types/issuable_state_enum.rb
index f2f6d6c6cab..543b7f8e5b2 100644
--- a/app/graphql/types/issuable_state_enum.rb
+++ b/app/graphql/types/issuable_state_enum.rb
@@ -8,5 +8,6 @@ module Types
value 'opened'
value 'closed'
value 'locked'
+ value 'all'
end
end
diff --git a/app/graphql/types/issue_connection_type.rb b/app/graphql/types/issue_connection_type.rb
deleted file mode 100644
index beed392f01a..00000000000
--- a/app/graphql/types/issue_connection_type.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- # rubocop: disable Graphql/AuthorizeTypes
- class IssueConnectionType < GraphQL::Types::Relay::BaseConnection
- field :count, Integer, null: false,
- description: 'Total count of collection'
-
- def count
- object.items.size
- end
- end
-end
diff --git a/app/graphql/types/issue_status_counts_type.rb b/app/graphql/types/issue_status_counts_type.rb
new file mode 100644
index 00000000000..f2b1ba8e655
--- /dev/null
+++ b/app/graphql/types/issue_status_counts_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ class IssueStatusCountsType < BaseObject
+ graphql_name 'IssueStatusCountsType'
+ description "Represents total number of issues for the represented statuses."
+
+ authorize :read_issue
+
+ def self.available_issue_states
+ @available_issue_states ||= Issue.available_states.keys.push('all')
+ end
+
+ ::Gitlab::IssuablesCountForState::STATES.each do |state|
+ next unless available_issue_states.include?(state.downcase)
+
+ field state,
+ GraphQL::INT_TYPE,
+ null: true,
+ description: "Number of issues with status #{state.upcase} for the project"
+ end
+ end
+end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 9baa0018999..0a73ce95424 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -4,7 +4,7 @@ module Types
class IssueType < BaseObject
graphql_name 'Issue'
- connection_type_class(Types::IssueConnectionType)
+ connection_type_class(Types::CountableConnectionType)
implements(Types::Notes::NoteableType)
@@ -97,6 +97,10 @@ module Types
field :design_collection, Types::DesignManagement::DesignCollectionType, null: true,
description: 'Collection of design images associated with this issue'
+
+ field :type, Types::IssueTypeEnum, null: true,
+ method: :issue_type,
+ description: 'Type of the issue'
end
end
diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb
new file mode 100644
index 00000000000..7dc45f78c99
--- /dev/null
+++ b/app/graphql/types/issue_type_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class IssueTypeEnum < BaseEnum
+ graphql_name 'IssueType'
+ description 'Issue type'
+
+ ::Issue.issue_types.keys.each do |issue_type|
+ value issue_type.upcase, value: issue_type, description: "#{issue_type.titleize} issue type"
+ end
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index c194b467363..01b02b7976f 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -4,6 +4,8 @@ module Types
class MergeRequestType < BaseObject
graphql_name 'MergeRequest'
+ connection_type_class(Types::CountableConnectionType)
+
implements(Types::Notes::NoteableType)
authorize :read_merge_request
@@ -141,6 +143,8 @@ module Types
end
field :task_completion_status, Types::TaskCompletionStatus, null: false,
description: Types::TaskCompletionStatus.description
+ field :commit_count, GraphQL::INT_TYPE, null: true,
+ description: 'Number of commits in the merge request'
def diff_stats(path: nil)
stats = Array.wrap(object.diff_stats&.to_a)
@@ -160,5 +164,14 @@ module Types
hash.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y }
end
end
+
+ def commit_count
+ object&.metrics&.commits_count
+ end
+
+ def approvers
+ object.approver_users
+ end
end
end
+Types::MergeRequestType.prepend_if_ee('::EE::Types::MergeRequestType')
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 49d51b626b2..e143d14676e 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -14,12 +14,17 @@ module Types
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
+ mount_mutation Mutations::Boards::Issues::IssueMoveList
+ mount_mutation Mutations::Boards::Lists::Create
+ mount_mutation Mutations::Boards::Lists::Update
mount_mutation Mutations::Branches::Create, calls_gitaly: true
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::Discussions::ToggleResolve
+ mount_mutation Mutations::Issues::SetAssignees
mount_mutation Mutations::Issues::SetConfidential
mount_mutation Mutations::Issues::SetLocked
mount_mutation Mutations::Issues::SetDueDate
+ mount_mutation Mutations::Issues::SetSubscription
mount_mutation Mutations::Issues::Update
mount_mutation Mutations::MergeRequests::Create
mount_mutation Mutations::MergeRequests::Update
@@ -55,6 +60,7 @@ module Types
mount_mutation Mutations::JiraImport::ImportUsers
mount_mutation Mutations::DesignManagement::Upload, calls_gitaly: true
mount_mutation Mutations::DesignManagement::Delete, calls_gitaly: true
+ mount_mutation Mutations::DesignManagement::Move
mount_mutation Mutations::ContainerExpirationPolicies::Update
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 2251a0f4e0c..5562db69de6 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -148,6 +148,16 @@ module Types
description: 'Issues of the project',
resolver: Resolvers::IssuesResolver
+ field :issue_status_counts,
+ Types::IssueStatusCountsType,
+ null: true,
+ description: 'Counts of issues by status for the project',
+ resolver: Resolvers::IssueStatusCountsResolver
+
+ field :milestones, Types::MilestoneType.connection_type, null: true,
+ description: 'Milestones of the project',
+ resolver: Resolvers::ProjectMilestonesResolver
+
field :project_members,
Types::ProjectMemberType.connection_type,
description: 'Members of the project',
@@ -159,9 +169,11 @@ module Types
description: 'Environments of the project',
resolver: Resolvers::EnvironmentsResolver
- field :sast_ci_configuration, ::Types::CiConfiguration::Sast::Type, null: true,
- description: 'SAST CI configuration for the project',
- resolver: ::Resolvers::CiConfiguration::SastResolver
+ field :environment,
+ Types::EnvironmentType,
+ null: true,
+ description: 'A single environment of the project',
+ resolver: Resolvers::EnvironmentsResolver.single
field :issue,
Types::IssueType,
diff --git a/app/graphql/types/projects/services/jira_service_type.rb b/app/graphql/types/projects/services/jira_service_type.rb
index 8bf85a14cbf..cb0712249e3 100644
--- a/app/graphql/types/projects/services/jira_service_type.rb
+++ b/app/graphql/types/projects/services/jira_service_type.rb
@@ -13,8 +13,6 @@ module Types
field :projects,
Types::Projects::Services::JiraProjectType.connection_type,
null: true,
- connection: false,
- extensions: [Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension],
description: 'List of all Jira projects fetched through Jira REST API',
resolver: Resolvers::Projects::JiraProjectsResolver
end
diff --git a/app/graphql/types/prometheus_alert_type.rb b/app/graphql/types/prometheus_alert_type.rb
new file mode 100644
index 00000000000..1d09a8dbeb7
--- /dev/null
+++ b/app/graphql/types/prometheus_alert_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ class PrometheusAlertType < BaseObject
+ graphql_name 'PrometheusAlert'
+ description 'The alert condition for Prometheus'
+
+ authorize :read_prometheus_alerts
+
+ present_using PrometheusAlertPresenter
+
+ field :id, GraphQL::ID_TYPE, null: false,
+ description: 'ID of the alert condition'
+
+ field :humanized_text,
+ GraphQL::STRING_TYPE,
+ null: false,
+ description: 'The human-readable text of the alert condition'
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index b4cbd96bfdb..c04f4da70cf 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -47,6 +47,15 @@ module Types
null: false,
description: 'Fields related to design management'
+ field :milestone, ::Types::MilestoneType,
+ null: true,
+ description: 'Find a milestone',
+ resolve: -> (_obj, args, _ctx) { GitlabSchema.find_by_gid(args[:id]) } do
+ argument :id, ::Types::GlobalIDType[Milestone],
+ required: true,
+ description: 'Find a milestone by its ID'
+ end
+
field :user, Types::UserType,
null: true,
description: 'Find a user',
diff --git a/app/graphql/types/snippet_type.rb b/app/graphql/types/snippet_type.rb
index 73ca3425ded..db98e62c10a 100644
--- a/app/graphql/types/snippet_type.rb
+++ b/app/graphql/types/snippet_type.rb
@@ -66,7 +66,8 @@ module Types
field :blob, type: Types::Snippets::BlobType,
description: 'Snippet blob',
calls_gitaly: true,
- null: false
+ null: false,
+ deprecated: { reason: 'Use `blobs`', milestone: '13.3' }
field :blobs, type: [Types::Snippets::BlobType],
description: 'Snippet blobs',
diff --git a/app/graphql/types/snippets/file_input_action_enum.rb b/app/graphql/types/snippets/blob_action_enum.rb
index 7785853f3a8..e3f89920f16 100644
--- a/app/graphql/types/snippets/file_input_action_enum.rb
+++ b/app/graphql/types/snippets/blob_action_enum.rb
@@ -2,9 +2,9 @@
module Types
module Snippets
- class FileInputActionEnum < BaseEnum
- graphql_name 'SnippetFileInputActionEnum'
- description 'Type of a snippet file input action'
+ class BlobActionEnum < BaseEnum
+ graphql_name 'SnippetBlobActionEnum'
+ description 'Type of a snippet blob input action'
value 'create', value: :create
value 'update', value: :update
diff --git a/app/graphql/types/snippets/file_input_type.rb b/app/graphql/types/snippets/blob_action_input_type.rb
index 85a02c8f493..ccb6ae3f2c1 100644
--- a/app/graphql/types/snippets/file_input_type.rb
+++ b/app/graphql/types/snippets/blob_action_input_type.rb
@@ -2,11 +2,11 @@
module Types
module Snippets
- class FileInputType < BaseInputObject # rubocop:disable Graphql/AuthorizeTypes
- graphql_name 'SnippetFileInputType'
+ class BlobActionInputType < BaseInputObject # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'SnippetBlobActionInputType'
description 'Represents an action to perform over a snippet file'
- argument :action, Types::Snippets::FileInputActionEnum,
+ argument :action, Types::Snippets::BlobActionEnum,
description: 'Type of input action',
required: true
diff --git a/app/graphql/types/time_type.rb b/app/graphql/types/time_type.rb
index f045a50e672..c31e4873df0 100644
--- a/app/graphql/types/time_type.rb
+++ b/app/graphql/types/time_type.rb
@@ -7,6 +7,8 @@ module Types
def self.coerce_input(value, ctx)
Time.parse(value)
+ rescue ArgumentError, TypeError => e
+ raise GraphQL::CoercionError, e.message
end
def self.coerce_result(value, ctx)
diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb
index 36cae756a0d..cc6bf7b4f00 100644
--- a/app/graphql/types/tree/blob_type.rb
+++ b/app/graphql/types/tree/blob_type.rb
@@ -12,6 +12,8 @@ module Types
field :web_url, GraphQL::STRING_TYPE, null: true,
description: 'Web URL of the blob'
+ field :web_path, GraphQL::STRING_TYPE, null: true,
+ description: 'Web path of the blob'
field :lfs_oid, GraphQL::STRING_TYPE, null: true,
description: 'LFS ID of the blob',
resolve: -> (blob, args, ctx) do
diff --git a/app/graphql/types/tree/tree_entry_type.rb b/app/graphql/types/tree/tree_entry_type.rb
index 81a7a7e66ae..aff2e025761 100644
--- a/app/graphql/types/tree/tree_entry_type.rb
+++ b/app/graphql/types/tree/tree_entry_type.rb
@@ -13,6 +13,8 @@ module Types
field :web_url, GraphQL::STRING_TYPE, null: true,
description: 'Web URL for the tree entry (directory)'
+ field :web_path, GraphQL::STRING_TYPE, null: true,
+ description: 'Web path for the tree entry (directory)'
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/user_status_type.rb b/app/graphql/types/user_status_type.rb
new file mode 100644
index 00000000000..ff277c1f8e8
--- /dev/null
+++ b/app/graphql/types/user_status_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class UserStatusType < BaseObject
+ graphql_name 'UserStatus'
+
+ markdown_field :message_html, null: true,
+ description: 'HTML of the user status message'
+ field :message, GraphQL::STRING_TYPE, null: true,
+ description: 'User status message'
+ field :emoji, GraphQL::STRING_TYPE, null: true,
+ description: 'String representation of emoji'
+ end
+end
diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb
index ab3c84ea539..cb3575b41d1 100644
--- a/app/graphql/types/user_type.rb
+++ b/app/graphql/types/user_type.rb
@@ -18,16 +18,22 @@ module Types
description: 'Human-readable name of the user'
field :state, Types::UserStateEnum, null: false,
description: 'State of the user'
+ field :email, GraphQL::STRING_TYPE, null: true,
+ description: 'User email'
field :avatar_url, GraphQL::STRING_TYPE, null: true,
description: "URL of the user's avatar"
field :web_url, GraphQL::STRING_TYPE, null: false,
description: 'Web URL of the user'
+ field :web_path, GraphQL::STRING_TYPE, null: false,
+ description: 'Web path of the user'
field :todos, Types::TodoType.connection_type, null: false,
resolver: Resolvers::TodoResolver,
description: 'Todos of the user'
field :group_memberships, Types::GroupMemberType.connection_type, null: true,
description: 'Group memberships of the user',
method: :group_members
+ field :status, Types::UserStatusType, null: true,
+ description: 'User status'
field :project_memberships, Types::ProjectMemberType.connection_type, null: true,
description: 'Project memberships of the user',
method: :project_members