summaryrefslogtreecommitdiff
path: root/app/graphql
diff options
context:
space:
mode:
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/batch_loaders/merge_request_diff_summary_batch_loader.rb29
-rw-r--r--app/graphql/mutations/award_emojis/base.rb7
-rw-r--r--app/graphql/mutations/base_mutation.rb1
-rw-r--r--app/graphql/mutations/boards/create.rb78
-rw-r--r--app/graphql/mutations/boards/lists/destroy.rb41
-rw-r--r--app/graphql/mutations/ci/base.rb7
-rw-r--r--app/graphql/mutations/concerns/mutations/spammable_mutation_fields.rb24
-rw-r--r--app/graphql/mutations/design_management/move.rb11
-rw-r--r--app/graphql/mutations/discussions/toggle_resolve.rb7
-rw-r--r--app/graphql/mutations/issues/common_mutation_arguments.rb28
-rw-r--r--app/graphql/mutations/issues/create.rb109
-rw-r--r--app/graphql/mutations/issues/move.rb33
-rw-r--r--app/graphql/mutations/issues/update.rb43
-rw-r--r--app/graphql/mutations/merge_requests/set_milestone.rb2
-rw-r--r--app/graphql/mutations/metrics/dashboard/annotations/create.rb19
-rw-r--r--app/graphql/mutations/notes/base.rb19
-rw-r--r--app/graphql/mutations/notes/create/base.rb11
-rw-r--r--app/graphql/mutations/notes/create/note.rb8
-rw-r--r--app/graphql/mutations/notes/destroy.rb8
-rw-r--r--app/graphql/mutations/notes/update/base.rb2
-rw-r--r--app/graphql/mutations/notes/update/image_diff_note.rb4
-rw-r--r--app/graphql/mutations/notes/update/note.rb4
-rw-r--r--app/graphql/mutations/snippets/create.rb29
-rw-r--r--app/graphql/mutations/snippets/update.rb22
-rw-r--r--app/graphql/mutations/todos/base.rb7
-rw-r--r--app/graphql/mutations/todos/mark_done.rb2
-rw-r--r--app/graphql/mutations/todos/restore.rb2
-rw-r--r--app/graphql/mutations/todos/restore_many.rb18
-rw-r--r--app/graphql/queries/repository/path_last_commit.query.graphql47
-rw-r--r--app/graphql/resolvers/alert_management/alert_resolver.rb4
-rw-r--r--app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb4
-rw-r--r--app/graphql/resolvers/assigned_merge_requests_resolver.rb2
-rw-r--r--app/graphql/resolvers/authored_merge_requests_resolver.rb2
-rw-r--r--app/graphql/resolvers/base_resolver.rb3
-rw-r--r--app/graphql/resolvers/board_list_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/board_lists_resolver.rb10
-rw-r--r--app/graphql/resolvers/board_resolver.rb27
-rw-r--r--app/graphql/resolvers/boards_resolver.rb2
-rw-r--r--app/graphql/resolvers/ci/runner_platforms_resolver.rb30
-rw-r--r--app/graphql/resolvers/concerns/group_issuable_resolver.rb14
-rw-r--r--app/graphql/resolvers/concerns/issue_resolver_arguments.rb6
-rw-r--r--app/graphql/resolvers/concerns/looks_ahead.rb4
-rw-r--r--app/graphql/resolvers/concerns/resolves_merge_requests.rb9
-rw-r--r--app/graphql/resolvers/concerns/time_frame_arguments.rb20
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb7
-rw-r--r--app/graphql/resolvers/group_merge_requests_resolver.rb25
-rw-r--r--app/graphql/resolvers/merge_requests_resolver.rb12
-rw-r--r--app/graphql/resolvers/milestones_resolver.rb27
-rw-r--r--app/graphql/resolvers/project_merge_requests_resolver.rb8
-rw-r--r--app/graphql/resolvers/projects/jira_projects_resolver.rb2
-rw-r--r--app/graphql/resolvers/projects_resolver.rb16
-rw-r--r--app/graphql/resolvers/snippets/blobs_resolver.rb40
-rw-r--r--app/graphql/resolvers/terraform/states_resolver.rb23
-rw-r--r--app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb16
-rw-r--r--app/graphql/types/alert_management/alert_status_counts_type.rb4
-rw-r--r--app/graphql/types/alert_management/alert_type.rb8
-rw-r--r--app/graphql/types/alert_management/status_enum.rb4
-rw-r--r--app/graphql/types/base_argument.rb14
-rw-r--r--app/graphql/types/base_field.rb2
-rw-r--r--app/graphql/types/board_list_type.rb7
-rw-r--r--app/graphql/types/ci/detailed_status_type.rb47
-rw-r--r--app/graphql/types/ci/group_type.rb3
-rw-r--r--app/graphql/types/ci/job_type.rb5
-rw-r--r--app/graphql/types/ci/runner_architecture_type.rb15
-rw-r--r--app/graphql/types/ci/runner_platform_type.rb17
-rw-r--r--app/graphql/types/ci/stage_type.rb3
-rw-r--r--app/graphql/types/ci/status_action_type.rb25
-rw-r--r--app/graphql/types/date_type.rb22
-rw-r--r--app/graphql/types/design_management/design_collection_copy_state_enum.rb27
-rw-r--r--app/graphql/types/design_management/design_collection_type.rb4
-rw-r--r--app/graphql/types/design_management/design_type.rb2
-rw-r--r--app/graphql/types/environment_type.rb8
-rw-r--r--app/graphql/types/global_id_type.rb16
-rw-r--r--app/graphql/types/group_type.rb10
-rw-r--r--app/graphql/types/issue_sort_enum.rb2
-rw-r--r--app/graphql/types/issue_state_event_enum.rb11
-rw-r--r--app/graphql/types/issue_type.rb21
-rw-r--r--app/graphql/types/label_type.rb2
-rw-r--r--app/graphql/types/merge_request_type.rb30
-rw-r--r--app/graphql/types/mutation_type.rb5
-rw-r--r--app/graphql/types/notes/noteable_type.rb32
-rw-r--r--app/graphql/types/package_type_enum.rb8
-rw-r--r--app/graphql/types/project_member_type.rb7
-rw-r--r--app/graphql/types/project_type.rb8
-rw-r--r--app/graphql/types/query_type.rb19
-rw-r--r--app/graphql/types/range_input_type.rb29
-rw-r--r--app/graphql/types/root_storage_statistics_type.rb1
-rw-r--r--app/graphql/types/snippet_type.rb19
-rw-r--r--app/graphql/types/sort_enum.rb15
-rw-r--r--app/graphql/types/terraform/state_type.rb37
-rw-r--r--app/graphql/types/timeframe_input_type.rb10
91 files changed, 1210 insertions, 226 deletions
diff --git a/app/graphql/batch_loaders/merge_request_diff_summary_batch_loader.rb b/app/graphql/batch_loaders/merge_request_diff_summary_batch_loader.rb
new file mode 100644
index 00000000000..9b8737a6703
--- /dev/null
+++ b/app/graphql/batch_loaders/merge_request_diff_summary_batch_loader.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module BatchLoaders
+ class MergeRequestDiffSummaryBatchLoader
+ NIL_STATS = { additions: 0, deletions: 0, file_count: 0 }.freeze
+
+ def self.load_for(merge_request)
+ BatchLoader::GraphQL.for(merge_request).batch(key: :diff_stats_summary) do |merge_requests, loader, args|
+ Preloaders::MergeRequestDiffPreloader.new(merge_requests).preload_all
+
+ merge_requests.each do |merge_request|
+ metrics = merge_request.metrics
+
+ summary = if metrics && metrics.added_lines && metrics.removed_lines
+ { additions: metrics.added_lines, deletions: metrics.removed_lines, file_count: merge_request.merge_request_diff&.files_count || 0 }
+ elsif merge_request.diff_stats.blank?
+ NIL_STATS
+ else
+ merge_request.diff_stats.each_with_object(NIL_STATS.dup) do |status, summary|
+ summary.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y }
+ end
+ end
+
+ loader.call(merge_request, summary)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/award_emojis/base.rb b/app/graphql/mutations/award_emojis/base.rb
index 583744c3884..df6b883529e 100644
--- a/app/graphql/mutations/award_emojis/base.rb
+++ b/app/graphql/mutations/award_emojis/base.rb
@@ -6,7 +6,7 @@ module Mutations
authorize :award_emoji
argument :awardable_id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Awardable],
required: true,
description: 'The global id of the awardable resource'
@@ -23,7 +23,10 @@ module Mutations
private
def find_object(id:)
- GitlabSchema.object_from_id(id)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Awardable].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
end
# Called by mutations methods after performing an authorization check
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb
index 577f10545b3..ac5ddc5bd4c 100644
--- a/app/graphql/mutations/base_mutation.rb
+++ b/app/graphql/mutations/base_mutation.rb
@@ -4,6 +4,7 @@ module Mutations
class BaseMutation < GraphQL::Schema::RelayClassicMutation
prepend Gitlab::Graphql::Authorize::AuthorizeResource
prepend Gitlab::Graphql::CopyFieldDescription
+ prepend ::Gitlab::Graphql::GlobalIDCompatibility
ERROR_MESSAGE = 'You cannot perform write operations on a read-only instance'
diff --git a/app/graphql/mutations/boards/create.rb b/app/graphql/mutations/boards/create.rb
new file mode 100644
index 00000000000..e381205242e
--- /dev/null
+++ b/app/graphql/mutations/boards/create.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ class Create < ::Mutations::BaseMutation
+ include Mutations::ResolvesGroup
+ include ResolvesProject
+
+ graphql_name 'CreateBoard'
+
+ field :board,
+ Types::BoardType,
+ null: true,
+ description: 'The board after mutation.'
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: false,
+ description: 'The project full path the board is associated with.'
+ argument :group_path, GraphQL::ID_TYPE,
+ required: false,
+ description: 'The group full path the board is associated with.'
+ argument :name,
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The board name.'
+ argument :assignee_id,
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The ID of the user to be assigned to the board.'
+ argument :milestone_id,
+ GraphQL::ID_TYPE,
+ required: false,
+ description: 'The ID of the milestone to be assigned to the board.'
+ argument :weight,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'The weight of the board.'
+ argument :label_ids,
+ [GraphQL::ID_TYPE],
+ required: false,
+ description: 'The IDs of labels to be added to the board.'
+
+ authorize :admin_board
+
+ def resolve(args)
+ group_path = args.delete(:group_path)
+ project_path = args.delete(:project_path)
+
+ board_parent = authorized_find!(group_path: group_path, project_path: project_path)
+ response = ::Boards::CreateService.new(board_parent, current_user, args).execute
+
+ {
+ board: response.payload,
+ errors: response.errors
+ }
+ end
+
+ def ready?(**args)
+ if args.values_at(:project_path, :group_path).compact.blank?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'group_path or project_path arguments are required'
+ end
+
+ super
+ end
+
+ private
+
+ def find_object(group_path: nil, project_path: nil)
+ if group_path
+ resolve_group(full_path: group_path)
+ else
+ resolve_project(full_path: project_path)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/lists/destroy.rb b/app/graphql/mutations/boards/lists/destroy.rb
new file mode 100644
index 00000000000..61ffae7c047
--- /dev/null
+++ b/app/graphql/mutations/boards/lists/destroy.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module Lists
+ class Destroy < ::Mutations::BaseMutation
+ graphql_name 'DestroyBoardList'
+
+ field :list,
+ Types::BoardListType,
+ null: true,
+ description: 'The list after mutation.'
+
+ argument :list_id, ::Types::GlobalIDType[::List],
+ required: true,
+ loads: Types::BoardListType,
+ description: 'Global ID of the list to destroy. Only label lists are accepted.'
+
+ def resolve(list:)
+ raise_resource_not_available_error! unless can_admin_list?(list)
+
+ response = ::Boards::Lists::DestroyService.new(list.board.resource_parent, current_user)
+ .execute(list)
+
+ {
+ list: response.success? ? nil : list,
+ errors: response.errors
+ }
+ end
+
+ private
+
+ def can_admin_list?(list)
+ return false unless list.present?
+
+ Ability.allowed?(current_user, :admin_list, list.board)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/base.rb b/app/graphql/mutations/ci/base.rb
index 09df4487a50..aaece2a3021 100644
--- a/app/graphql/mutations/ci/base.rb
+++ b/app/graphql/mutations/ci/base.rb
@@ -3,13 +3,18 @@
module Mutations
module Ci
class Base < BaseMutation
- argument :id, ::Types::GlobalIDType[::Ci::Pipeline],
+ PipelineID = ::Types::GlobalIDType[::Ci::Pipeline]
+
+ argument :id, PipelineID,
required: true,
description: 'The id of the pipeline to mutate'
private
def find_object(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = PipelineID.coerce_isolated_input(id)
GlobalID::Locator.locate(id)
end
end
diff --git a/app/graphql/mutations/concerns/mutations/spammable_mutation_fields.rb b/app/graphql/mutations/concerns/mutations/spammable_mutation_fields.rb
new file mode 100644
index 00000000000..7aef55f8011
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/spammable_mutation_fields.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Mutations
+ module SpammableMutationFields
+ extend ActiveSupport::Concern
+
+ included do
+ field :spam,
+ GraphQL::BOOLEAN_TYPE,
+ null: true,
+ description: 'Indicates whether the operation returns a record detected as spam'
+ end
+
+ def with_spam_params(&block)
+ request = Feature.enabled?(:snippet_spam) ? context[:request] : nil
+
+ yield.merge({ api: true, request: request })
+ end
+
+ def with_spam_fields(spammable, &block)
+ { spam: spammable.spam? }.merge!(yield)
+ end
+ end
+end
diff --git a/app/graphql/mutations/design_management/move.rb b/app/graphql/mutations/design_management/move.rb
index 6126af8b68b..aed4cfec0fd 100644
--- a/app/graphql/mutations/design_management/move.rb
+++ b/app/graphql/mutations/design_management/move.rb
@@ -21,7 +21,7 @@ module Mutations
description: "The current state of the collection"
def resolve(**args)
- service = ::DesignManagement::MoveDesignsService.new(current_user, parameters(args))
+ service = ::DesignManagement::MoveDesignsService.new(current_user, parameters(**args))
{ design_collection: service.collection, errors: service.execute.errors }
end
@@ -29,11 +29,18 @@ module Mutations
private
def parameters(**args)
- args.transform_values { |id| GitlabSchema.find_by_gid(id) }.transform_values(&:sync).tap do |hash|
+ args.transform_values { |id| find_design(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 find_design(id)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = DesignID.coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+
def not_found(gid)
raise Gitlab::Graphql::Errors::ResourceNotAvailable, "Resource not available: #{gid}"
end
diff --git a/app/graphql/mutations/discussions/toggle_resolve.rb b/app/graphql/mutations/discussions/toggle_resolve.rb
index 41fd22c6b55..4492da74706 100644
--- a/app/graphql/mutations/discussions/toggle_resolve.rb
+++ b/app/graphql/mutations/discussions/toggle_resolve.rb
@@ -8,7 +8,7 @@ module Mutations
description 'Toggles the resolved state of a discussion'
argument :id,
- GraphQL::ID_TYPE,
+ Types::GlobalIDType[Discussion],
required: true,
description: 'The global id of the discussion'
@@ -54,7 +54,10 @@ module Mutations
end
def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Discussion)
+ # TODO: remove explicit coercion once compatibility layer has been removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = Types::GlobalIDType[Discussion].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
end
def resolve!(discussion)
diff --git a/app/graphql/mutations/issues/common_mutation_arguments.rb b/app/graphql/mutations/issues/common_mutation_arguments.rb
new file mode 100644
index 00000000000..4b5b246281f
--- /dev/null
+++ b/app/graphql/mutations/issues/common_mutation_arguments.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ module CommonMutationArguments
+ extend ActiveSupport::Concern
+
+ included do
+ argument :description, GraphQL::STRING_TYPE,
+ required: false,
+ description: copy_field_description(Types::IssueType, :description)
+
+ argument :due_date, GraphQL::Types::ISO8601Date,
+ required: false,
+ description: copy_field_description(Types::IssueType, :due_date)
+
+ argument :confidential, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: copy_field_description(Types::IssueType, :confidential)
+
+ argument :locked, GraphQL::BOOLEAN_TYPE,
+ as: :discussion_locked,
+ required: false,
+ description: copy_field_description(Types::IssueType, :discussion_locked)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/create.rb b/app/graphql/mutations/issues/create.rb
new file mode 100644
index 00000000000..1454916bc77
--- /dev/null
+++ b/app/graphql/mutations/issues/create.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class Create < BaseMutation
+ include ResolvesProject
+ graphql_name 'CreateIssue'
+
+ authorize :create_issue
+
+ include CommonMutationArguments
+
+ argument :project_path, GraphQL::ID_TYPE,
+ required: true,
+ description: 'Project full path the issue is associated with'
+
+ argument :iid, GraphQL::INT_TYPE,
+ required: false,
+ description: 'The IID (internal ID) of a project issue. Only admins and project owners can modify'
+
+ argument :title, GraphQL::STRING_TYPE,
+ required: true,
+ description: copy_field_description(Types::IssueType, :title)
+
+ argument :milestone_id, ::Types::GlobalIDType[::Milestone],
+ required: false,
+ description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null'
+
+ argument :labels, [GraphQL::STRING_TYPE],
+ required: false,
+ description: copy_field_description(Types::IssueType, :labels)
+
+ argument :label_ids, [::Types::GlobalIDType[::Label]],
+ required: false,
+ description: 'The IDs of labels to be added to the issue'
+
+ argument :created_at, Types::TimeType,
+ required: false,
+ description: 'Timestamp when the issue was created. Available only for admins and project owners'
+
+ argument :merge_request_to_resolve_discussions_of, ::Types::GlobalIDType[::MergeRequest],
+ required: false,
+ description: 'The IID of a merge request for which to resolve discussions'
+
+ argument :discussion_to_resolve, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The ID of a discussion to resolve. Also pass `merge_request_to_resolve_discussions_of`'
+
+ argument :assignee_ids, [::Types::GlobalIDType[::User]],
+ required: false,
+ description: 'The array of user IDs to assign to the issue'
+
+ field :issue,
+ Types::IssueType,
+ null: true,
+ description: 'The issue after mutation'
+
+ def ready?(**args)
+ if args.slice(*mutually_exclusive_label_args).size > 1
+ arg_str = mutually_exclusive_label_args.map { |x| x.to_s.camelize(:lower) }.join(' or ')
+ raise Gitlab::Graphql::Errors::ArgumentError, "one and only one of #{arg_str} is required."
+ end
+
+ if args[:discussion_to_resolve].present? && args[:merge_request_to_resolve_discussions_of].blank?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'to resolve a discussion please also provide `merge_request_to_resolve_discussions_of` parameter'
+ end
+
+ super
+ end
+
+ def resolve(project_path:, **attributes)
+ project = authorized_find!(full_path: project_path)
+ params = build_create_issue_params(attributes.merge(author_id: current_user.id))
+
+ issue = ::Issues::CreateService.new(project, current_user, params).execute
+
+ if issue.spam?
+ issue.errors.add(:base, 'Spam detected.')
+ end
+
+ {
+ issue: issue.valid? ? issue : nil,
+ errors: errors_on_object(issue)
+ }
+ end
+
+ private
+
+ def build_create_issue_params(params)
+ params[:milestone_id] &&= params[:milestone_id]&.model_id
+ params[:assignee_ids] &&= params[:assignee_ids].map { |assignee_id| assignee_id&.model_id }
+ params[:label_ids] &&= params[:label_ids].map { |label_id| label_id&.model_id }
+
+ params
+ end
+
+ def mutually_exclusive_label_args
+ [:labels, :label_ids]
+ end
+
+ def find_object(full_path:)
+ resolve_project(full_path: full_path)
+ end
+ end
+ end
+end
+
+Mutations::Issues::Create.prepend_if_ee('::EE::Mutations::Issues::Create')
diff --git a/app/graphql/mutations/issues/move.rb b/app/graphql/mutations/issues/move.rb
new file mode 100644
index 00000000000..e6971c9df8c
--- /dev/null
+++ b/app/graphql/mutations/issues/move.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class Move < Base
+ graphql_name 'IssueMove'
+
+ argument :target_project_path,
+ GraphQL::ID_TYPE,
+ required: true,
+ description: 'The project to move the issue to'
+
+ def resolve(project_path:, iid:, target_project_path:)
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/267762')
+
+ issue = authorized_find!(project_path: project_path, iid: iid)
+ source_project = issue.project
+ target_project = resolve_project(full_path: target_project_path).sync
+
+ begin
+ moved_issue = ::Issues::MoveService.new(source_project, current_user).execute(issue, target_project)
+ rescue ::Issues::MoveService::MoveError => error
+ errors = error.message
+ end
+
+ {
+ issue: moved_issue,
+ errors: Array.wrap(errors)
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb
index cc03d32731b..9b216b31f9b 100644
--- a/app/graphql/mutations/issues/update.rb
+++ b/app/graphql/mutations/issues/update.rb
@@ -5,46 +5,27 @@ module Mutations
class Update < Base
graphql_name 'UpdateIssue'
- argument :title,
- GraphQL::STRING_TYPE,
- required: false,
- description: copy_field_description(Types::IssueType, :title)
+ include CommonMutationArguments
- argument :description,
- GraphQL::STRING_TYPE,
- required: false,
- description: copy_field_description(Types::IssueType, :description)
-
- argument :due_date,
- Types::TimeType,
- required: false,
- description: copy_field_description(Types::IssueType, :due_date)
-
- argument :confidential,
- GraphQL::BOOLEAN_TYPE,
+ argument :title, GraphQL::STRING_TYPE,
required: false,
- description: copy_field_description(Types::IssueType, :confidential)
+ description: copy_field_description(Types::IssueType, :title)
- argument :locked,
- GraphQL::BOOLEAN_TYPE,
- as: :discussion_locked,
+ argument :milestone_id, GraphQL::ID_TYPE,
required: false,
- description: copy_field_description(Types::IssueType, :discussion_locked)
+ description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null'
- argument :add_label_ids,
- [GraphQL::ID_TYPE],
+ argument :add_label_ids, [GraphQL::ID_TYPE],
required: false,
- description: 'The IDs of labels to be added to the issue.'
+ description: 'The IDs of labels to be added to the issue'
- argument :remove_label_ids,
- [GraphQL::ID_TYPE],
+ argument :remove_label_ids, [GraphQL::ID_TYPE],
required: false,
- description: 'The IDs of labels to be removed from the issue.'
+ description: 'The IDs of labels to be removed from the issue'
- argument :milestone_id,
- GraphQL::ID_TYPE,
- required: false,
- description: 'The ID of the milestone to be assigned, milestone will be removed if set to null.'
+ argument :state_event, Types::IssueStateEventEnum,
+ description: 'Close or reopen an issue',
+ required: false
def resolve(project_path:, iid:, **args)
issue = authorized_find!(project_path: project_path, iid: iid)
diff --git a/app/graphql/mutations/merge_requests/set_milestone.rb b/app/graphql/mutations/merge_requests/set_milestone.rb
index b3412dd9ed2..abcb1bda1f3 100644
--- a/app/graphql/mutations/merge_requests/set_milestone.rb
+++ b/app/graphql/mutations/merge_requests/set_milestone.rb
@@ -6,7 +6,7 @@ module Mutations
graphql_name 'MergeRequestSetMilestone'
argument :milestone_id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Milestone],
required: false,
loads: Types::MilestoneType,
description: <<~DESC
diff --git a/app/graphql/mutations/metrics/dashboard/annotations/create.rb b/app/graphql/mutations/metrics/dashboard/annotations/create.rb
index f99688aeac6..b064f55825f 100644
--- a/app/graphql/mutations/metrics/dashboard/annotations/create.rb
+++ b/app/graphql/mutations/metrics/dashboard/annotations/create.rb
@@ -18,12 +18,12 @@ module Mutations
description: 'The created annotation'
argument :environment_id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Environment],
required: false,
description: 'The global id of the environment to add an annotation to'
argument :cluster_id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Clusters::Cluster],
required: false,
description: 'The global id of the cluster to add an annotation to'
@@ -80,11 +80,11 @@ module Mutations
raise Gitlab::Graphql::Errors::ArgumentError, ANNOTATION_SOURCE_ARGUMENT_ERROR
end
- super(args)
+ super(**args)
end
def find_object(id:)
- GitlabSchema.object_from_id(id)
+ GitlabSchema.find_by_gid(id)
end
def annotation_create_params(args)
@@ -96,7 +96,16 @@ module Mutations
end
def annotation_source(args)
- annotation_source_id = args[:cluster_id] || args[:environment_id]
+ # TODO: remove these lines when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ annotation_source_id = if args[:cluster_id]
+ ::Types::GlobalIDType[::Clusters::Cluster].coerce_isolated_input(args[:cluster_id])
+ else
+ ::Types::GlobalIDType[::Environment].coerce_isolated_input(args[:environment_id])
+ end
+
+ # TODO: uncomment following line once lines above are removed
+ # annotation_source_id = args[:cluster_id] || args[:environment_id]
authorized_find!(id: annotation_source_id)
end
end
diff --git a/app/graphql/mutations/notes/base.rb b/app/graphql/mutations/notes/base.rb
index 31dabc0a660..f2678211335 100644
--- a/app/graphql/mutations/notes/base.rb
+++ b/app/graphql/mutations/notes/base.rb
@@ -11,21 +11,10 @@ module Mutations
private
def find_object(id:)
- GitlabSchema.object_from_id(id)
- end
-
- def check_object_is_noteable!(object)
- unless object.is_a?(Noteable)
- raise Gitlab::Graphql::Errors::ResourceNotAvailable,
- 'Cannot add notes to this resource'
- end
- end
-
- def check_object_is_note!(object)
- unless object.is_a?(Note)
- raise Gitlab::Graphql::Errors::ResourceNotAvailable,
- 'Resource is not a note'
- end
+ # TODO: remove explicit coercion once compatibility layer has been removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Note].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
end
end
end
diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb
index f081eac368e..3cfdaf84760 100644
--- a/app/graphql/mutations/notes/create/base.rb
+++ b/app/graphql/mutations/notes/create/base.rb
@@ -9,7 +9,7 @@ module Mutations
authorize :create_note
argument :noteable_id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Noteable],
required: true,
description: 'The global id of the resource to add a note to'
@@ -26,8 +26,6 @@ module Mutations
def resolve(args)
noteable = authorized_find!(id: args[:noteable_id])
- check_object_is_noteable!(noteable)
-
note = ::Notes::CreateService.new(
noteable.project,
current_user,
@@ -42,6 +40,13 @@ module Mutations
private
+ def find_object(id:)
+ # TODO: remove explicit coercion once compatibility layer has been removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Noteable].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+
def create_note_params(noteable, args)
{
noteable: noteable,
diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
index 5236e48026e..e97037171f7 100644
--- a/app/graphql/mutations/notes/create/note.rb
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -7,7 +7,7 @@ module Mutations
graphql_name 'CreateNote'
argument :discussion_id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Discussion],
required: false,
description: 'The global id of the discussion this note is in reply to'
@@ -17,7 +17,11 @@ module Mutations
discussion_id = nil
if args[:discussion_id]
- discussion = GitlabSchema.object_from_id(args[:discussion_id])
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ discussion_gid = ::Types::GlobalIDType[::Discussion].coerce_isolated_input(args[:discussion_id])
+ discussion = GitlabSchema.find_by_gid(discussion_gid)
+
authorize_discussion!(discussion)
discussion_id = discussion.id
diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb
index a81322bc9b7..63e5eeb5ecf 100644
--- a/app/graphql/mutations/notes/destroy.rb
+++ b/app/graphql/mutations/notes/destroy.rb
@@ -8,15 +8,13 @@ module Mutations
authorize :admin_note
argument :id,
- GraphQL::ID_TYPE,
- required: true,
- description: 'The global id of the note to destroy'
+ ::Types::GlobalIDType[::Note],
+ required: true,
+ description: 'The global id of the note to destroy'
def resolve(id:)
note = authorized_find!(id: id)
- check_object_is_note!(note)
-
::Notes::DestroyService.new(note.project, current_user).execute(note)
{
diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb
index 8a2a78a29ec..1d5738ada77 100644
--- a/app/graphql/mutations/notes/update/base.rb
+++ b/app/graphql/mutations/notes/update/base.rb
@@ -9,7 +9,7 @@ module Mutations
authorize :admin_note
argument :id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Note],
required: true,
description: 'The global id of the note to update'
diff --git a/app/graphql/mutations/notes/update/image_diff_note.rb b/app/graphql/mutations/notes/update/image_diff_note.rb
index 7aad3af1e04..ef70a8d2bf4 100644
--- a/app/graphql/mutations/notes/update/image_diff_note.rb
+++ b/app/graphql/mutations/notes/update/image_diff_note.rb
@@ -28,12 +28,12 @@ module Mutations
'body or position arguments are required'
end
- super(args)
+ super(**args)
end
private
- def pre_update_checks!(note, args)
+ def pre_update_checks!(note, _args)
unless note.is_a?(DiffNote) && note.position.on_image?
raise Gitlab::Graphql::Errors::ResourceNotAvailable,
'Resource is not an ImageDiffNote'
diff --git a/app/graphql/mutations/notes/update/note.rb b/app/graphql/mutations/notes/update/note.rb
index ca97dad6ded..73b9b9bc49a 100644
--- a/app/graphql/mutations/notes/update/note.rb
+++ b/app/graphql/mutations/notes/update/note.rb
@@ -18,8 +18,8 @@ module Mutations
private
- def pre_update_checks!(note, _args)
- check_object_is_note!(note)
+ def pre_update_checks!(_note, _args)
+ # no-op
end
end
end
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index a8aeb15afcd..37c0f80310c 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -3,6 +3,7 @@
module Mutations
module Snippets
class Create < BaseMutation
+ include SpammableMutationFields
include ResolvesProject
graphql_name 'CreateSnippet'
@@ -56,10 +57,12 @@ module Mutations
::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user)
end
- {
- snippet: service_response.success? ? snippet : nil,
- errors: errors_on_object(snippet)
- }
+ with_spam_fields(snippet) do
+ {
+ snippet: service_response.success? ? snippet : nil,
+ errors: errors_on_object(snippet)
+ }
+ end
end
private
@@ -81,14 +84,16 @@ module Mutations
end
def create_params(args)
- args.tap do |create_args|
- # We need to rename `blob_actions` into `snippet_actions` because
- # it's the expected key param
- 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
- create_args[:files] = create_args.delete(:uploaded_files)
+ with_spam_params do
+ args.tap do |create_args|
+ # We need to rename `blob_actions` into `snippet_actions` because
+ # it's the expected key param
+ 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
+ create_args[:files] = create_args.delete(:uploaded_files)
+ end
end
end
end
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index d0db5fa2eb9..74266880806 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -3,6 +3,8 @@
module Mutations
module Snippets
class Update < Base
+ include SpammableMutationFields
+
graphql_name 'UpdateSnippet'
argument :id,
@@ -39,10 +41,12 @@ module Mutations
::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user)
end
- {
- snippet: result.success? ? snippet : snippet.reset,
- errors: errors_on_object(snippet)
- }
+ with_spam_fields(snippet) do
+ {
+ snippet: result.success? ? snippet : snippet.reset,
+ errors: errors_on_object(snippet)
+ }
+ end
end
private
@@ -52,10 +56,12 @@ module Mutations
end
def update_params(args)
- args.tap do |update_args|
- # We need to rename `blob_actions` into `snippet_actions` because
- # it's the expected key param
- update_args[:snippet_actions] = update_args.delete(:blob_actions)&.map(&:to_h)
+ with_spam_params do
+ args.tap do |update_args|
+ # We need to rename `blob_actions` into `snippet_actions` because
+ # it's the expected key param
+ update_args[:snippet_actions] = update_args.delete(:blob_actions)&.map(&:to_h)
+ end
end
end
end
diff --git a/app/graphql/mutations/todos/base.rb b/app/graphql/mutations/todos/base.rb
index 2a72019fbac..6db863796bc 100644
--- a/app/graphql/mutations/todos/base.rb
+++ b/app/graphql/mutations/todos/base.rb
@@ -6,7 +6,10 @@ module Mutations
private
def find_object(id:)
- GitlabSchema.object_from_id(id)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Todo].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
end
def map_to_global_ids(ids)
@@ -16,7 +19,7 @@ module Mutations
end
def to_global_id(id)
- ::URI::GID.build(app: GlobalID.app, model_name: Todo.name, model_id: id, params: nil).to_s
+ Gitlab::GlobalId.as_global_id(id, model_name: Todo.name).to_s
end
end
end
diff --git a/app/graphql/mutations/todos/mark_done.rb b/app/graphql/mutations/todos/mark_done.rb
index 748e02d8782..3d73022f266 100644
--- a/app/graphql/mutations/todos/mark_done.rb
+++ b/app/graphql/mutations/todos/mark_done.rb
@@ -8,7 +8,7 @@ module Mutations
authorize :update_todo
argument :id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Todo],
required: true,
description: 'The global id of the todo to mark as done'
diff --git a/app/graphql/mutations/todos/restore.rb b/app/graphql/mutations/todos/restore.rb
index a0a1772db0a..7c8f92d32f5 100644
--- a/app/graphql/mutations/todos/restore.rb
+++ b/app/graphql/mutations/todos/restore.rb
@@ -8,7 +8,7 @@ module Mutations
authorize :update_todo
argument :id,
- GraphQL::ID_TYPE,
+ ::Types::GlobalIDType[::Todo],
required: true,
description: 'The global id of the todo to restore'
diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb
index c5e2750768c..ea5f5414134 100644
--- a/app/graphql/mutations/todos/restore_many.rb
+++ b/app/graphql/mutations/todos/restore_many.rb
@@ -8,7 +8,7 @@ module Mutations
MAX_UPDATE_AMOUNT = 50
argument :ids,
- [GraphQL::ID_TYPE],
+ [::Types::GlobalIDType[::Todo]],
required: true,
description: 'The global ids of the todos to restore (a maximum of 50 is supported at once)'
@@ -37,24 +37,18 @@ module Mutations
private
def gids_of(ids)
- ids.map { |id| ::URI::GID.build(app: GlobalID.app, model_name: Todo.name, model_id: id, params: nil).to_s }
+ ids.map { |id| Gitlab::GlobalId.as_global_id(id, model_name: Todo.name).to_s }
end
def model_ids_of(ids)
ids.map do |gid|
- parsed_gid = ::URI::GID.parse(gid)
- parsed_gid.model_id.to_i if accessible_todo?(parsed_gid)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ gid = ::Types::GlobalIDType[::Todo].coerce_isolated_input(gid)
+ gid.model_id.to_i
end.compact
end
- def accessible_todo?(gid)
- gid.app == GlobalID.app && todo?(gid)
- end
-
- def todo?(gid)
- GlobalID.parse(gid)&.model_class&.ancestors&.include?(Todo)
- end
-
def raise_too_many_todos_requested_error
raise Gitlab::Graphql::Errors::ArgumentError, 'Too many todos requested.'
end
diff --git a/app/graphql/queries/repository/path_last_commit.query.graphql b/app/graphql/queries/repository/path_last_commit.query.graphql
new file mode 100644
index 00000000000..d845f7c6224
--- /dev/null
+++ b/app/graphql/queries/repository/path_last_commit.query.graphql
@@ -0,0 +1,47 @@
+query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
+ project(fullPath: $projectPath) {
+ __typename
+ repository {
+ __typename
+ tree(path: $path, ref: $ref) {
+ __typename
+ lastCommit {
+ __typename
+ sha
+ title
+ titleHtml
+ descriptionHtml
+ message
+ webPath
+ authoredDate
+ authorName
+ authorGravatar
+ author {
+ __typename
+ name
+ avatarUrl
+ webPath
+ }
+ signatureHtml
+ pipelines(ref: $ref, first: 1) {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ detailedStatus {
+ __typename
+ detailsPath
+ icon
+ tooltip
+ text
+ group
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/graphql/resolvers/alert_management/alert_resolver.rb b/app/graphql/resolvers/alert_management/alert_resolver.rb
index 71a7615685a..dc9b1dbb5f4 100644
--- a/app/graphql/resolvers/alert_management/alert_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_resolver.rb
@@ -22,6 +22,10 @@ module Resolvers
description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
required: false
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of a user assigned to the issue'
+
type Types::AlertManagement::AlertType, null: true
def resolve_with_lookahead(**args)
diff --git a/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb b/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
index a45de21002f..96ea4610aff 100644
--- a/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
@@ -9,6 +9,10 @@ module Resolvers
description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
required: false
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of a user assigned to the issue'
+
def resolve(**args)
::Gitlab::AlertManagement::AlertStatusCounts.new(context[:current_user], object, args)
end
diff --git a/app/graphql/resolvers/assigned_merge_requests_resolver.rb b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
index fa08b142a7e..172a8e298ad 100644
--- a/app/graphql/resolvers/assigned_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class AssignedMergeRequestsResolver < UserMergeRequestsResolver
+ accept_author
+
def user_role
:assignee
end
diff --git a/app/graphql/resolvers/authored_merge_requests_resolver.rb b/app/graphql/resolvers/authored_merge_requests_resolver.rb
index e19bc9e8715..bc796f8685a 100644
--- a/app/graphql/resolvers/authored_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/authored_merge_requests_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class AuthoredMergeRequestsResolver < UserMergeRequestsResolver
+ accept_assignee
+
def user_role
:author
end
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 791c6eab42f..2b8854fb4d0 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -4,6 +4,9 @@ module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
+ include ::Gitlab::Graphql::GlobalIDCompatibility
+
+ argument_class ::Types::BaseArgument
def self.single
@single ||= Class.new(self) do
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb
index dba9f99edeb..3421e1024c0 100644
--- a/app/graphql/resolvers/board_list_issues_resolver.rb
+++ b/app/graphql/resolvers/board_list_issues_resolver.rb
@@ -14,7 +14,7 @@ module Resolvers
def resolve(**args)
filter_params = issue_filters(args[:filters]).merge(board_id: list.board.id, id: list.id)
- service = Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], filter_params)
+ service = ::Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], filter_params)
Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(service.execute)
end
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
index b1d43934f24..3384b37e2ce 100644
--- a/app/graphql/resolvers/board_lists_resolver.rb
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -2,6 +2,7 @@
module Resolvers
class BoardListsResolver < BaseResolver
+ include BoardIssueFilterable
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::BoardListType, null: true
@@ -10,12 +11,17 @@ module Resolvers
required: false,
description: 'Find a list by its global ID'
+ argument :issue_filters, Types::Boards::BoardIssueInputType,
+ required: false,
+ description: 'Filters applied when getting issue metadata in the board list'
+
alias_method :board, :object
- def resolve(lookahead: nil, id: nil)
+ def resolve(lookahead: nil, id: nil, issue_filters: {})
authorize!(board)
lists = board_lists(id)
+ context.scoped_set!(:issue_filters, issue_filters(issue_filters))
if load_preferences?(lookahead)
List.preload_preferences_for_user(lists, context[:current_user])
@@ -27,7 +33,7 @@ module Resolvers
private
def board_lists(id)
- service = Boards::Lists::ListService.new(
+ service = ::Boards::Lists::ListService.new(
board.resource_parent,
context[:current_user],
list_id: extract_list_id(id)
diff --git a/app/graphql/resolvers/board_resolver.rb b/app/graphql/resolvers/board_resolver.rb
new file mode 100644
index 00000000000..517f4e514c9
--- /dev/null
+++ b/app/graphql/resolvers/board_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class BoardResolver < BaseResolver.single
+ alias_method :parent, :synchronized_object
+
+ type Types::BoardType, null: true
+
+ argument :id, ::Types::GlobalIDType[::Board],
+ required: true,
+ description: 'The board\'s ID'
+
+ def resolve(id: nil)
+ return unless parent
+
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false).first
+ rescue ActiveRecord::RecordNotFound
+ nil
+ end
+
+ private
+
+ def extract_board_id(gid)
+ GitlabSchema.parse_gid(gid, expected_type: ::Board).model_id
+ end
+ end
+end
diff --git a/app/graphql/resolvers/boards_resolver.rb b/app/graphql/resolvers/boards_resolver.rb
index eceb5b38031..82efd92d33f 100644
--- a/app/graphql/resolvers/boards_resolver.rb
+++ b/app/graphql/resolvers/boards_resolver.rb
@@ -16,7 +16,7 @@ module Resolvers
return Board.none unless parent
- Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false)
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false)
rescue ActiveRecord::RecordNotFound
Board.none
end
diff --git a/app/graphql/resolvers/ci/runner_platforms_resolver.rb b/app/graphql/resolvers/ci/runner_platforms_resolver.rb
new file mode 100644
index 00000000000..9677c5139b4
--- /dev/null
+++ b/app/graphql/resolvers/ci/runner_platforms_resolver.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class RunnerPlatformsResolver < BaseResolver
+ type Types::Ci::RunnerPlatformType, null: false
+
+ def resolve(**args)
+ runner_instructions.map do |platform, data|
+ {
+ name: platform, human_readable_name: data[:human_readable_name],
+ architectures: parse_architectures(data[:download_locations])
+ }
+ end
+ end
+
+ private
+
+ def runner_instructions
+ Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS)
+ end
+
+ def parse_architectures(download_locations)
+ download_locations&.map do |architecture, download_location|
+ { name: architecture, download_location: download_location }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/group_issuable_resolver.rb b/app/graphql/resolvers/concerns/group_issuable_resolver.rb
new file mode 100644
index 00000000000..49a79683e9f
--- /dev/null
+++ b/app/graphql/resolvers/concerns/group_issuable_resolver.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module GroupIssuableResolver
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def include_subgroups(name_of_things)
+ argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ default_value: false,
+ description: "Include #{name_of_things} belonging to subgroups"
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
index 2b14d8275d1..fe6fa0bb262 100644
--- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
+++ b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
@@ -18,9 +18,15 @@ module IssueResolverArguments
argument :milestone_title, GraphQL::STRING_TYPE.to_list_type,
required: false,
description: 'Milestone applied to this issue'
+ argument :author_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the author of the issue'
argument :assignee_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of a user assigned to the issue'
+ argument :assignee_usernames, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Usernames of users 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'
diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb
index e7230287e13..61f23920ebb 100644
--- a/app/graphql/resolvers/concerns/looks_ahead.rb
+++ b/app/graphql/resolvers/concerns/looks_ahead.rb
@@ -3,8 +3,6 @@
module LooksAhead
extend ActiveSupport::Concern
- FEATURE_FLAG = :graphql_lookahead_support
-
included do
attr_accessor :lookahead
end
@@ -16,8 +14,6 @@ module LooksAhead
end
def apply_lookahead(query)
- return query unless Feature.enabled?(FEATURE_FLAG)
-
selection = node_selection
includes = preloads.each.flat_map do |name, requirements|
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index 0c01efd4f9a..ab83476ddea 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -12,7 +12,7 @@ module ResolvesMergeRequests
def resolve_with_lookahead(**args)
mr_finder = MergeRequestsFinder.new(current_user, args.compact)
- finder = Gitlab::Graphql::Loaders::IssuableLoader.new(project, mr_finder)
+ finder = Gitlab::Graphql::Loaders::IssuableLoader.new(mr_parent, mr_finder)
select_result(finder.batching_find_all { |query| apply_lookahead(query) })
end
@@ -29,6 +29,10 @@ module ResolvesMergeRequests
private
+ def mr_parent
+ project
+ end
+
def unconditional_includes
[:target_project]
end
@@ -40,7 +44,8 @@ module ResolvesMergeRequests
author: [:author],
merged_at: [:metrics],
commit_count: [:metrics],
- approved_by: [:approver_users],
+ diff_stats_summary: [:metrics],
+ approved_by: [:approved_by_users],
milestone: [:milestone],
head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }]
}
diff --git a/app/graphql/resolvers/concerns/time_frame_arguments.rb b/app/graphql/resolvers/concerns/time_frame_arguments.rb
index ef333dd05a5..94bfe6f7f9f 100644
--- a/app/graphql/resolvers/concerns/time_frame_arguments.rb
+++ b/app/graphql/resolvers/concerns/time_frame_arguments.rb
@@ -3,21 +3,33 @@
module TimeFrameArguments
extend ActiveSupport::Concern
+ OVERLAPPING_TIMEFRAME_DESC = 'List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present)'
+
included do
argument :start_date, Types::TimeType,
required: false,
- description: 'List items within a time frame where items.start_date is between startDate and endDate parameters (endDate parameter must be present)'
+ description: OVERLAPPING_TIMEFRAME_DESC,
+ deprecated: { reason: 'Use timeframe.start', milestone: '13.5' }
argument :end_date, Types::TimeType,
required: false,
- description: 'List items within a time frame where items.end_date is between startDate and endDate parameters (startDate parameter must be present)'
+ description: OVERLAPPING_TIMEFRAME_DESC,
+ deprecated: { reason: 'Use timeframe.end', milestone: '13.5' }
+
+ argument :timeframe, Types::TimeframeInputType,
+ required: false,
+ description: 'List items overlapping the given timeframe'
end
+ # TODO: remove when the start_date and end_date arguments are removed
def validate_timeframe_params!(args)
- return unless args[:start_date].present? || args[:end_date].present?
+ return unless %i[start_date end_date timeframe].any? { |k| args[k].present? }
+ return if args[:timeframe] && %i[start_date end_date].all? { |k| args[k].nil? }
error_message =
- if args[:start_date].nil? || args[:end_date].nil?
+ if args[:timeframe].present?
+ "startDate and endDate are deprecated in favor of timeframe. Please use only timeframe."
+ elsif args[:start_date].nil? || args[:end_date].nil?
"Both startDate and endDate must be present."
elsif args[:start_date] > args[:end_date]
"startDate is after endDate"
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index ac51011eea8..1fa6c78e730 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -2,9 +2,8 @@
module Resolvers
class GroupIssuesResolver < IssuesResolver
- argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
- required: false,
- default_value: false,
- description: 'Include issues belonging to subgroups.'
+ include GroupIssuableResolver
+
+ include_subgroups 'issues'
end
end
diff --git a/app/graphql/resolvers/group_merge_requests_resolver.rb b/app/graphql/resolvers/group_merge_requests_resolver.rb
new file mode 100644
index 00000000000..5ee72e3f781
--- /dev/null
+++ b/app/graphql/resolvers/group_merge_requests_resolver.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class GroupMergeRequestsResolver < MergeRequestsResolver
+ include GroupIssuableResolver
+
+ alias_method :group, :synchronized_object
+
+ include_subgroups 'merge requests'
+ accept_assignee
+ accept_author
+
+ def project
+ nil
+ end
+
+ def mr_parent
+ group
+ end
+
+ def no_results_possible?(args)
+ group.nil? || some_argument_is_empty?(args)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb
index 677f84e5795..cb4a76243ae 100644
--- a/app/graphql/resolvers/merge_requests_resolver.rb
+++ b/app/graphql/resolvers/merge_requests_resolver.rb
@@ -6,6 +6,18 @@ module Resolvers
alias_method :project, :synchronized_object
+ def self.accept_assignee
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the assignee'
+ end
+
+ def self.accept_author
+ argument :author_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the author'
+ end
+
argument :iids, [GraphQL::STRING_TYPE],
required: false,
description: 'Array of IIDs of merge requests, for example `[1, 2]`'
diff --git a/app/graphql/resolvers/milestones_resolver.rb b/app/graphql/resolvers/milestones_resolver.rb
index 5f80506c01b..84712b674db 100644
--- a/app/graphql/resolvers/milestones_resolver.rb
+++ b/app/graphql/resolvers/milestones_resolver.rb
@@ -13,6 +13,18 @@ module Resolvers
required: false,
description: 'Filter milestones by state'
+ argument :title, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The title of the milestone'
+
+ argument :search_title, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'A search string for the title'
+
+ argument :containing_date, Types::TimeType,
+ required: false,
+ description: 'A date that the milestone contains'
+
type Types::MilestoneType, null: true
def resolve(**args)
@@ -29,9 +41,18 @@ module Resolvers
{
ids: parse_gids(args[:ids]),
state: args[:state] || 'all',
- start_date: args[:start_date],
- end_date: args[:end_date]
- }.merge(parent_id_parameters(args))
+ title: args[:title],
+ search_title: args[:search_title],
+ containing_date: args[:containing_date]
+ }.merge!(timeframe_parameters(args)).merge!(parent_id_parameters(args))
+ end
+
+ def timeframe_parameters(args)
+ if args[:timeframe]
+ args[:timeframe].transform_keys { |k| :"#{k}_date" }
+ else
+ args.slice(:start_date, :end_date)
+ end
end
def parent
diff --git a/app/graphql/resolvers/project_merge_requests_resolver.rb b/app/graphql/resolvers/project_merge_requests_resolver.rb
index 0526ccd315f..ba13cb6e52c 100644
--- a/app/graphql/resolvers/project_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/project_merge_requests_resolver.rb
@@ -2,11 +2,7 @@
module Resolvers
class ProjectMergeRequestsResolver < MergeRequestsResolver
- argument :assignee_username, GraphQL::STRING_TYPE,
- required: false,
- description: 'Username of the assignee'
- argument :author_username, GraphQL::STRING_TYPE,
- required: false,
- description: 'Username of the author'
+ accept_assignee
+ accept_author
end
end
diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb
index ed382ac82d0..d017f973e17 100644
--- a/app/graphql/resolvers/projects/jira_projects_resolver.rb
+++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb
@@ -22,7 +22,7 @@ module Resolvers
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 })
+ **args.merge({ max_page_size: projects_array.size })
)
else
raise Gitlab::Graphql::Errors::BaseError, response.message
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index 3bbadf87a71..69438229a50 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -13,8 +13,16 @@ module Resolvers
description: 'Search query for project name, path, or description'
argument :ids, [GraphQL::ID_TYPE],
- required: false,
- description: 'Filter projects by IDs'
+ required: false,
+ description: 'Filter projects by IDs'
+
+ argument :search_namespaces, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Include namespace in project search'
+
+ argument :sort, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Sort order of results'
def resolve(**args)
ProjectsFinder
@@ -28,7 +36,9 @@ module Resolvers
{
without_deleted: true,
non_public: params[:membership],
- search: params[:search]
+ search: params[:search],
+ search_namespaces: params[:search_namespaces],
+ sort: params[:sort]
}.compact
end
diff --git a/app/graphql/resolvers/snippets/blobs_resolver.rb b/app/graphql/resolvers/snippets/blobs_resolver.rb
new file mode 100644
index 00000000000..dc28358cab6
--- /dev/null
+++ b/app/graphql/resolvers/snippets/blobs_resolver.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Snippets
+ class BlobsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ alias_method :snippet, :object
+
+ argument :paths, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Paths of the blobs'
+
+ def resolve(**args)
+ authorize!(snippet)
+
+ return [snippet.blob] if snippet.empty_repo?
+
+ paths = Array(args.fetch(:paths, []))
+
+ if paths.empty?
+ snippet.blobs
+ else
+ snippet.repository.blobs_at(transformed_blob_paths(paths))
+ end
+ end
+
+ def authorized_resource?(snippet)
+ Ability.allowed?(context[:current_user], :read_snippet, snippet)
+ end
+
+ private
+
+ def transformed_blob_paths(paths)
+ ref = snippet.default_branch
+ paths.map { |path| [ref, path] }
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/terraform/states_resolver.rb b/app/graphql/resolvers/terraform/states_resolver.rb
new file mode 100644
index 00000000000..38b26a948b1
--- /dev/null
+++ b/app/graphql/resolvers/terraform/states_resolver.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Terraform
+ class StatesResolver < BaseResolver
+ type Types::Terraform::StateType, null: true
+
+ alias_method :project, :object
+
+ def resolve(**args)
+ return ::Terraform::State.none unless can_read_terraform_states?
+
+ project.terraform_states.ordered_by_name
+ end
+
+ private
+
+ def can_read_terraform_states?
+ current_user.can?(:read_terraform_state, project)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb b/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb
index 13c67442c2e..c6ca5963588 100644
--- a/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb
+++ b/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb
@@ -8,12 +8,16 @@ module Types
graphql_name 'MeasurementIdentifier'
description 'Possible identifier types for a measurement'
- value 'PROJECTS', 'Project count', value: :projects
- value 'USERS', 'User count', value: :users
- value 'ISSUES', 'Issue count', value: :issues
- value 'MERGE_REQUESTS', 'Merge request count', value: :merge_requests
- value 'GROUPS', 'Group count', value: :groups
- value 'PIPELINES', 'Pipeline count', value: :pipelines
+ value 'PROJECTS', 'Project count', value: 'projects'
+ value 'USERS', 'User count', value: 'users'
+ value 'ISSUES', 'Issue count', value: 'issues'
+ value 'MERGE_REQUESTS', 'Merge request count', value: 'merge_requests'
+ value 'GROUPS', 'Group count', value: 'groups'
+ value 'PIPELINES', 'Pipeline count', value: 'pipelines'
+ value 'PIPELINES_SUCCEEDED', 'Pipeline count with success status', value: 'pipelines_succeeded'
+ value 'PIPELINES_FAILED', 'Pipeline count with failed status', value: 'pipelines_failed'
+ value 'PIPELINES_CANCELED', 'Pipeline count with canceled status', value: 'pipelines_canceled'
+ value 'PIPELINES_SKIPPED', 'Pipeline count with skipped status', value: 'pipelines_skipped'
end
end
end
diff --git a/app/graphql/types/alert_management/alert_status_counts_type.rb b/app/graphql/types/alert_management/alert_status_counts_type.rb
index f80b289eabc..a84be705445 100644
--- a/app/graphql/types/alert_management/alert_status_counts_type.rb
+++ b/app/graphql/types/alert_management/alert_status_counts_type.rb
@@ -9,11 +9,11 @@ module Types
authorize :read_alert_management_alert
- ::Gitlab::AlertManagement::AlertStatusCounts::STATUSES.each_key do |status|
+ ::AlertManagement::Alert.status_names.each do |status|
field status,
GraphQL::INT_TYPE,
null: true,
- description: "Number of alerts with status #{status.upcase} for the project"
+ description: "Number of alerts with status #{status.to_s.upcase} for the project"
end
field :open,
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb
index 2da97030b88..623762de208 100644
--- a/app/graphql/types/alert_management/alert_type.rb
+++ b/app/graphql/types/alert_management/alert_type.rb
@@ -40,7 +40,8 @@ module Types
field :status,
AlertManagement::StatusEnum,
null: true,
- description: 'Status of the alert'
+ description: 'Status of the alert',
+ method: :status_name
field :service,
GraphQL::STRING_TYPE,
@@ -67,6 +68,11 @@ module Types
null: true,
description: 'Timestamp the alert ended'
+ field :environment,
+ Types::EnvironmentType,
+ null: true,
+ description: 'Environment for the alert'
+
field :event_count,
GraphQL::INT_TYPE,
null: true,
diff --git a/app/graphql/types/alert_management/status_enum.rb b/app/graphql/types/alert_management/status_enum.rb
index 4ff6c4a9505..9d2c7316254 100644
--- a/app/graphql/types/alert_management/status_enum.rb
+++ b/app/graphql/types/alert_management/status_enum.rb
@@ -6,8 +6,8 @@ module Types
graphql_name 'AlertManagementStatus'
description 'Alert status values'
- ::AlertManagement::Alert::STATUSES.each do |name, value|
- value name.upcase, value: value, description: "#{name.to_s.titleize} status"
+ ::AlertManagement::Alert.status_names.each do |status|
+ value status.to_s.upcase, value: status, description: "#{status.to_s.titleize} status"
end
end
end
diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb
new file mode 100644
index 00000000000..11774d0b59d
--- /dev/null
+++ b/app/graphql/types/base_argument.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ class BaseArgument < GraphQL::Schema::Argument
+ include GitlabStyleDeprecations
+
+ def initialize(*args, **kwargs, &block)
+ kwargs = gitlab_deprecation(kwargs)
+ kwargs.delete(:deprecation_reason)
+
+ super(*args, **kwargs, &block)
+ end
+ end
+end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 1e72a4cddf5..5c8aabfe163 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -5,6 +5,8 @@ module Types
prepend Gitlab::Graphql::Authorize
include GitlabStyleDeprecations
+ argument_class ::Types::BaseArgument
+
DEFAULT_COMPLEXITY = 1
def initialize(*args, **kwargs, &block)
diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb
index 24faf1fe8bc..6ee76b0d1f1 100644
--- a/app/graphql/types/board_list_type.rb
+++ b/app/graphql/types/board_list_type.rb
@@ -32,17 +32,14 @@ module Types
metadata[:size]
end
- def total_weight
- metadata[:total_weight]
- end
-
def metadata
strong_memoize(:metadata) do
list = self.object
user = context[:current_user]
+ params = (context[:issue_filters] || {}).merge(board_id: list.board_id, id: list.id)
::Boards::Issues::ListService
- .new(list.board.resource_parent, user, board_id: list.board_id, id: list.id)
+ .new(list.board.resource_parent, user, params)
.metadata
end
end
diff --git a/app/graphql/types/ci/detailed_status_type.rb b/app/graphql/types/ci/detailed_status_type.rb
index 90b5283fc9a..f4a50115ee6 100644
--- a/app/graphql/types/ci/detailed_status_type.rb
+++ b/app/graphql/types/ci/detailed_status_type.rb
@@ -6,24 +6,39 @@ module Types
class DetailedStatusType < BaseObject
graphql_name 'DetailedStatus'
- field :group, GraphQL::STRING_TYPE, null: false,
- description: 'Group of the pipeline status'
- field :icon, GraphQL::STRING_TYPE, null: false,
- description: 'Icon of the pipeline status'
- field :favicon, GraphQL::STRING_TYPE, null: false,
- description: 'Favicon of the pipeline status'
- field :details_path, GraphQL::STRING_TYPE, null: false,
- description: 'Path of the details for the pipeline status'
- field :has_details, GraphQL::BOOLEAN_TYPE, null: false,
- description: 'Indicates if the pipeline status has further details',
+ field :group, GraphQL::STRING_TYPE, null: true,
+ description: 'Group of the status'
+ field :icon, GraphQL::STRING_TYPE, null: true,
+ description: 'Icon of the status'
+ field :favicon, GraphQL::STRING_TYPE, null: true,
+ description: 'Favicon of the status'
+ field :details_path, GraphQL::STRING_TYPE, null: true,
+ description: 'Path of the details for the status'
+ field :has_details, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Indicates if the status has further details',
method: :has_details?
- field :label, GraphQL::STRING_TYPE, null: false,
- description: 'Label of the pipeline status'
- field :text, GraphQL::STRING_TYPE, null: false,
- description: 'Text of the pipeline status'
- field :tooltip, GraphQL::STRING_TYPE, null: false,
- description: 'Tooltip associated with the pipeline status',
+ field :label, GraphQL::STRING_TYPE, null: true,
+ description: 'Label of the status'
+ field :text, GraphQL::STRING_TYPE, null: true,
+ description: 'Text of the status'
+ field :tooltip, GraphQL::STRING_TYPE, null: true,
+ description: 'Tooltip associated with the status',
method: :status_tooltip
+ field :action, Types::Ci::StatusActionType, null: true,
+ description: 'Action information for the status. This includes method, button title, icon, path, and title',
+ resolve: -> (obj, _args, _ctx) {
+ if obj.has_action?
+ {
+ button_title: obj.action_button_title,
+ icon: obj.icon,
+ method: obj.action_method,
+ path: obj.action_path,
+ title: obj.action_title
+ }
+ else
+ nil
+ 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
index 04c0eb93068..d930ae311b7 100644
--- a/app/graphql/types/ci/group_type.rb
+++ b/app/graphql/types/ci/group_type.rb
@@ -12,6 +12,9 @@ module Types
description: 'Size of the group'
field :jobs, Ci::JobType.connection_type, null: true,
description: 'Jobs in group'
+ field :detailed_status, Types::Ci::DetailedStatusType, null: true,
+ description: 'Detailed status of the group',
+ resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
end
end
end
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 4c18f3ffd52..0ee1ad47b62 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -10,6 +10,11 @@ module Types
description: 'Name of the job'
field :needs, JobType.connection_type, null: true,
description: 'Builds that must complete before the jobs run'
+ field :detailed_status, Types::Ci::DetailedStatusType, null: true,
+ description: 'Detailed status of the job',
+ resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ field :scheduled_at, Types::TimeType, null: true,
+ description: 'Schedule for the build'
end
end
end
diff --git a/app/graphql/types/ci/runner_architecture_type.rb b/app/graphql/types/ci/runner_architecture_type.rb
new file mode 100644
index 00000000000..526348abd9d
--- /dev/null
+++ b/app/graphql/types/ci/runner_architecture_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class RunnerArchitectureType < BaseObject
+ graphql_name 'RunnerArchitecture'
+
+ field :name, GraphQL::STRING_TYPE, null: false,
+ description: 'Name of the runner platform architecture'
+ field :download_location, GraphQL::STRING_TYPE, null: false,
+ description: 'Download location for the runner for the platform architecture'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/runner_platform_type.rb b/app/graphql/types/ci/runner_platform_type.rb
new file mode 100644
index 00000000000..64719bc4908
--- /dev/null
+++ b/app/graphql/types/ci/runner_platform_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class RunnerPlatformType < BaseObject
+ graphql_name 'RunnerPlatform'
+
+ field :name, GraphQL::STRING_TYPE, null: false,
+ description: 'Name slug of the runner platform'
+ field :human_readable_name, GraphQL::STRING_TYPE, null: false,
+ description: 'Human readable name of the runner platform'
+ field :architectures, Types::Ci::RunnerArchitectureType.connection_type, null: true,
+ description: 'Runner architectures supported for the platform'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb
index 278c4d4d748..fc2c72d0d06 100644
--- a/app/graphql/types/ci/stage_type.rb
+++ b/app/graphql/types/ci/stage_type.rb
@@ -10,6 +10,9 @@ module Types
description: 'Name of the stage'
field :groups, Ci::GroupType.connection_type, null: true,
description: 'Group of jobs for the stage'
+ field :detailed_status, Types::Ci::DetailedStatusType, null: true,
+ description: 'Detailed status of the stage',
+ resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
end
end
end
diff --git a/app/graphql/types/ci/status_action_type.rb b/app/graphql/types/ci/status_action_type.rb
new file mode 100644
index 00000000000..08cbb6d3b59
--- /dev/null
+++ b/app/graphql/types/ci/status_action_type.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class StatusActionType < BaseObject
+ graphql_name 'StatusAction'
+
+ field :button_title, GraphQL::STRING_TYPE, null: true,
+ description: 'Title for the button, for example: Retry this job'
+ field :icon, GraphQL::STRING_TYPE, null: true,
+ description: 'Icon used in the action button'
+ field :method, GraphQL::STRING_TYPE, null: true,
+ description: 'Method for the action, for example: :post',
+ resolver_method: :action_method
+ field :path, GraphQL::STRING_TYPE, null: true,
+ description: 'Path for the action'
+ field :title, GraphQL::STRING_TYPE, null: true,
+ description: 'Title for the action, for example: Retry'
+
+ def action_method
+ object[:method]
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/date_type.rb b/app/graphql/types/date_type.rb
new file mode 100644
index 00000000000..7129b75b8bb
--- /dev/null
+++ b/app/graphql/types/date_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ class DateType < BaseScalar
+ graphql_name 'Date'
+ description 'Date represented in ISO 8601'
+
+ def self.coerce_input(value, ctx)
+ return if value.nil?
+
+ Date.iso8601(value)
+ rescue ArgumentError, TypeError => e
+ raise GraphQL::CoercionError, e.message
+ end
+
+ def self.coerce_result(value, ctx)
+ return if value.nil?
+
+ value.to_date.iso8601
+ end
+ end
+end
diff --git a/app/graphql/types/design_management/design_collection_copy_state_enum.rb b/app/graphql/types/design_management/design_collection_copy_state_enum.rb
new file mode 100644
index 00000000000..7e7303c50ef
--- /dev/null
+++ b/app/graphql/types/design_management/design_collection_copy_state_enum.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Types
+ module DesignManagement
+ class DesignCollectionCopyStateEnum < BaseEnum
+ graphql_name 'DesignCollectionCopyState'
+ description 'Copy state of a DesignCollection'
+
+ DESCRIPTION_VARIANTS = {
+ in_progress: 'is being copied',
+ error: 'encountered an error during a copy',
+ ready: 'has no copy in progress'
+ }.freeze
+
+ def self.description_variant(copy_state)
+ DESCRIPTION_VARIANTS[copy_state.to_sym] ||
+ (raise ArgumentError, "Unknown copy state: #{copy_state}")
+ end
+
+ ::DesignManagement::DesignCollection.state_machines[:copy_state].states.keys.each do |copy_state|
+ value copy_state.upcase,
+ value: copy_state.to_s,
+ description: "The DesignCollection #{description_variant(copy_state)}"
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/design_management/design_collection_type.rb b/app/graphql/types/design_management/design_collection_type.rb
index 904fb270e11..9af1f4db425 100644
--- a/app/graphql/types/design_management/design_collection_type.rb
+++ b/app/graphql/types/design_management/design_collection_type.rb
@@ -39,6 +39,10 @@ module Types
null: true,
resolver: ::Resolvers::DesignManagement::DesignResolver,
description: 'Find a specific design'
+
+ field :copy_state, ::Types::DesignManagement::DesignCollectionCopyStateEnum,
+ null: true,
+ description: 'Copy state of the design collection'
end
end
end
diff --git a/app/graphql/types/design_management/design_type.rb b/app/graphql/types/design_management/design_type.rb
index 4e11a7aaf09..bab22015dc4 100644
--- a/app/graphql/types/design_management/design_type.rb
+++ b/app/graphql/types/design_management/design_type.rb
@@ -30,7 +30,7 @@ module Types
# most recent `Version` for an issue
Gitlab::SafeRequestStore.fetch([request_cache_base_key, 'stateful_version', object.issue_id, version_gid]) do
if version_gid
- GitlabSchema.object_from_id(version_gid)&.sync
+ GitlabSchema.object_from_id(version_gid, expected_type: ::DesignManagement::Version)&.sync
else
object.issue.design_versions.most_recent
end
diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb
index 239b26f9c38..e4631a4a903 100644
--- a/app/graphql/types/environment_type.rb
+++ b/app/graphql/types/environment_type.rb
@@ -5,6 +5,8 @@ module Types
graphql_name 'Environment'
description 'Describes where code is deployed for a project'
+ present_using ::EnvironmentPresenter
+
authorize :read_environment
field :name, GraphQL::STRING_TYPE, null: false,
@@ -16,6 +18,10 @@ module Types
field :state, GraphQL::STRING_TYPE, null: false,
description: 'State of the environment, for example: available/stopped'
+ field :path, GraphQL::STRING_TYPE, null: true,
+ description: 'The path to the environment. Will always return null ' \
+ 'if `expose_environment_path_in_alert_details` feature flag is disabled'
+
field :metrics_dashboard, Types::Metrics::DashboardType, null: true,
description: 'Metrics dashboard schema for the environment',
resolver: Resolvers::Metrics::DashboardResolver
@@ -23,6 +29,6 @@ module Types
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.'
+ 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/global_id_type.rb b/app/graphql/types/global_id_type.rb
index a3964ba83e1..9ae9ba32c13 100644
--- a/app/graphql/types/global_id_type.rb
+++ b/app/graphql/types/global_id_type.rb
@@ -1,5 +1,21 @@
# frozen_string_literal: true
+module GraphQLExtensions
+ module ScalarExtensions
+ # Allow ID to unify with GlobalID Types
+ def ==(other)
+ if name == 'ID' && other.is_a?(self.class) &&
+ other.type_class.ancestors.include?(::Types::GlobalIDType)
+ return true
+ end
+
+ super
+ end
+ end
+end
+
+::GraphQL::ScalarType.prepend(GraphQLExtensions::ScalarExtensions)
+
module Types
class GlobalIDType < BaseScalar
graphql_name 'GlobalID'
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 60b2e3c7b6e..199cc0308c5 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -46,9 +46,15 @@ module Types
field :issues,
Types::IssueType.connection_type,
null: true,
- description: 'Issues of the group',
+ description: 'Issues for projects in this group',
resolver: Resolvers::GroupIssuesResolver
+ field :merge_requests,
+ Types::MergeRequestType.connection_type,
+ null: true,
+ description: 'Merge requests for projects in this group',
+ resolver: Resolvers::GroupMergeRequestsResolver
+
field :milestones, Types::MilestoneType.connection_type, null: true,
description: 'Milestones of the group',
resolver: Resolvers::GroupMilestonesResolver
@@ -64,7 +70,7 @@ module Types
Types::BoardType,
null: true,
description: 'A single board of the group',
- resolver: Resolvers::BoardsResolver.single
+ resolver: Resolvers::BoardResolver
field :label,
Types::LabelType,
diff --git a/app/graphql/types/issue_sort_enum.rb b/app/graphql/types/issue_sort_enum.rb
index e458d6e02c5..08762264b1b 100644
--- a/app/graphql/types/issue_sort_enum.rb
+++ b/app/graphql/types/issue_sort_enum.rb
@@ -8,6 +8,8 @@ module Types
value 'DUE_DATE_ASC', 'Due date by ascending order', value: :due_date_asc
value 'DUE_DATE_DESC', 'Due date by descending order', value: :due_date_desc
value 'RELATIVE_POSITION_ASC', 'Relative position by ascending order', value: :relative_position_asc
+ value 'SEVERITY_ASC', 'Severity from less critical to more critical', value: :severity_asc
+ value 'SEVERITY_DESC', 'Severity from more critical to less critical', value: :severity_desc
end
end
diff --git a/app/graphql/types/issue_state_event_enum.rb b/app/graphql/types/issue_state_event_enum.rb
new file mode 100644
index 00000000000..6a9d840831d
--- /dev/null
+++ b/app/graphql/types/issue_state_event_enum.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ class IssueStateEventEnum < BaseEnum
+ graphql_name 'IssueStateEvent'
+ description 'Values for issue state events'
+
+ value 'REOPEN', 'Reopens the issue', value: 'reopen'
+ value 'CLOSE', 'Closes the issue', value: 'close'
+ end
+end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index d6253f74ce5..487508f448f 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -36,8 +36,7 @@ module Types
end
field :author, Types::UserType, null: false,
- description: 'User that created the issue',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find }
+ description: 'User that created the issue'
field :assignees, Types::UserType.connection_type, null: true,
description: 'Assignees of the issue'
@@ -45,16 +44,14 @@ module Types
field :labels, Types::LabelType.connection_type, null: true,
description: 'Labels of the issue'
field :milestone, Types::MilestoneType, null: true,
- description: 'Milestone of the issue',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find }
+ description: 'Milestone of the issue'
field :due_date, Types::TimeType, null: true,
description: 'Due date of the issue'
field :confidential, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates the issue is confidential'
field :discussion_locked, GraphQL::BOOLEAN_TYPE, null: false,
- description: 'Indicates discussion is locked on the issue',
- resolve: -> (obj, _args, _ctx) { !!obj.discussion_locked }
+ description: 'Indicates discussion is locked on the issue'
field :upvotes, GraphQL::INT_TYPE, null: false,
description: 'Number of upvotes the issue has received'
@@ -108,6 +105,18 @@ module Types
field :severity, Types::IssuableSeverityEnum, null: true,
description: 'Severity level of the incident'
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
+
+ def milestone
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, object.milestone_id).find
+ end
+
+ def discussion_locked
+ !!object.discussion_locked
+ end
end
end
diff --git a/app/graphql/types/label_type.rb b/app/graphql/types/label_type.rb
index 738a00ad616..28dee2a9db5 100644
--- a/app/graphql/types/label_type.rb
+++ b/app/graphql/types/label_type.rb
@@ -4,6 +4,8 @@ module Types
class LabelType < BaseObject
graphql_name 'Label'
+ connection_type_class(Types::CountableConnectionType)
+
authorize :read_label
field :id, GraphQL::ID_TYPE, null: false,
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 56c88491684..372aeac055b 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -152,6 +152,25 @@ module Types
field :auto_merge_enabled, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates if auto merge is enabled for the merge request'
+ field :approved_by, Types::UserType.connection_type, null: true,
+ description: 'Users who approved the merge request'
+
+ def approved_by
+ object.approved_by_users
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def user_notes_count
+ BatchLoader::GraphQL.for(object.id).batch(key: :merge_request_user_notes_count) do |ids, loader, args|
+ counts = Note.where(noteable_type: 'MergeRequest', noteable_id: ids).user.group(:noteable_id).count
+
+ ids.each do |id|
+ loader.call(id, counts[id] || 0)
+ end
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def diff_stats(path: nil)
stats = Array.wrap(object.diff_stats&.to_a)
@@ -163,21 +182,12 @@ module Types
end
def diff_stats_summary
- nil_stats = { additions: 0, deletions: 0, file_count: 0 }
- return nil_stats unless object.diff_stats.present?
-
- object.diff_stats.each_with_object(nil_stats) do |status, hash|
- hash.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y }
- end
+ BatchLoaders::MergeRequestDiffSummaryBatchLoader.load_for(object)
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 b2732d83aac..3f48e7b4a16 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -14,13 +14,16 @@ module Types
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
+ mount_mutation Mutations::Boards::Create
mount_mutation Mutations::Boards::Destroy
mount_mutation Mutations::Boards::Issues::IssueMoveList
mount_mutation Mutations::Boards::Lists::Create
mount_mutation Mutations::Boards::Lists::Update
+ mount_mutation Mutations::Boards::Lists::Destroy
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::Create
mount_mutation Mutations::Issues::SetAssignees
mount_mutation Mutations::Issues::SetConfidential
mount_mutation Mutations::Issues::SetLocked
@@ -28,6 +31,7 @@ module Types
mount_mutation Mutations::Issues::SetSeverity
mount_mutation Mutations::Issues::SetSubscription
mount_mutation Mutations::Issues::Update
+ mount_mutation Mutations::Issues::Move
mount_mutation Mutations::MergeRequests::Create
mount_mutation Mutations::MergeRequests::Update
mount_mutation Mutations::MergeRequests::SetLabels
@@ -71,4 +75,5 @@ module Types
end
::Types::MutationType.prepend(::Types::DeprecatedMutations)
+::Types::MutationType.prepend_if_ee('EE::Types::DeprecatedMutations')
::Types::MutationType.prepend_if_ee('::EE::Types::MutationType')
diff --git a/app/graphql/types/notes/noteable_type.rb b/app/graphql/types/notes/noteable_type.rb
index 3a16d54f9cd..602634d9292 100644
--- a/app/graphql/types/notes/noteable_type.rb
+++ b/app/graphql/types/notes/noteable_type.rb
@@ -8,24 +8,24 @@ module Types
field :notes, Types::Notes::NoteType.connection_type, null: false, description: "All notes on this noteable"
field :discussions, Types::Notes::DiscussionType.connection_type, null: false, description: "All discussions on this noteable"
- definition_methods do
- def resolve_type(object, context)
- case object
- when Issue
- Types::IssueType
- when MergeRequest
- Types::MergeRequestType
- when Snippet
- Types::SnippetType
- when ::DesignManagement::Design
- Types::DesignManagement::DesignType
- when ::AlertManagement::Alert
- Types::AlertManagement::AlertType
- else
- raise "Unknown GraphQL type for #{object}"
- end
+ def self.resolve_type(object, context)
+ case object
+ when Issue
+ Types::IssueType
+ when MergeRequest
+ Types::MergeRequestType
+ when Snippet
+ Types::SnippetType
+ when ::DesignManagement::Design
+ Types::DesignManagement::DesignType
+ when ::AlertManagement::Alert
+ Types::AlertManagement::AlertType
+ else
+ raise "Unknown GraphQL type for #{object}"
end
end
end
end
end
+
+Types::Notes::NoteableType.prepend_if_ee('::EE::Types::Notes::NoteableType')
diff --git a/app/graphql/types/package_type_enum.rb b/app/graphql/types/package_type_enum.rb
index bc03b8f5f8b..6f50c166da3 100644
--- a/app/graphql/types/package_type_enum.rb
+++ b/app/graphql/types/package_type_enum.rb
@@ -2,8 +2,14 @@
module Types
class PackageTypeEnum < BaseEnum
+ PACKAGE_TYPE_NAMES = {
+ pypi: 'PyPI',
+ npm: 'NPM'
+ }.freeze
+
::Packages::Package.package_types.keys.each do |package_type|
- value package_type.to_s.upcase, "Packages from the #{package_type} package manager", value: package_type.to_s
+ type_name = PACKAGE_TYPE_NAMES.fetch(package_type.to_sym, package_type.capitalize)
+ value package_type.to_s.upcase, "Packages from the #{type_name} package manager", value: package_type.to_s
end
end
end
diff --git a/app/graphql/types/project_member_type.rb b/app/graphql/types/project_member_type.rb
index f08781238d0..01731531ae2 100644
--- a/app/graphql/types/project_member_type.rb
+++ b/app/graphql/types/project_member_type.rb
@@ -12,7 +12,10 @@ module Types
authorize :read_project
field :project, Types::ProjectType, null: true,
- description: 'Project that User is a member of',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, obj.source_id).find }
+ description: 'Project that User is a member of'
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.source_id).find
+ end
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 0fd54af1538..c7fc193abe8 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -234,7 +234,7 @@ module Types
Types::BoardType,
null: true,
description: 'A single board of the project',
- resolver: Resolvers::BoardsResolver.single
+ resolver: Resolvers::BoardResolver
field :jira_imports,
Types::JiraImportType.connection_type,
@@ -294,6 +294,12 @@ module Types
description: 'Title of the label'
end
+ field :terraform_states,
+ Types::Terraform::StateType.connection_type,
+ null: true,
+ description: 'Terraform states associated with the project',
+ resolver: Resolvers::Terraform::StatesResolver
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args|
LabelsFinder
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 447ac63a294..bd4b53bdaa7 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -49,8 +49,7 @@ module Types
field :milestone, ::Types::MilestoneType,
null: true,
- description: 'Find a milestone',
- resolve: -> (_obj, args, _ctx) { GitlabSchema.find_by_gid(args[:id]) } do
+ description: 'Find a milestone' do
argument :id, ::Types::GlobalIDType[Milestone],
required: true,
description: 'Find a milestone by its ID'
@@ -81,12 +80,26 @@ module Types
description: 'Get statistics on the instance',
resolver: Resolvers::Admin::Analytics::InstanceStatistics::MeasurementsResolver
+ field :runner_platforms, Types::Ci::RunnerPlatformType.connection_type,
+ null: true, description: 'Supported runner platforms',
+ resolver: Resolvers::Ci::RunnerPlatformsResolver
+
def design_management
DesignManagementObject.new(nil)
end
def issue(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Issue)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Issue].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+
+ def milestone(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[Milestone].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
end
end
end
diff --git a/app/graphql/types/range_input_type.rb b/app/graphql/types/range_input_type.rb
new file mode 100644
index 00000000000..766e523a99e
--- /dev/null
+++ b/app/graphql/types/range_input_type.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class RangeInputType < BaseInputObject
+ def self.[](type, closed = true)
+ @subtypes ||= {}
+
+ @subtypes[[type, closed]] ||= Class.new(self) do
+ argument :start, type,
+ required: closed,
+ description: 'The start of the range'
+
+ argument :end, type,
+ required: closed,
+ description: 'The end of the range'
+ end
+ end
+
+ def prepare
+ if self[:end] && self[:start] && self[:end] < self[:start]
+ raise ::Gitlab::Graphql::Errors::ArgumentError, 'start must be before end'
+ end
+
+ to_h
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb
index 3acc1d9ca44..224e8c7ee03 100644
--- a/app/graphql/types/root_storage_statistics_type.rb
+++ b/app/graphql/types/root_storage_statistics_type.rb
@@ -13,5 +13,6 @@ module Types
field :packages_size, GraphQL::FLOAT_TYPE, null: false, description: 'The packages size in bytes'
field :wiki_size, GraphQL::FLOAT_TYPE, null: false, description: 'The wiki size in bytes'
field :snippets_size, GraphQL::FLOAT_TYPE, null: false, description: 'The snippets size in bytes'
+ field :pipeline_artifacts_size, GraphQL::FLOAT_TYPE, null: false, description: 'The CI pipeline artifacts size in bytes'
end
end
diff --git a/app/graphql/types/snippet_type.rb b/app/graphql/types/snippet_type.rb
index db98e62c10a..495c25c1776 100644
--- a/app/graphql/types/snippet_type.rb
+++ b/app/graphql/types/snippet_type.rb
@@ -24,16 +24,14 @@ module Types
field :project, Types::ProjectType,
description: 'The project the snippet is associated with',
null: true,
- authorize: :read_project,
- resolve: -> (snippet, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, snippet.project_id).find }
+ authorize: :read_project
# Author can be nil in some scenarios. For example,
# when the admin setting restricted visibility
# level is set to public
field :author, Types::UserType,
description: 'The owner of the snippet',
- null: true,
- resolve: -> (snippet, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, snippet.author_id).find }
+ null: true
field :file_name, GraphQL::STRING_TYPE,
description: 'File Name of the snippet',
@@ -69,10 +67,11 @@ module Types
null: false,
deprecated: { reason: 'Use `blobs`', milestone: '13.3' }
- field :blobs, type: [Types::Snippets::BlobType],
+ field :blobs, type: Types::Snippets::BlobType.connection_type,
description: 'Snippet blobs',
calls_gitaly: true,
- null: false
+ null: true,
+ resolver: Resolvers::Snippets::BlobsResolver
field :ssh_url_to_repo, type: GraphQL::STRING_TYPE,
description: 'SSH URL to the snippet repository',
@@ -85,5 +84,13 @@ module Types
null: true
markdown_field :description_html, null: true, method: :description
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
end
end
diff --git a/app/graphql/types/sort_enum.rb b/app/graphql/types/sort_enum.rb
index 3245cb33e0d..d0a6eecb672 100644
--- a/app/graphql/types/sort_enum.rb
+++ b/app/graphql/types/sort_enum.rb
@@ -5,9 +5,16 @@ module Types
graphql_name 'Sort'
description 'Common sort values'
- value 'updated_desc', 'Updated at descending order'
- value 'updated_asc', 'Updated at ascending order'
- value 'created_desc', 'Created at descending order'
- value 'created_asc', 'Created at ascending order'
+ # Deprecated, as we prefer uppercase enums
+ # https://gitlab.com/groups/gitlab-org/-/epics/1838
+ value 'updated_desc', 'Updated at descending order', deprecated: { reason: 'Use UPDATED_DESC', milestone: '13.5' }
+ value 'updated_asc', 'Updated at ascending order', deprecated: { reason: 'Use UPDATED_ASC', milestone: '13.5' }
+ value 'created_desc', 'Created at descending order', deprecated: { reason: 'Use CREATED_DESC', milestone: '13.5' }
+ value 'created_asc', 'Created at ascending order', deprecated: { reason: 'Use CREATED_ASC', milestone: '13.5' }
+
+ value 'UPDATED_DESC', 'Updated at descending order', value: :updated_desc
+ value 'UPDATED_ASC', 'Updated at ascending order', value: :updated_asc
+ value 'CREATED_DESC', 'Created at descending order', value: :created_desc
+ value 'CREATED_ASC', 'Created at ascending order', value: :created_asc
end
end
diff --git a/app/graphql/types/terraform/state_type.rb b/app/graphql/types/terraform/state_type.rb
new file mode 100644
index 00000000000..f25f3a7789b
--- /dev/null
+++ b/app/graphql/types/terraform/state_type.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Types
+ module Terraform
+ class StateType < BaseObject
+ graphql_name 'TerraformState'
+
+ authorize :read_terraform_state
+
+ field :id, GraphQL::ID_TYPE,
+ null: false,
+ description: 'ID of the Terraform state'
+
+ field :name, GraphQL::STRING_TYPE,
+ null: false,
+ description: 'Name of the Terraform state'
+
+ field :locked_by_user, Types::UserType,
+ null: true,
+ authorize: :read_user,
+ description: 'The user currently holding a lock on the Terraform state',
+ resolve: -> (state, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, state.locked_by_user_id).find }
+
+ field :locked_at, Types::TimeType,
+ null: true,
+ description: 'Timestamp the Terraform state was locked'
+
+ field :created_at, Types::TimeType,
+ null: false,
+ description: 'Timestamp the Terraform state was created'
+
+ field :updated_at, Types::TimeType,
+ null: false,
+ description: 'Timestamp the Terraform state was updated'
+ end
+ end
+end
diff --git a/app/graphql/types/timeframe_input_type.rb b/app/graphql/types/timeframe_input_type.rb
new file mode 100644
index 00000000000..79c1bc5cf01
--- /dev/null
+++ b/app/graphql/types/timeframe_input_type.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class TimeframeInputType < RangeInputType[::Types::DateType]
+ graphql_name 'Timeframe'
+ description 'A time-frame defined as a closed inclusive range of two dates'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end