summaryrefslogtreecommitdiff
path: root/app/graphql
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-17 11:59:07 +0000
commit8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch)
tree544930fb309b30317ae9797a9683768705d664c4 /app/graphql
parent4b1de649d0168371549608993deac953eb692019 (diff)
downloadgitlab-ce-8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca.tar.gz
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/mutations/alert_management/base.rb2
-rw-r--r--app/graphql/mutations/alert_management/create_alert_issue.rb1
-rw-r--r--app/graphql/mutations/alert_management/http_integration/destroy.rb2
-rw-r--r--app/graphql/mutations/alert_management/http_integration/reset_token.rb2
-rw-r--r--app/graphql/mutations/alert_management/http_integration/update.rb2
-rw-r--r--app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb2
-rw-r--r--app/graphql/mutations/alert_management/prometheus_integration/update.rb2
-rw-r--r--app/graphql/mutations/award_emojis/add.rb2
-rw-r--r--app/graphql/mutations/award_emojis/base.rb23
-rw-r--r--app/graphql/mutations/award_emojis/remove.rb2
-rw-r--r--app/graphql/mutations/award_emojis/toggle.rb2
-rw-r--r--app/graphql/mutations/boards/common_mutation_arguments.rb24
-rw-r--r--app/graphql/mutations/boards/create.rb26
-rw-r--r--app/graphql/mutations/boards/lists/create.rb20
-rw-r--r--app/graphql/mutations/boards/update.rb43
-rw-r--r--app/graphql/mutations/ci/base.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/finds_by_gid.rb9
-rw-r--r--app/graphql/mutations/container_repositories/destroy.rb13
-rw-r--r--app/graphql/mutations/container_repositories/destroy_base.rb18
-rw-r--r--app/graphql/mutations/container_repositories/destroy_tags.rb50
-rw-r--r--app/graphql/mutations/design_management/base.rb2
-rw-r--r--app/graphql/mutations/discussions/toggle_resolve.rb2
-rw-r--r--app/graphql/mutations/environments/canary_ingress/update.rb39
-rw-r--r--app/graphql/mutations/issues/update.rb2
-rw-r--r--app/graphql/mutations/merge_requests/base.rb2
-rw-r--r--app/graphql/mutations/metrics/dashboard/annotations/create.rb4
-rw-r--r--app/graphql/mutations/metrics/dashboard/annotations/delete.rb2
-rw-r--r--app/graphql/mutations/notes/create/base.rb2
-rw-r--r--app/graphql/mutations/notes/create/note.rb2
-rw-r--r--app/graphql/mutations/notes/destroy.rb2
-rw-r--r--app/graphql/mutations/notes/reposition_image_diff_note.rb2
-rw-r--r--app/graphql/mutations/notes/update/base.rb2
-rw-r--r--app/graphql/mutations/releases/create.rb3
-rw-r--r--app/graphql/mutations/releases/delete.rb40
-rw-r--r--app/graphql/mutations/releases/update.rb70
-rw-r--r--app/graphql/mutations/snippets/create.rb31
-rw-r--r--app/graphql/mutations/snippets/destroy.rb2
-rw-r--r--app/graphql/mutations/snippets/mark_as_spam.rb4
-rw-r--r--app/graphql/mutations/snippets/update.rb8
-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.rb4
-rw-r--r--app/graphql/queries/epic/epic_children.query.graphql126
-rw-r--r--app/graphql/queries/epic/epic_details.query.graphql20
-rw-r--r--app/graphql/resolvers/alert_management/alert_resolver.rb5
-rw-r--r--app/graphql/resolvers/assigned_merge_requests_resolver.rb1
-rw-r--r--app/graphql/resolvers/authored_merge_requests_resolver.rb1
-rw-r--r--app/graphql/resolvers/base_resolver.rb12
-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/ci/config_resolver.rb60
-rw-r--r--app/graphql/resolvers/ci/jobs_resolver.rb2
-rw-r--r--app/graphql/resolvers/ci/pipeline_stages_resolver.rb3
-rw-r--r--app/graphql/resolvers/ci/runner_setup_resolver.rb5
-rw-r--r--app/graphql/resolvers/concerns/caching_array_resolver.rb17
-rw-r--r--app/graphql/resolvers/concerns/manual_authorization.rb11
-rw-r--r--app/graphql/resolvers/design_management/design_resolver.rb2
-rw-r--r--app/graphql/resolvers/design_management/version/design_at_version_resolver.rb2
-rw-r--r--app/graphql/resolvers/design_management/version_in_collection_resolver.rb2
-rw-r--r--app/graphql/resolvers/design_management/versions_resolver.rb2
-rw-r--r--app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb2
-rw-r--r--app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb27
-rw-r--r--app/graphql/resolvers/group_members_resolver.rb5
-rw-r--r--app/graphql/resolvers/issue_status_counts_resolver.rb2
-rw-r--r--app/graphql/resolvers/issues_resolver.rb4
-rw-r--r--app/graphql/resolvers/members_resolver.rb4
-rw-r--r--app/graphql/resolvers/merge_request_pipelines_resolver.rb22
-rw-r--r--app/graphql/resolvers/merge_requests_resolver.rb10
-rw-r--r--app/graphql/resolvers/project_members_resolver.rb5
-rw-r--r--app/graphql/resolvers/project_merge_requests_resolver.rb1
-rw-r--r--app/graphql/resolvers/project_pipeline_resolver.rb4
-rw-r--r--app/graphql/resolvers/project_pipeline_statistics_resolver.rb28
-rw-r--r--app/graphql/resolvers/projects/jira_imports_resolver.rb23
-rw-r--r--app/graphql/resolvers/projects/services_resolver.rb6
-rw-r--r--app/graphql/resolvers/review_requested_merge_requests_resolver.rb13
-rw-r--r--app/graphql/resolvers/snippets/blobs_resolver.rb6
-rw-r--r--app/graphql/resolvers/user_discussions_count_resolver.rb26
-rw-r--r--app/graphql/resolvers/user_notes_count_resolver.rb26
-rw-r--r--app/graphql/resolvers/users/group_count_resolver.rb2
-rw-r--r--app/graphql/resolvers/users_resolver.rb2
-rw-r--r--app/graphql/types/alert_management/domain_filter_enum.rb13
-rw-r--r--app/graphql/types/alert_management/prometheus_integration_type.rb2
-rw-r--r--app/graphql/types/award_emojis/award_emoji_type.rb9
-rw-r--r--app/graphql/types/base_field.rb7
-rw-r--r--app/graphql/types/base_interface.rb2
-rw-r--r--app/graphql/types/board_list_type.rb7
-rw-r--r--app/graphql/types/board_type.rb6
-rw-r--r--app/graphql/types/ci/analytics_type.rb33
-rw-r--r--app/graphql/types/ci/ci_cd_setting_type.rb20
-rw-r--r--app/graphql/types/ci/config/config_type.rb21
-rw-r--r--app/graphql/types/ci/config/group_type.rb19
-rw-r--r--app/graphql/types/ci/config/job_type.rb21
-rw-r--r--app/graphql/types/ci/config/need_type.rb15
-rw-r--r--app/graphql/types/ci/config/stage_type.rb17
-rw-r--r--app/graphql/types/ci/config/status_enum.rb15
-rw-r--r--app/graphql/types/ci/detailed_status_type.rb30
-rw-r--r--app/graphql/types/ci/group_type.rb7
-rw-r--r--app/graphql/types/ci/job_artifact_file_type_enum.rb13
-rw-r--r--app/graphql/types/ci/job_artifact_type.rb24
-rw-r--r--app/graphql/types/ci/job_type.rb30
-rw-r--r--app/graphql/types/ci/pipeline_type.rb24
-rw-r--r--app/graphql/types/ci/stage_type.rb7
-rw-r--r--app/graphql/types/commit_type.rb11
-rw-r--r--app/graphql/types/concerns/gitlab_style_deprecations.rb4
-rw-r--r--app/graphql/types/container_repository_type.rb5
-rw-r--r--app/graphql/types/design_management/design_collection_type.rb2
-rw-r--r--app/graphql/types/error_tracking/sentry_error_collection_type.rb21
-rw-r--r--app/graphql/types/group_invitation_type.rb7
-rw-r--r--app/graphql/types/group_member_relation_enum.rb12
-rw-r--r--app/graphql/types/group_member_type.rb7
-rw-r--r--app/graphql/types/group_type.rb25
-rw-r--r--app/graphql/types/issue_type.rb26
-rw-r--r--app/graphql/types/jira_import_type.rb3
-rw-r--r--app/graphql/types/jira_users_mapping_input_type.rb2
-rw-r--r--app/graphql/types/merge_request_connection_type.rb15
-rw-r--r--app/graphql/types/merge_request_type.rb54
-rw-r--r--app/graphql/types/mutation_type.rb4
-rw-r--r--app/graphql/types/namespace_type.rb8
-rw-r--r--app/graphql/types/notes/diff_position_type.rb42
-rw-r--r--app/graphql/types/notes/note_type.rb14
-rw-r--r--app/graphql/types/permission_types/merge_request.rb4
-rw-r--r--app/graphql/types/project_member_relation_enum.rb12
-rw-r--r--app/graphql/types/project_type.rb71
-rw-r--r--app/graphql/types/query_type.rb10
-rw-r--r--app/graphql/types/snippets/blob_viewer_type.rb14
-rw-r--r--app/graphql/types/sort_enum.rb8
-rw-r--r--app/graphql/types/terraform/state_type.rb8
-rw-r--r--app/graphql/types/terraform/state_version_type.rb35
-rw-r--r--app/graphql/types/todo_type.rb21
-rw-r--r--app/graphql/types/tree/blob_type.rb11
-rw-r--r--app/graphql/types/tree/tree_type.rb31
-rw-r--r--app/graphql/types/user_type.rb12
132 files changed, 1451 insertions, 367 deletions
diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb
index 81d5ee95f06..8c6b4005cf8 100644
--- a/app/graphql/mutations/alert_management/base.rb
+++ b/app/graphql/mutations/alert_management/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::STRING_TYPE,
required: true,
- description: "The iid of the alert to mutate"
+ description: "The IID of the alert to mutate"
field :alert,
Types::AlertManagement::AlertType,
diff --git a/app/graphql/mutations/alert_management/create_alert_issue.rb b/app/graphql/mutations/alert_management/create_alert_issue.rb
index 2ddb94700c2..2c128e1b339 100644
--- a/app/graphql/mutations/alert_management/create_alert_issue.rb
+++ b/app/graphql/mutations/alert_management/create_alert_issue.rb
@@ -10,6 +10,7 @@ module Mutations
result = create_alert_issue(alert, current_user)
track_usage_event(:incident_management_incident_created, current_user.id)
+ track_usage_event(:incident_management_alert_create_incident, current_user.id)
prepare_response(alert, result)
end
diff --git a/app/graphql/mutations/alert_management/http_integration/destroy.rb b/app/graphql/mutations/alert_management/http_integration/destroy.rb
index 0f478760aab..45d4bd778da 100644
--- a/app/graphql/mutations/alert_management/http_integration/destroy.rb
+++ b/app/graphql/mutations/alert_management/http_integration/destroy.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
- description: "The id of the integration to remove"
+ description: "The ID of the integration to remove"
def resolve(id:)
integration = authorized_find!(id: id)
diff --git a/app/graphql/mutations/alert_management/http_integration/reset_token.rb b/app/graphql/mutations/alert_management/http_integration/reset_token.rb
index eefab156825..3938b38260e 100644
--- a/app/graphql/mutations/alert_management/http_integration/reset_token.rb
+++ b/app/graphql/mutations/alert_management/http_integration/reset_token.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
def resolve(id:)
integration = authorized_find!(id: id)
diff --git a/app/graphql/mutations/alert_management/http_integration/update.rb b/app/graphql/mutations/alert_management/http_integration/update.rb
index 309c45b04ac..98e0f7eb14f 100644
--- a/app/graphql/mutations/alert_management/http_integration/update.rb
+++ b/app/graphql/mutations/alert_management/http_integration/update.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::AlertManagement::HttpIntegration],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
argument :name, GraphQL::STRING_TYPE,
required: false,
diff --git a/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb b/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb
index 745ac51f6e3..effecd8364d 100644
--- a/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb
+++ b/app/graphql/mutations/alert_management/prometheus_integration/reset_token.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::PrometheusService],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
def resolve(id:)
integration = authorized_find!(id: id)
diff --git a/app/graphql/mutations/alert_management/prometheus_integration/update.rb b/app/graphql/mutations/alert_management/prometheus_integration/update.rb
index 1f0dea119c5..46f4c23b739 100644
--- a/app/graphql/mutations/alert_management/prometheus_integration/update.rb
+++ b/app/graphql/mutations/alert_management/prometheus_integration/update.rb
@@ -8,7 +8,7 @@ module Mutations
argument :id, Types::GlobalIDType[::PrometheusService],
required: true,
- description: "The id of the integration to mutate"
+ description: "The ID of the integration to mutate"
argument :active, GraphQL::BOOLEAN_TYPE,
required: false,
diff --git a/app/graphql/mutations/award_emojis/add.rb b/app/graphql/mutations/award_emojis/add.rb
index 856fdd5fb14..e7ee2ec4fad 100644
--- a/app/graphql/mutations/award_emojis/add.rb
+++ b/app/graphql/mutations/award_emojis/add.rb
@@ -8,8 +8,6 @@ module Mutations
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
- check_object_is_awardable!(awardable)
-
service = ::AwardEmojis::AddService.new(awardable, args[:name], current_user).execute
{
diff --git a/app/graphql/mutations/award_emojis/base.rb b/app/graphql/mutations/award_emojis/base.rb
index df6b883529e..4bd8304c3fc 100644
--- a/app/graphql/mutations/award_emojis/base.rb
+++ b/app/graphql/mutations/award_emojis/base.rb
@@ -3,12 +3,16 @@
module Mutations
module AwardEmojis
class Base < BaseMutation
+ include ::Mutations::FindsByGid
+
+ NOT_EMOJI_AWARDABLE = 'You cannot award emoji to this resource.'
+
authorize :award_emoji
argument :awardable_id,
::Types::GlobalIDType[::Awardable],
required: true,
- description: 'The global id of the awardable resource'
+ description: 'The global ID of the awardable resource'
argument :name,
GraphQL::STRING_TYPE,
@@ -22,20 +26,15 @@ module Mutations
private
+ # TODO: remove this method when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
def find_object(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)
+ super(id: ::Types::GlobalIDType[::Awardable].coerce_isolated_input(id))
end
- # Called by mutations methods after performing an authorization check
- # of an awardable object.
- def check_object_is_awardable!(object)
- unless object.is_a?(Awardable) && object.emoji_awardable?
- raise Gitlab::Graphql::Errors::ResourceNotAvailable,
- 'Cannot award emoji to this resource'
- end
+ def authorize!(object)
+ super
+ raise_resource_not_available_error!(NOT_EMOJI_AWARDABLE) unless object.emoji_awardable?
end
end
end
diff --git a/app/graphql/mutations/award_emojis/remove.rb b/app/graphql/mutations/award_emojis/remove.rb
index c654688c6dc..a9655daeea7 100644
--- a/app/graphql/mutations/award_emojis/remove.rb
+++ b/app/graphql/mutations/award_emojis/remove.rb
@@ -8,8 +8,6 @@ module Mutations
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
- check_object_is_awardable!(awardable)
-
service = ::AwardEmojis::DestroyService.new(awardable, args[:name], current_user).execute
{
diff --git a/app/graphql/mutations/award_emojis/toggle.rb b/app/graphql/mutations/award_emojis/toggle.rb
index 679ec7a14ff..e741f972b1b 100644
--- a/app/graphql/mutations/award_emojis/toggle.rb
+++ b/app/graphql/mutations/award_emojis/toggle.rb
@@ -12,8 +12,6 @@ module Mutations
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
- check_object_is_awardable!(awardable)
-
service = ::AwardEmojis::ToggleService.new(awardable, args[:name], current_user).execute
toggled_on = awardable.awarded_emoji?(args[:name], current_user)
diff --git a/app/graphql/mutations/boards/common_mutation_arguments.rb b/app/graphql/mutations/boards/common_mutation_arguments.rb
new file mode 100644
index 00000000000..c4f8d299318
--- /dev/null
+++ b/app/graphql/mutations/boards/common_mutation_arguments.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ module CommonMutationArguments
+ extend ActiveSupport::Concern
+
+ included do
+ argument :name,
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The board name.'
+ argument :hide_backlog_list,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: copy_field_description(Types::BoardType, :hide_backlog_list)
+ argument :hide_closed_list,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: copy_field_description(Types::BoardType, :hide_closed_list)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/create.rb b/app/graphql/mutations/boards/create.rb
index ebbd19930ec..92bce557446 100644
--- a/app/graphql/mutations/boards/create.rb
+++ b/app/graphql/mutations/boards/create.rb
@@ -7,36 +7,18 @@ module Mutations
graphql_name 'CreateBoard'
+ include Mutations::Boards::CommonMutationArguments
+
field :board,
Types::BoardType,
null: true,
description: 'The board after mutation.'
- 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,
- Types::GlobalIDType[Milestone],
- 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,
- [Types::GlobalIDType[Label]],
- required: false,
- description: 'The IDs of labels to be added to the board.'
-
authorize :admin_board
def resolve(args)
board_parent = authorized_resource_parent_find!(args)
+
response = ::Boards::CreateService.new(board_parent, current_user, args).execute
{
@@ -47,3 +29,5 @@ module Mutations
end
end
end
+
+Mutations::Boards::Create.prepend_if_ee('::EE::Mutations::Boards::Create')
diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb
index 3fe1052315f..f6df63365b2 100644
--- a/app/graphql/mutations/boards/lists/create.rb
+++ b/app/graphql/mutations/boards/lists/create.rb
@@ -27,30 +27,16 @@ module Mutations
board = authorized_find!(id: args[:board_id])
params = create_list_params(args)
- authorize_list_type_resource!(board, params)
-
- list = create_list(board, params)
+ response = create_list(board, params)
{
- list: list.valid? ? list : nil,
- errors: errors_on_object(list)
+ list: response.success? ? response.payload[:list] : nil,
+ errors: response.errors
}
end
private
- # Overridden in EE
- def authorize_list_type_resource!(board, params)
- return unless params[:label_id]
-
- labels = ::Labels::AvailableLabelsService.new(current_user, board.resource_parent, params)
- .filter_labels_ids_in_param(:label_id)
-
- unless labels.present?
- raise Gitlab::Graphql::Errors::ArgumentError, 'Label not found!'
- end
- end
-
def create_list(board, params)
create_list_service =
::Boards::Lists::CreateService.new(board.resource_parent, current_user, params)
diff --git a/app/graphql/mutations/boards/update.rb b/app/graphql/mutations/boards/update.rb
new file mode 100644
index 00000000000..5cb434e41fd
--- /dev/null
+++ b/app/graphql/mutations/boards/update.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Boards
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'UpdateBoard'
+
+ include Mutations::Boards::CommonMutationArguments
+
+ argument :id,
+ ::Types::GlobalIDType[::Board],
+ required: true,
+ description: 'The board global ID.'
+
+ field :board,
+ Types::BoardType,
+ null: true,
+ description: 'The board after mutation.'
+
+ authorize :admin_board
+
+ def resolve(id:, **args)
+ board = authorized_find!(id: id)
+
+ ::Boards::UpdateService.new(board.resource_parent, current_user, args).execute(board)
+
+ {
+ board: board,
+ errors: errors_on_object(board)
+ }
+ end
+
+ def find_object(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Board].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
+
+Mutations::Boards::Update.prepend_if_ee('::EE::Mutations::Boards::Update')
diff --git a/app/graphql/mutations/ci/base.rb b/app/graphql/mutations/ci/base.rb
index aaece2a3021..0ccee5661b7 100644
--- a/app/graphql/mutations/ci/base.rb
+++ b/app/graphql/mutations/ci/base.rb
@@ -7,7 +7,7 @@ module Mutations
argument :id, PipelineID,
required: true,
- description: 'The id of the pipeline to mutate'
+ description: 'The ID of the pipeline to mutate'
private
diff --git a/app/graphql/mutations/concerns/mutations/finds_by_gid.rb b/app/graphql/mutations/concerns/mutations/finds_by_gid.rb
new file mode 100644
index 00000000000..157f87a413d
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/finds_by_gid.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Mutations
+ module FindsByGid
+ def find_object(id:)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+end
diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb
index 8312193147f..90fba66e7b3 100644
--- a/app/graphql/mutations/container_repositories/destroy.rb
+++ b/app/graphql/mutations/container_repositories/destroy.rb
@@ -2,9 +2,7 @@
module Mutations
module ContainerRepositories
- class Destroy < Mutations::BaseMutation
- include ::Mutations::PackageEventable
-
+ class Destroy < ::Mutations::ContainerRepositories::DestroyBase
graphql_name 'DestroyContainerRepository'
authorize :destroy_container_image
@@ -31,15 +29,6 @@ module Mutations
errors: []
}
end
-
- 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 = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
- GitlabSchema.find_by_gid(id)
- end
end
end
end
diff --git a/app/graphql/mutations/container_repositories/destroy_base.rb b/app/graphql/mutations/container_repositories/destroy_base.rb
new file mode 100644
index 00000000000..ddaa6c52121
--- /dev/null
+++ b/app/graphql/mutations/container_repositories/destroy_base.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRepositories
+ class DestroyBase < Mutations::BaseMutation
+ include ::Mutations::PackageEventable
+
+ 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 = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/container_repositories/destroy_tags.rb b/app/graphql/mutations/container_repositories/destroy_tags.rb
new file mode 100644
index 00000000000..ca6a67867c3
--- /dev/null
+++ b/app/graphql/mutations/container_repositories/destroy_tags.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRepositories
+ class DestroyTags < ::Mutations::ContainerRepositories::DestroyBase
+ LIMIT = 20.freeze
+
+ TOO_MANY_TAGS_ERROR_MESSAGE = "Number of tags is greater than #{LIMIT}"
+
+ graphql_name 'DestroyContainerRepositoryTags'
+
+ authorize :destroy_container_image
+
+ argument :id,
+ ::Types::GlobalIDType[::ContainerRepository],
+ required: true,
+ description: 'ID of the container repository.'
+
+ argument :tag_names,
+ [GraphQL::STRING_TYPE],
+ required: true,
+ description: "Container repository tag(s) to delete. Total number can't be greater than #{LIMIT}",
+ prepare: ->(tag_names, _) do
+ raise Gitlab::Graphql::Errors::ArgumentError, TOO_MANY_TAGS_ERROR_MESSAGE if tag_names.size > LIMIT
+
+ tag_names
+ end
+
+ field :deleted_tag_names,
+ [GraphQL::STRING_TYPE],
+ description: 'Deleted container repository tags',
+ null: false
+
+ def resolve(id:, tag_names:)
+ container_repository = authorized_find!(id: id)
+
+ result = ::Projects::ContainerRepository::DeleteTagsService
+ .new(container_repository.project, current_user, tags: tag_names)
+ .execute(container_repository)
+
+ track_event(:delete_tag_bulk, :tag) if result[:status] == :success
+
+ {
+ errors: Array(result[:message]),
+ deleted_tag_names: result[:deleted] || []
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/design_management/base.rb b/app/graphql/mutations/design_management/base.rb
index 918e5709b94..69fd22e46cd 100644
--- a/app/graphql/mutations/design_management/base.rb
+++ b/app/graphql/mutations/design_management/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::ID_TYPE,
required: true,
- description: "The iid of the issue to modify designs for"
+ description: "The IID of the issue to modify designs for"
private
diff --git a/app/graphql/mutations/discussions/toggle_resolve.rb b/app/graphql/mutations/discussions/toggle_resolve.rb
index 4492da74706..0e3baf8d548 100644
--- a/app/graphql/mutations/discussions/toggle_resolve.rb
+++ b/app/graphql/mutations/discussions/toggle_resolve.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
Types::GlobalIDType[Discussion],
required: true,
- description: 'The global id of the discussion'
+ description: 'The global ID of the discussion'
argument :resolve,
GraphQL::BOOLEAN_TYPE,
diff --git a/app/graphql/mutations/environments/canary_ingress/update.rb b/app/graphql/mutations/environments/canary_ingress/update.rb
new file mode 100644
index 00000000000..1798143053a
--- /dev/null
+++ b/app/graphql/mutations/environments/canary_ingress/update.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Environments
+ module CanaryIngress
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'EnvironmentsCanaryIngressUpdate'
+
+ authorize :update_environment
+
+ argument :id,
+ ::Types::GlobalIDType[::Environment],
+ required: true,
+ description: 'The global ID of the environment to update'
+
+ argument :weight,
+ GraphQL::INT_TYPE,
+ required: true,
+ description: 'The weight of the Canary Ingress'
+
+ def resolve(id:, **kwargs)
+ environment = authorized_find!(id: id)
+
+ result = ::Environments::CanaryIngress::UpdateService
+ .new(environment.project, current_user, kwargs)
+ .execute_async(environment)
+
+ { errors: Array.wrap(result[:message]) }
+ end
+
+ def find_object(id:)
+ # TODO: remove as part of https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::Environment].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb
index 9b216b31f9b..d34e351b2a6 100644
--- a/app/graphql/mutations/issues/update.rb
+++ b/app/graphql/mutations/issues/update.rb
@@ -11,7 +11,7 @@ module Mutations
required: false,
description: copy_field_description(Types::IssueType, :title)
- argument :milestone_id, GraphQL::ID_TYPE,
+ argument :milestone_id, GraphQL::ID_TYPE, # rubocop: disable Graphql/IDType
required: false,
description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null'
diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb
index 96228855ace..57920259cf7 100644
--- a/app/graphql/mutations/merge_requests/base.rb
+++ b/app/graphql/mutations/merge_requests/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :iid, GraphQL::STRING_TYPE,
required: true,
- description: "The iid of the merge request to mutate"
+ description: "The IID of the merge request to mutate"
field :merge_request,
Types::MergeRequestType,
diff --git a/app/graphql/mutations/metrics/dashboard/annotations/create.rb b/app/graphql/mutations/metrics/dashboard/annotations/create.rb
index b064f55825f..c2ec88c68ed 100644
--- a/app/graphql/mutations/metrics/dashboard/annotations/create.rb
+++ b/app/graphql/mutations/metrics/dashboard/annotations/create.rb
@@ -20,12 +20,12 @@ module Mutations
argument :environment_id,
::Types::GlobalIDType[::Environment],
required: false,
- description: 'The global id of the environment to add an annotation to'
+ description: 'The global ID of the environment to add an annotation to'
argument :cluster_id,
::Types::GlobalIDType[::Clusters::Cluster],
required: false,
- description: 'The global id of the cluster to add an annotation to'
+ description: 'The global ID of the cluster to add an annotation to'
argument :starting_at, Types::TimeType,
required: true,
diff --git a/app/graphql/mutations/metrics/dashboard/annotations/delete.rb b/app/graphql/mutations/metrics/dashboard/annotations/delete.rb
index d6731dfcafd..5d6763d8711 100644
--- a/app/graphql/mutations/metrics/dashboard/annotations/delete.rb
+++ b/app/graphql/mutations/metrics/dashboard/annotations/delete.rb
@@ -11,7 +11,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Metrics::Dashboard::Annotation],
required: true,
- description: 'The global ID of the annotation to delete'
+ description: 'Global ID of the annotation to delete'
def resolve(id:)
annotation = authorized_find!(id: id)
diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb
index 3cfdaf84760..a1d81c62d91 100644
--- a/app/graphql/mutations/notes/create/base.rb
+++ b/app/graphql/mutations/notes/create/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :noteable_id,
::Types::GlobalIDType[::Noteable],
required: true,
- description: 'The global id of the resource to add a note to'
+ description: 'The global ID of the resource to add a note to'
argument :body,
GraphQL::STRING_TYPE,
diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
index e97037171f7..f1cd3bddca8 100644
--- a/app/graphql/mutations/notes/create/note.rb
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -9,7 +9,7 @@ module Mutations
argument :discussion_id,
::Types::GlobalIDType[::Discussion],
required: false,
- description: 'The global id of the discussion this note is in reply to'
+ description: 'The global ID of the discussion this note is in reply to'
private
diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb
index 63e5eeb5ecf..0e6a215bf00 100644
--- a/app/graphql/mutations/notes/destroy.rb
+++ b/app/graphql/mutations/notes/destroy.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Note],
required: true,
- description: 'The global id of the note to destroy'
+ description: 'The global ID of the note to destroy'
def resolve(id:)
note = authorized_find!(id: id)
diff --git a/app/graphql/mutations/notes/reposition_image_diff_note.rb b/app/graphql/mutations/notes/reposition_image_diff_note.rb
index 0d88bcd9a30..15bfb361b13 100644
--- a/app/graphql/mutations/notes/reposition_image_diff_note.rb
+++ b/app/graphql/mutations/notes/reposition_image_diff_note.rb
@@ -16,7 +16,7 @@ module Mutations
loads: Types::Notes::NoteType,
as: :note,
required: true,
- description: 'The global id of the DiffNote to update'
+ description: 'The global ID of the DiffNote to update'
argument :position,
Types::Notes::UpdateDiffImagePositionInputType,
diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb
index 1d5738ada77..42dac20f5d3 100644
--- a/app/graphql/mutations/notes/update/base.rb
+++ b/app/graphql/mutations/notes/update/base.rb
@@ -11,7 +11,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Note],
required: true,
- description: 'The global id of the note to update'
+ description: 'The global ID of the note to update'
def resolve(args)
note = authorized_find!(id: args[:id])
diff --git a/app/graphql/mutations/releases/create.rb b/app/graphql/mutations/releases/create.rb
index 57c1541c368..156cd252848 100644
--- a/app/graphql/mutations/releases/create.rb
+++ b/app/graphql/mutations/releases/create.rb
@@ -40,12 +40,11 @@ module Mutations
authorize :create_release
- def resolve(project_path:, milestones: nil, assets: nil, **scalars)
+ def resolve(project_path:, assets: nil, **scalars)
project = authorized_find!(full_path: project_path)
params = {
**scalars,
- milestones: milestones.presence || [],
assets: assets.to_h
}.with_indifferent_access
diff --git a/app/graphql/mutations/releases/delete.rb b/app/graphql/mutations/releases/delete.rb
new file mode 100644
index 00000000000..e887b702cce
--- /dev/null
+++ b/app/graphql/mutations/releases/delete.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Releases
+ class Delete < Base
+ graphql_name 'ReleaseDelete'
+
+ field :release,
+ Types::ReleaseType,
+ null: true,
+ description: 'The deleted release.'
+
+ argument :tag_name, GraphQL::STRING_TYPE,
+ required: true, as: :tag,
+ description: 'Name of the tag associated with the release to delete.'
+
+ authorize :destroy_release
+
+ def resolve(project_path:, tag:)
+ project = authorized_find!(full_path: project_path)
+
+ params = { tag: tag }.with_indifferent_access
+
+ result = ::Releases::DestroyService.new(project, current_user, params).execute
+
+ if result[:status] == :success
+ {
+ release: result[:release],
+ errors: []
+ }
+ else
+ {
+ release: nil,
+ errors: [result[:message]]
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/releases/update.rb b/app/graphql/mutations/releases/update.rb
new file mode 100644
index 00000000000..bf72b907679
--- /dev/null
+++ b/app/graphql/mutations/releases/update.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Releases
+ class Update < Base
+ graphql_name 'ReleaseUpdate'
+
+ field :release,
+ Types::ReleaseType,
+ null: true,
+ description: 'The release after mutation.'
+
+ argument :tag_name, GraphQL::STRING_TYPE,
+ required: true, as: :tag,
+ description: 'Name of the tag associated with the release'
+
+ argument :name, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Name of the release'
+
+ argument :description, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Description (release notes) of the release'
+
+ argument :released_at, Types::TimeType,
+ required: false,
+ description: 'The release date'
+
+ argument :milestones, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.'
+
+ authorize :update_release
+
+ def ready?(**args)
+ if args.key?(:released_at) && args[:released_at].nil?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'if the releasedAt argument is provided, it cannot be null'
+ end
+
+ if args.key?(:milestones) && args[:milestones].nil?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'if the milestones argument is provided, it cannot be null'
+ end
+
+ super
+ end
+
+ def resolve(project_path:, **scalars)
+ project = authorized_find!(full_path: project_path)
+
+ params = scalars.with_indifferent_access
+
+ release_result = ::Releases::UpdateService.new(project, current_user, params).execute
+
+ if release_result[:status] == :success
+ {
+ release: release_result[:release],
+ errors: []
+ }
+ else
+ {
+ release: nil,
+ errors: [release_result[:message]]
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index 37c0f80310c..56c3b398949 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -4,7 +4,8 @@ module Mutations
module Snippets
class Create < BaseMutation
include SpammableMutationFields
- include ResolvesProject
+
+ authorize :create_snippet
graphql_name 'CreateSnippet'
@@ -37,17 +38,15 @@ module Mutations
description: 'Actions to perform over the snippet repository and blobs',
required: false
- def resolve(args)
- project_path = args.delete(:project_path)
-
+ def resolve(project_path: nil, **args)
if project_path.present?
- project = find_project!(project_path: project_path)
- elsif !can_create_personal_snippet?
- raise_resource_not_available_error!
+ project = authorized_find!(project_path)
+ else
+ authorize!(:global)
end
service_response = ::Snippets::CreateService.new(project,
- context[:current_user],
+ current_user,
create_params(args)).execute
snippet = service_response.payload[:snippet]
@@ -67,20 +66,8 @@ module Mutations
private
- def find_project!(project_path:)
- authorized_find!(full_path: project_path)
- end
-
- def find_object(full_path:)
- resolve_project(full_path: full_path)
- end
-
- def authorized_resource?(project)
- Ability.allowed?(context[:current_user], :create_snippet, project)
- end
-
- def can_create_personal_snippet?
- Ability.allowed?(context[:current_user], :create_snippet)
+ def find_object(full_path)
+ Project.find_by_full_path(full_path)
end
def create_params(args)
diff --git a/app/graphql/mutations/snippets/destroy.rb b/app/graphql/mutations/snippets/destroy.rb
index 4915d7dd77a..bee6503372d 100644
--- a/app/graphql/mutations/snippets/destroy.rb
+++ b/app/graphql/mutations/snippets/destroy.rb
@@ -9,7 +9,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Snippet],
required: true,
- description: 'The global id of the snippet to destroy'
+ description: 'The global ID of the snippet to destroy'
def resolve(id:)
snippet = authorized_find!(id: id)
diff --git a/app/graphql/mutations/snippets/mark_as_spam.rb b/app/graphql/mutations/snippets/mark_as_spam.rb
index d6b96c699c0..2d6fea1f5ec 100644
--- a/app/graphql/mutations/snippets/mark_as_spam.rb
+++ b/app/graphql/mutations/snippets/mark_as_spam.rb
@@ -7,7 +7,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Snippet],
required: true,
- description: 'The global id of the snippet to update'
+ description: 'The global ID of the snippet to update'
def resolve(id:)
snippet = authorized_find!(id: id)
@@ -23,7 +23,7 @@ module Mutations
private
def mark_as_spam(snippet)
- Spam::MarkAsSpamService.new(spammable: snippet).execute
+ Spam::MarkAsSpamService.new(target: snippet).execute
end
def authorized_resource?(snippet)
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index bcaa807e4c1..6df1ad6d8b9 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -9,7 +9,7 @@ module Mutations
argument :id, ::Types::GlobalIDType[::Snippet],
required: true,
- description: 'The global id of the snippet to update'
+ description: 'The global ID of the snippet to update'
argument :title, GraphQL::STRING_TYPE,
required: false,
@@ -27,11 +27,11 @@ module Mutations
description: 'Actions to perform over the snippet repository and blobs',
required: false
- def resolve(args)
- snippet = authorized_find!(id: args.delete(:id))
+ def resolve(id:, **args)
+ snippet = authorized_find!(id: id)
result = ::Snippets::UpdateService.new(snippet.project,
- context[:current_user],
+ current_user,
update_params(args)).execute(snippet)
snippet = result.payload[:snippet]
diff --git a/app/graphql/mutations/todos/mark_done.rb b/app/graphql/mutations/todos/mark_done.rb
index 3d73022f266..2ae50846108 100644
--- a/app/graphql/mutations/todos/mark_done.rb
+++ b/app/graphql/mutations/todos/mark_done.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Todo],
required: true,
- description: 'The global id of the todo to mark as done'
+ description: 'The global ID of the todo to mark as done'
field :todo, Types::TodoType,
null: false,
diff --git a/app/graphql/mutations/todos/restore.rb b/app/graphql/mutations/todos/restore.rb
index 7c8f92d32f5..c532b455a16 100644
--- a/app/graphql/mutations/todos/restore.rb
+++ b/app/graphql/mutations/todos/restore.rb
@@ -10,7 +10,7 @@ module Mutations
argument :id,
::Types::GlobalIDType[::Todo],
required: true,
- description: 'The global id of the todo to restore'
+ description: 'The global ID of the todo to restore'
field :todo, Types::TodoType,
null: false,
diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb
index 9e0a95c48ec..59965589856 100644
--- a/app/graphql/mutations/todos/restore_many.rb
+++ b/app/graphql/mutations/todos/restore_many.rb
@@ -10,11 +10,11 @@ module Mutations
argument :ids,
[::Types::GlobalIDType[::Todo]],
required: true,
- description: 'The global ids of the todos to restore (a maximum of 50 is supported at once)'
+ description: 'The global IDs of the todos to restore (a maximum of 50 is supported at once)'
field :updated_ids, [::Types::GlobalIDType[Todo]],
null: false,
- description: 'The ids of the updated todo items',
+ description: 'The IDs of the updated todo items',
deprecated: { reason: 'Use todos', milestone: '13.2' }
field :todos, [::Types::TodoType],
diff --git a/app/graphql/queries/epic/epic_children.query.graphql b/app/graphql/queries/epic/epic_children.query.graphql
new file mode 100644
index 00000000000..c12778109d0
--- /dev/null
+++ b/app/graphql/queries/epic/epic_children.query.graphql
@@ -0,0 +1,126 @@
+fragment PageInfo on PageInfo {
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+}
+
+fragment RelatedTreeBaseEpic on Epic {
+ id
+ iid
+ title
+ webPath
+ relativePosition
+ userPermissions {
+ __typename
+ adminEpic
+ createEpic
+ }
+ descendantCounts {
+ __typename
+ openedEpics
+ closedEpics
+ openedIssues
+ closedIssues
+ }
+ healthStatus {
+ __typename
+ issuesAtRisk
+ issuesOnTrack
+ issuesNeedingAttention
+ }
+}
+
+fragment EpicNode on Epic {
+ ...RelatedTreeBaseEpic
+ state
+ reference(full: true)
+ relationPath
+ createdAt
+ closedAt
+ hasChildren
+ hasIssues
+ group {
+ __typename
+ fullPath
+ }
+}
+
+query childItems(
+ $fullPath: ID!
+ $iid: ID
+ $pageSize: Int = 100
+ $epicEndCursor: String = ""
+ $issueEndCursor: String = ""
+) {
+ group(fullPath: $fullPath) {
+ __typename
+ id
+ path
+ fullPath
+ epic(iid: $iid) {
+ __typename
+ ...RelatedTreeBaseEpic
+ children(first: $pageSize, after: $epicEndCursor) {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ ...EpicNode
+ }
+ }
+ pageInfo {
+ __typename
+ ...PageInfo
+ }
+ }
+ issues(first: $pageSize, after: $issueEndCursor) {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ iid
+ epicIssueId
+ title
+ closedAt
+ state
+ createdAt
+ confidential
+ dueDate
+ weight
+ webPath
+ reference(full: true)
+ relationPath
+ relativePosition
+ assignees {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ webUrl
+ name
+ username
+ avatarUrl
+ }
+ }
+ }
+ milestone {
+ __typename
+ title
+ startDate
+ dueDate
+ }
+ healthStatus
+ }
+ }
+ pageInfo {
+ __typename
+ ...PageInfo
+ }
+ }
+ }
+ }
+}
diff --git a/app/graphql/queries/epic/epic_details.query.graphql b/app/graphql/queries/epic/epic_details.query.graphql
new file mode 100644
index 00000000000..406d630b180
--- /dev/null
+++ b/app/graphql/queries/epic/epic_details.query.graphql
@@ -0,0 +1,20 @@
+query epicDetails($fullPath: ID!, $iid: ID!) {
+ group(fullPath: $fullPath) {
+ __typename
+ epic(iid: $iid) {
+ __typename
+ participants {
+ __typename
+ edges {
+ __typename
+ node {
+ __typename
+ name
+ avatarUrl
+ webUrl
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/graphql/resolvers/alert_management/alert_resolver.rb b/app/graphql/resolvers/alert_management/alert_resolver.rb
index c3219d9cdc3..b115bd80113 100644
--- a/app/graphql/resolvers/alert_management/alert_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_resolver.rb
@@ -18,6 +18,11 @@ module Resolvers
description: 'Sort alerts by this criteria',
required: false
+ argument :domain, Types::AlertManagement::DomainFilterEnum,
+ description: 'Filter query for given domain',
+ required: true,
+ default_value: 'operations'
+
argument :search, GraphQL::STRING_TYPE,
description: 'Search query for title, description, service, or monitoring_tool.',
required: false
diff --git a/app/graphql/resolvers/assigned_merge_requests_resolver.rb b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
index 30415ef5d2d..385f8db51b0 100644
--- a/app/graphql/resolvers/assigned_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
@@ -4,6 +4,7 @@ module Resolvers
class AssignedMergeRequestsResolver < UserMergeRequestsResolverBase
type ::Types::MergeRequestType.connection_type, null: true
accept_author
+ accept_reviewer
def user_role
:assignee
diff --git a/app/graphql/resolvers/authored_merge_requests_resolver.rb b/app/graphql/resolvers/authored_merge_requests_resolver.rb
index 1426ca83c06..4de1046ce0d 100644
--- a/app/graphql/resolvers/authored_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/authored_merge_requests_resolver.rb
@@ -4,6 +4,7 @@ module Resolvers
class AuthoredMergeRequestsResolver < UserMergeRequestsResolverBase
type ::Types::MergeRequestType.connection_type, null: true
accept_assignee
+ accept_reviewer
def user_role
:author
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 87a63231b22..539e37db1c2 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -8,6 +8,14 @@ module Resolvers
argument_class ::Types::BaseArgument
+ def self.requires_argument!
+ @requires_argument = true
+ end
+
+ def self.field_options
+ super.merge(requires_argument: @requires_argument)
+ end
+
def self.singular_type
return unless type
@@ -109,6 +117,10 @@ module Resolvers
[args[:iid], args[:iids]].any? ? 0 : 0.01
end
+ def offset_pagination(relation)
+ ::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(relation)
+ end
+
override :object
def object
super.tap do |obj|
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb
index 3421e1024c0..3e4a5a3cb70 100644
--- a/app/graphql/resolvers/board_list_issues_resolver.rb
+++ b/app/graphql/resolvers/board_list_issues_resolver.rb
@@ -16,7 +16,7 @@ module Resolvers
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)
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(service.execute)
+ offset_pagination(service.execute)
end
# https://gitlab.com/gitlab-org/gitlab/-/issues/235681
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
index ef12dfa19ff..35d938c50be 100644
--- a/app/graphql/resolvers/board_lists_resolver.rb
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -3,9 +3,13 @@
module Resolvers
class BoardListsResolver < BaseResolver
include BoardIssueFilterable
+ prepend ManualAuthorization
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::BoardListType, null: true
+ extras [:lookahead]
+
+ authorize :read_list
argument :id, Types::GlobalIDType[List],
required: false,
@@ -27,7 +31,7 @@ module Resolvers
List.preload_preferences_for_user(lists, context[:current_user])
end
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(lists)
+ offset_pagination(lists)
end
private
@@ -42,10 +46,6 @@ module Resolvers
service.execute(board, create_default_lists: false)
end
- def authorized_resource?(board)
- Ability.allowed?(context[:current_user], :read_list, board)
- end
-
def load_preferences?(lookahead)
lookahead&.selection(:edges)&.selection(:node)&.selects?(:collapsed)
end
diff --git a/app/graphql/resolvers/ci/config_resolver.rb b/app/graphql/resolvers/ci/config_resolver.rb
new file mode 100644
index 00000000000..d6e7c206691
--- /dev/null
+++ b/app/graphql/resolvers/ci/config_resolver.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class ConfigResolver < BaseResolver
+ type Types::Ci::Config::ConfigType, null: true
+
+ argument :content, GraphQL::STRING_TYPE,
+ required: true,
+ description: 'Contents of .gitlab-ci.yml'
+
+ def resolve(content:)
+ result = ::Gitlab::Ci::YamlProcessor.new(content).execute
+
+ response = if result.errors.empty?
+ {
+ status: :valid,
+ errors: [],
+ stages: make_stages(result.jobs)
+ }
+ else
+ {
+ status: :invalid,
+ errors: result.errors
+ }
+ end
+
+ response.merge(merged_yaml: result.merged_yaml)
+ end
+
+ private
+
+ def make_jobs(config_jobs)
+ config_jobs.map do |job_name, job|
+ {
+ name: job_name,
+ stage: job[:stage],
+ group_name: CommitStatus.new(name: job_name).group_name,
+ needs: job.dig(:needs, :job) || []
+ }
+ end
+ end
+
+ def make_groups(job_data)
+ jobs = make_jobs(job_data)
+
+ jobs_by_group = jobs.group_by { |job| job[:group_name] }
+ jobs_by_group.map do |name, jobs|
+ { jobs: jobs, name: name, stage: jobs.first[:stage], size: jobs.size }
+ end
+ end
+
+ def make_stages(jobs)
+ make_groups(jobs)
+ .group_by { |group| group[:stage] }
+ .map { |name, groups| { name: name, groups: groups } }
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/jobs_resolver.rb b/app/graphql/resolvers/ci/jobs_resolver.rb
index 8a9ae42b375..2c4911748a5 100644
--- a/app/graphql/resolvers/ci/jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/jobs_resolver.rb
@@ -5,6 +5,8 @@ module Resolvers
class JobsResolver < BaseResolver
alias_method :pipeline, :object
+ type ::Types::Ci::JobType.connection_type, null: true
+
argument :security_report_types, [Types::Security::ReportTypeEnum],
required: false,
description: 'Filter jobs by the type of security report they produce'
diff --git a/app/graphql/resolvers/ci/pipeline_stages_resolver.rb b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
index f9817d8b97b..98170e0cd2e 100644
--- a/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
+++ b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb
@@ -5,6 +5,9 @@ module Resolvers
class PipelineStagesResolver < BaseResolver
include LooksAhead
+ type Types::Ci::StageType.connection_type, null: true
+ extras [:lookahead]
+
alias_method :pipeline, :object
def resolve_with_lookahead
diff --git a/app/graphql/resolvers/ci/runner_setup_resolver.rb b/app/graphql/resolvers/ci/runner_setup_resolver.rb
index 241cd57f74b..f68d71174c3 100644
--- a/app/graphql/resolvers/ci/runner_setup_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_setup_resolver.rb
@@ -23,7 +23,10 @@ module Resolvers
def resolve(platform:, architecture:, **args)
instructions = Gitlab::Ci::RunnerInstructions.new(
- { current_user: current_user, os: platform, arch: architecture }.merge(target_param(args))
+ current_user: current_user,
+ os: platform,
+ arch: architecture,
+ **target_param(args)
)
{
diff --git a/app/graphql/resolvers/concerns/caching_array_resolver.rb b/app/graphql/resolvers/concerns/caching_array_resolver.rb
index 4f2c8b98928..e7555dcf42c 100644
--- a/app/graphql/resolvers/concerns/caching_array_resolver.rb
+++ b/app/graphql/resolvers/concerns/caching_array_resolver.rb
@@ -43,8 +43,10 @@
# (i.e. `resolve(**args).sync == query_for(query_input(**args)).to_a`).
#
# Classes may implement:
-# - #item_found(A, R) (return value is ignored)
# - max_union_size Integer (the maximum number of queries to run in any one union)
+# - preload -> Preloads|NilClass (a set of preloads to apply to each query)
+# - #item_found(A, R) (return value is ignored)
+# - allowed?(R) -> Boolean (if this method returns false, the value is not resolved)
module CachingArrayResolver
MAX_UNION_SIZE = 50
@@ -62,6 +64,7 @@ module CachingArrayResolver
queries.in_groups_of(max_union_size, false).each do |group|
by_id = model_class
.from_union(tag(group), remove_duplicates: false)
+ .preload(preload) # rubocop: disable CodeReuse/ActiveRecord
.group_by { |r| r[primary_key] }
by_id.values.each do |item_group|
@@ -75,6 +78,16 @@ module CachingArrayResolver
end
end
+ # Override to apply filters on a per-item basis
+ def allowed?(item)
+ true
+ end
+
+ # Override to specify preloads for each query
+ def preload
+ nil
+ end
+
# Override this to intercept the items once they are found
def item_found(query_input, item)
end
@@ -94,6 +107,8 @@ module CachingArrayResolver
end
def found(loader, key, value)
+ return unless allowed?(value)
+
loader.call(key) do |vs|
item_found(key, value)
vs << value
diff --git a/app/graphql/resolvers/concerns/manual_authorization.rb b/app/graphql/resolvers/concerns/manual_authorization.rb
new file mode 100644
index 00000000000..182110b9594
--- /dev/null
+++ b/app/graphql/resolvers/concerns/manual_authorization.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+# TODO: remove this entirely when framework authorization is released
+# See: https://gitlab.com/gitlab-org/gitlab/-/issues/290216
+module ManualAuthorization
+ def resolve(**args)
+ super
+ rescue ::Gitlab::Graphql::Errors::ResourceNotAvailable
+ nil
+ end
+end
diff --git a/app/graphql/resolvers/design_management/design_resolver.rb b/app/graphql/resolvers/design_management/design_resolver.rb
index e0a68bae397..b60c14ca835 100644
--- a/app/graphql/resolvers/design_management/design_resolver.rb
+++ b/app/graphql/resolvers/design_management/design_resolver.rb
@@ -5,6 +5,8 @@ module Resolvers
class DesignResolver < BaseResolver
type ::Types::DesignManagement::DesignType, null: true
+ requires_argument!
+
argument :id, ::Types::GlobalIDType[::DesignManagement::Design],
required: false,
description: 'Find a design by its ID'
diff --git a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
index 70021057f71..49a4974bfbf 100644
--- a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
+++ b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
@@ -12,6 +12,8 @@ module Resolvers
type Types::DesignManagement::DesignAtVersionType, null: true
+ requires_argument!
+
authorize :read_design
argument :id, DesignAtVersionID,
diff --git a/app/graphql/resolvers/design_management/version_in_collection_resolver.rb b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
index ecd7ab3ee45..7d20cfc2c8e 100644
--- a/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
+++ b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
@@ -7,6 +7,8 @@ module Resolvers
type Types::DesignManagement::VersionType, null: true
+ requires_argument!
+
authorize :read_design
alias_method :collection, :object
diff --git a/app/graphql/resolvers/design_management/versions_resolver.rb b/app/graphql/resolvers/design_management/versions_resolver.rb
index 23858c8e991..3c718a631db 100644
--- a/app/graphql/resolvers/design_management/versions_resolver.rb
+++ b/app/graphql/resolvers/design_management/versions_resolver.rb
@@ -9,6 +9,8 @@ module Resolvers
VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
+ extras [:parent]
+
argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE,
as: :sha,
required: false,
diff --git a/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb b/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb
index 669b487db10..13b5672d750 100644
--- a/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb
+++ b/app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb
@@ -3,6 +3,8 @@
module Resolvers
module ErrorTracking
class SentryErrorStackTraceResolver < BaseResolver
+ type Types::ErrorTracking::SentryErrorStackTraceType, null: true
+
argument :id, ::Types::GlobalIDType[::Gitlab::ErrorTracking::DetailedError],
required: true,
description: 'ID of the Sentry issue'
diff --git a/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb b/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb
index c5cf924ce7f..e844ffedbeb 100644
--- a/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb
+++ b/app/graphql/resolvers/error_tracking/sentry_errors_resolver.rb
@@ -4,19 +4,26 @@ module Resolvers
module ErrorTracking
class SentryErrorsResolver < BaseResolver
type Types::ErrorTracking::SentryErrorType.connection_type, null: true
+ extension Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
+
+ argument :search_term, ::GraphQL::STRING_TYPE,
+ description: 'Search query for the Sentry error details',
+ required: false
+
+ # TODO: convert to Enum
+ argument :sort, ::GraphQL::STRING_TYPE,
+ description: 'Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default',
+ required: false
+
+ delegate :project, to: :object
def resolve(**args)
args[:cursor] = args.delete(:after)
- project = object.project
- result = ::ErrorTracking::ListIssuesService.new(
- project,
- context[:current_user],
- args
- ).execute
+ result = ::ErrorTracking::ListIssuesService.new(project, current_user, args).execute
- next_cursor = result[:pagination]&.dig('next', 'cursor')
- previous_cursor = result[:pagination]&.dig('previous', 'cursor')
+ next_cursor = result.dig(:pagination, 'next', 'cursor')
+ previous_cursor = result.dig(:pagination, 'previous', 'cursor')
issues = result[:issues]
# ReactiveCache is still fetching data
@@ -24,6 +31,10 @@ module Resolvers
Gitlab::Graphql::ExternallyPaginatedArray.new(previous_cursor, next_cursor, *issues)
end
+
+ def self.field_options
+ super.merge(connection: false) # we manage the pagination manually, so opt out of the connection field extension
+ end
end
end
end
diff --git a/app/graphql/resolvers/group_members_resolver.rb b/app/graphql/resolvers/group_members_resolver.rb
index d3aa376c29c..fcdf7c01d8b 100644
--- a/app/graphql/resolvers/group_members_resolver.rb
+++ b/app/graphql/resolvers/group_members_resolver.rb
@@ -6,6 +6,11 @@ module Resolvers
authorize :read_group_member
+ argument :relations, [Types::GroupMemberRelationEnum],
+ description: 'Filter members by the given member relations',
+ required: false,
+ default_value: GroupMembersFinder::DEFAULT_RELATIONS
+
private
def preloads
diff --git a/app/graphql/resolvers/issue_status_counts_resolver.rb b/app/graphql/resolvers/issue_status_counts_resolver.rb
index 5d0d5693244..58cff559d0d 100644
--- a/app/graphql/resolvers/issue_status_counts_resolver.rb
+++ b/app/graphql/resolvers/issue_status_counts_resolver.rb
@@ -6,6 +6,8 @@ module Resolvers
type Types::IssueStatusCountsType, null: true
+ extras [:lookahead]
+
def continue_issue_resolve(parent, finder, **args)
finder.parent_param = parent
apply_lookahead(Gitlab::IssuablesCountForState.new(finder, parent))
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index dd35219454f..ae27cce9113 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -10,7 +10,7 @@ module Resolvers
argument :sort, Types::IssueSortEnum,
description: 'Sort issues by this criteria',
required: false,
- default_value: 'created_desc'
+ default_value: :created_desc
type Types::IssueType.connection_type, null: true
@@ -24,7 +24,7 @@ module Resolvers
if non_stable_cursor_sort?(args[:sort])
# Certain complex sorts are not supported by the stable cursor pagination yet.
# In these cases, we use offset pagination, so we return the correct connection.
- Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(issues)
+ offset_pagination(issues)
else
issues
end
diff --git a/app/graphql/resolvers/members_resolver.rb b/app/graphql/resolvers/members_resolver.rb
index 523642e912f..cf51fd298bd 100644
--- a/app/graphql/resolvers/members_resolver.rb
+++ b/app/graphql/resolvers/members_resolver.rb
@@ -14,7 +14,9 @@ module Resolvers
def resolve_with_lookahead(**args)
authorize!(object)
- apply_lookahead(finder_class.new(object, current_user, params: args).execute)
+ relations = args.delete(:relations)
+
+ apply_lookahead(finder_class.new(object, current_user, params: args).execute(include_relations: relations))
end
private
diff --git a/app/graphql/resolvers/merge_request_pipelines_resolver.rb b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
index 6590dfdc78c..f84eedb4c3b 100644
--- a/app/graphql/resolvers/merge_request_pipelines_resolver.rb
+++ b/app/graphql/resolvers/merge_request_pipelines_resolver.rb
@@ -5,14 +5,32 @@ module Resolvers
class MergeRequestPipelinesResolver < BaseResolver
# The GraphQL type here gets defined in this include
include ::ResolvesPipelines
+ include ::CachingArrayResolver
alias_method :merge_request, :object
+ # Return at most 500 pipelines for each MR.
+ # Merge requests generally have many fewer pipelines than this.
+ def self.field_options
+ super.merge(max_page_size: 500)
+ end
+
def resolve(**args)
return unless project
- resolve_pipelines(project, args)
- .merge(merge_request.all_pipelines)
+ super
+ end
+
+ def query_for(args)
+ resolve_pipelines(project, args).merge(merge_request.all_pipelines)
+ end
+
+ def model_class
+ ::Ci::Pipeline
+ end
+
+ def query_input(**args)
+ args
end
def project
diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb
index cb4a76243ae..98c95565778 100644
--- a/app/graphql/resolvers/merge_requests_resolver.rb
+++ b/app/graphql/resolvers/merge_requests_resolver.rb
@@ -4,6 +4,8 @@ module Resolvers
class MergeRequestsResolver < BaseResolver
include ResolvesMergeRequests
+ type ::Types::MergeRequestType.connection_type, null: true
+
alias_method :project, :synchronized_object
def self.accept_assignee
@@ -18,6 +20,12 @@ module Resolvers
description: 'Username of the author'
end
+ def self.accept_reviewer
+ argument :reviewer_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the reviewer'
+ end
+
argument :iids, [GraphQL::STRING_TYPE],
required: false,
description: 'Array of IIDs of merge requests, for example `[1, 2]`'
@@ -52,7 +60,7 @@ module Resolvers
argument :sort, Types::MergeRequestSortEnum,
description: 'Sort merge requests by this criteria',
required: false,
- default_value: 'created_desc'
+ default_value: :created_desc
def self.single
::Resolvers::MergeRequestResolver
diff --git a/app/graphql/resolvers/project_members_resolver.rb b/app/graphql/resolvers/project_members_resolver.rb
index e64e8b845a5..659b12c2563 100644
--- a/app/graphql/resolvers/project_members_resolver.rb
+++ b/app/graphql/resolvers/project_members_resolver.rb
@@ -5,6 +5,11 @@ module Resolvers
class ProjectMembersResolver < MembersResolver
authorize :read_project_member
+ argument :relations, [Types::ProjectMemberRelationEnum],
+ description: 'Filter members by the given member relations',
+ required: false,
+ default_value: MembersFinder::DEFAULT_RELATIONS
+
private
def finder_class
diff --git a/app/graphql/resolvers/project_merge_requests_resolver.rb b/app/graphql/resolvers/project_merge_requests_resolver.rb
index bf082c0b182..830649d5e52 100644
--- a/app/graphql/resolvers/project_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/project_merge_requests_resolver.rb
@@ -5,5 +5,6 @@ module Resolvers
type ::Types::MergeRequestType.connection_type, null: true
accept_assignee
accept_author
+ accept_reviewer
end
end
diff --git a/app/graphql/resolvers/project_pipeline_resolver.rb b/app/graphql/resolvers/project_pipeline_resolver.rb
index 4cf47dbdc60..8bf4e0b08ef 100644
--- a/app/graphql/resolvers/project_pipeline_resolver.rb
+++ b/app/graphql/resolvers/project_pipeline_resolver.rb
@@ -12,7 +12,9 @@ module Resolvers
def resolve(iid:)
BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args|
- args[:key].all_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) }
+ finder = ::Ci::PipelinesFinder.new(project, context[:current_user], iids: iids)
+
+ finder.execute.each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) }
end
end
end
diff --git a/app/graphql/resolvers/project_pipeline_statistics_resolver.rb b/app/graphql/resolvers/project_pipeline_statistics_resolver.rb
new file mode 100644
index 00000000000..29ab9402f5b
--- /dev/null
+++ b/app/graphql/resolvers/project_pipeline_statistics_resolver.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ProjectPipelineStatisticsResolver < BaseResolver
+ type Types::Ci::AnalyticsType, null: true
+
+ def resolve
+ weekly_stats = Gitlab::Ci::Charts::WeekChart.new(object)
+ monthly_stats = Gitlab::Ci::Charts::MonthChart.new(object)
+ yearly_stats = Gitlab::Ci::Charts::YearChart.new(object)
+ pipeline_times = Gitlab::Ci::Charts::PipelineTime.new(object)
+
+ {
+ week_pipelines_labels: weekly_stats.labels,
+ week_pipelines_totals: weekly_stats.total,
+ week_pipelines_successful: weekly_stats.success,
+ month_pipelines_labels: monthly_stats.labels,
+ month_pipelines_totals: monthly_stats.total,
+ month_pipelines_successful: monthly_stats.success,
+ year_pipelines_labels: yearly_stats.labels,
+ year_pipelines_totals: yearly_stats.total,
+ year_pipelines_successful: yearly_stats.success,
+ pipeline_times_labels: pipeline_times.labels,
+ pipeline_times_values: pipeline_times.pipeline_times
+ }
+ end
+ end
+end
diff --git a/app/graphql/resolvers/projects/jira_imports_resolver.rb b/app/graphql/resolvers/projects/jira_imports_resolver.rb
deleted file mode 100644
index efd45c2c465..00000000000
--- a/app/graphql/resolvers/projects/jira_imports_resolver.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- module Projects
- class JiraImportsResolver < BaseResolver
- type Types::JiraImportType.connection_type, null: true
-
- include Gitlab::Graphql::Authorize::AuthorizeResource
-
- alias_method :project, :object
-
- def resolve(**args)
- authorize!(project)
-
- project.jira_imports
- end
-
- def authorized_resource?(project)
- context[:current_user].present? && Ability.allowed?(context[:current_user], :read_project, project)
- end
- end
- end
-end
diff --git a/app/graphql/resolvers/projects/services_resolver.rb b/app/graphql/resolvers/projects/services_resolver.rb
index 17d81e21c28..4f5a6cddbb3 100644
--- a/app/graphql/resolvers/projects/services_resolver.rb
+++ b/app/graphql/resolvers/projects/services_resolver.rb
@@ -3,9 +3,11 @@
module Resolvers
module Projects
class ServicesResolver < BaseResolver
+ prepend ManualAuthorization
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Projects::ServiceType.connection_type, null: true
+ authorize :admin_project
argument :active,
GraphQL::BOOLEAN_TYPE,
@@ -24,10 +26,6 @@ module Resolvers
services(args[:active], args[:type])
end
- def authorized_resource?(project)
- Ability.allowed?(context[:current_user], :admin_project, project)
- end
-
private
def services(active, type)
diff --git a/app/graphql/resolvers/review_requested_merge_requests_resolver.rb b/app/graphql/resolvers/review_requested_merge_requests_resolver.rb
new file mode 100644
index 00000000000..e0ab7b5b600
--- /dev/null
+++ b/app/graphql/resolvers/review_requested_merge_requests_resolver.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ReviewRequestedMergeRequestsResolver < UserMergeRequestsResolverBase
+ type ::Types::MergeRequestType.connection_type, null: true
+ accept_author
+ accept_assignee
+
+ def user_role
+ :reviewer
+ end
+ end
+end
diff --git a/app/graphql/resolvers/snippets/blobs_resolver.rb b/app/graphql/resolvers/snippets/blobs_resolver.rb
index 3a0dcb50faf..cfb1711aed4 100644
--- a/app/graphql/resolvers/snippets/blobs_resolver.rb
+++ b/app/graphql/resolvers/snippets/blobs_resolver.rb
@@ -3,9 +3,11 @@
module Resolvers
module Snippets
class BlobsResolver < BaseResolver
+ prepend ManualAuthorization
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Snippets::BlobType.connection_type, null: true
+ authorize :read_snippet
alias_method :snippet, :object
@@ -27,10 +29,6 @@ module Resolvers
end
end
- def authorized_resource?(snippet)
- Ability.allowed?(context[:current_user], :read_snippet, snippet)
- end
-
private
def transformed_blob_paths(paths)
diff --git a/app/graphql/resolvers/user_discussions_count_resolver.rb b/app/graphql/resolvers/user_discussions_count_resolver.rb
new file mode 100644
index 00000000000..115997ec666
--- /dev/null
+++ b/app/graphql/resolvers/user_discussions_count_resolver.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class UserDiscussionsCountResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type GraphQL::INT_TYPE, null: true
+
+ def resolve
+ authorize!(object)
+
+ BatchLoader::GraphQL.for(object.id).batch do |ids, loader, args|
+ counts = Note.count_for_collection(ids, object.class.name, 'COUNT(DISTINCT discussion_id) as count').index_by(&:noteable_id)
+
+ ids.each do |id|
+ loader.call(id, counts[id]&.count || 0)
+ end
+ end
+ end
+
+ def authorized_resource?(object)
+ ability = "read_#{object.class.name.underscore}".to_sym
+ context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/user_notes_count_resolver.rb b/app/graphql/resolvers/user_notes_count_resolver.rb
new file mode 100644
index 00000000000..2cb61104c18
--- /dev/null
+++ b/app/graphql/resolvers/user_notes_count_resolver.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class UserNotesCountResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type GraphQL::INT_TYPE, null: true
+
+ def resolve
+ authorize!(object)
+
+ BatchLoader::GraphQL.for(object.id).batch(key: :user_notes_count) do |ids, loader, args|
+ counts = Note.count_for_collection(ids, object.class.name).index_by(&:noteable_id)
+
+ ids.each do |id|
+ loader.call(id, counts[id]&.count || 0)
+ end
+ end
+ end
+
+ def authorized_resource?(object)
+ ability = "read_#{object.class.name.underscore}".to_sym
+ context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/users/group_count_resolver.rb b/app/graphql/resolvers/users/group_count_resolver.rb
index 5033c26554a..ebfe594d31d 100644
--- a/app/graphql/resolvers/users/group_count_resolver.rb
+++ b/app/graphql/resolvers/users/group_count_resolver.rb
@@ -3,6 +3,8 @@
module Resolvers
module Users
class GroupCountResolver < BaseResolver
+ type GraphQL::INT_TYPE, null: true
+
alias_method :user, :object
def resolve(**args)
diff --git a/app/graphql/resolvers/users_resolver.rb b/app/graphql/resolvers/users_resolver.rb
index f5838642141..a0ed076595d 100644
--- a/app/graphql/resolvers/users_resolver.rb
+++ b/app/graphql/resolvers/users_resolver.rb
@@ -17,7 +17,7 @@ module Resolvers
argument :sort, Types::SortEnum,
description: 'Sort users by this criteria',
required: false,
- default_value: 'created_desc'
+ default_value: :created_desc
argument :search, GraphQL::STRING_TYPE,
required: false,
diff --git a/app/graphql/types/alert_management/domain_filter_enum.rb b/app/graphql/types/alert_management/domain_filter_enum.rb
new file mode 100644
index 00000000000..58dbc8bb2cf
--- /dev/null
+++ b/app/graphql/types/alert_management/domain_filter_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module AlertManagement
+ class DomainFilterEnum < BaseEnum
+ graphql_name 'AlertManagementDomainFilter'
+ description 'Filters the alerts based on given domain'
+
+ value 'operations', description: 'Alerts for operations domain '
+ value 'threat_monitoring', description: 'Alerts for threat monitoring domain'
+ end
+ end
+end
diff --git a/app/graphql/types/alert_management/prometheus_integration_type.rb b/app/graphql/types/alert_management/prometheus_integration_type.rb
index f605e325b8b..79f265f2f1e 100644
--- a/app/graphql/types/alert_management/prometheus_integration_type.rb
+++ b/app/graphql/types/alert_management/prometheus_integration_type.rb
@@ -2,7 +2,7 @@
module Types
module AlertManagement
- class PrometheusIntegrationType < BaseObject
+ class PrometheusIntegrationType < ::Types::BaseObject
include ::Gitlab::Routing
graphql_name 'AlertManagementPrometheusIntegration'
diff --git a/app/graphql/types/award_emojis/award_emoji_type.rb b/app/graphql/types/award_emojis/award_emoji_type.rb
index fe7affa50cc..cd7a2f34ba6 100644
--- a/app/graphql/types/award_emojis/award_emoji_type.rb
+++ b/app/graphql/types/award_emojis/award_emoji_type.rb
@@ -38,10 +38,11 @@ module Types
field :user,
Types::UserType,
null: false,
- description: 'The user who awarded the emoji',
- resolve: -> (award_emoji, _args, _context) {
- Gitlab::Graphql::Loaders::BatchModelLoader.new(User, award_emoji.user_id).find
- }
+ description: 'The user who awarded the emoji'
+
+ def user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find
+ end
end
end
end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 5c8aabfe163..c4ce2cecd8b 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -12,6 +12,7 @@ module Types
def initialize(*args, **kwargs, &block)
@calls_gitaly = !!kwargs.delete(:calls_gitaly)
@constant_complexity = !!kwargs[:complexity]
+ @requires_argument = !!kwargs.delete(:requires_argument)
kwargs[:complexity] = field_complexity(kwargs[:resolver_class], kwargs[:complexity])
@feature_flag = kwargs[:feature_flag]
kwargs = check_feature_flag(kwargs)
@@ -20,6 +21,10 @@ module Types
super(*args, **kwargs, &block)
end
+ def requires_argument?
+ @requires_argument || arguments.values.any? { |argument| argument.type.non_null? }
+ end
+
# Based on https://github.com/rmosolgo/graphql-ruby/blob/v1.11.4/lib/graphql/schema/field.rb#L538-L563
# Modified to fix https://github.com/rmosolgo/graphql-ruby/issues/3113
def resolve_field(obj, args, ctx)
@@ -73,7 +78,7 @@ module Types
attr_reader :feature_flag
def feature_documentation_message(key, description)
- "#{description}. Available only when feature flag `#{key}` is enabled"
+ "#{description} Available only when feature flag `#{key}` is enabled."
end
def check_feature_flag(args)
diff --git a/app/graphql/types/base_interface.rb b/app/graphql/types/base_interface.rb
index 3451a195c33..4b1f3193136 100644
--- a/app/graphql/types/base_interface.rb
+++ b/app/graphql/types/base_interface.rb
@@ -3,5 +3,7 @@
module Types
module BaseInterface
include GraphQL::Schema::Interface
+
+ field_class ::Types::BaseField
end
end
diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb
index 6ee76b0d1f1..7999e77eb30 100644
--- a/app/graphql/types/board_list_type.rb
+++ b/app/graphql/types/board_list_type.rb
@@ -19,8 +19,7 @@ module Types
field :label, Types::LabelType, null: true,
description: 'Label of the list'
field :collapsed, GraphQL::BOOLEAN_TYPE, null: true,
- description: 'Indicates if list is collapsed for this user',
- resolve: -> (list, _args, ctx) { list.collapsed?(ctx[:current_user]) }
+ description: 'Indicates if list is collapsed for this user'
field :issues_count, GraphQL::INT_TYPE, null: true,
description: 'Count of issues in the list'
@@ -32,6 +31,10 @@ module Types
metadata[:size]
end
+ def collapsed
+ object.collapsed?(context[:current_user])
+ end
+
def metadata
strong_memoize(:metadata) do
list = self.object
diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb
index 2a7b318e283..f47c744d1bb 100644
--- a/app/graphql/types/board_type.rb
+++ b/app/graphql/types/board_type.rb
@@ -12,6 +12,12 @@ module Types
field :name, type: GraphQL::STRING_TYPE, null: true,
description: 'Name of the board'
+ field :hide_backlog_list, type: GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether or not backlog list is hidden'
+
+ field :hide_closed_list, type: GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether or not closed list is hidden'
+
field :lists,
Types::BoardListType.connection_type,
null: true,
diff --git a/app/graphql/types/ci/analytics_type.rb b/app/graphql/types/ci/analytics_type.rb
new file mode 100644
index 00000000000..c8b12c6a9b8
--- /dev/null
+++ b/app/graphql/types/ci/analytics_type.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class AnalyticsType < BaseObject
+ graphql_name 'PipelineAnalytics'
+
+ field :week_pipelines_totals, [GraphQL::INT_TYPE], null: true,
+ description: 'Total weekly pipeline count'
+ field :week_pipelines_successful, [GraphQL::INT_TYPE], null: true,
+ description: 'Total weekly successful pipeline count'
+ field :week_pipelines_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Labels for the weekly pipeline count'
+ field :month_pipelines_totals, [GraphQL::INT_TYPE], null: true,
+ description: 'Total monthly pipeline count'
+ field :month_pipelines_successful, [GraphQL::INT_TYPE], null: true,
+ description: 'Total monthly successful pipeline count'
+ field :month_pipelines_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Labels for the monthly pipeline count'
+ field :year_pipelines_totals, [GraphQL::INT_TYPE], null: true,
+ description: 'Total yearly pipeline count'
+ field :year_pipelines_successful, [GraphQL::INT_TYPE], null: true,
+ description: 'Total yearly successful pipeline count'
+ field :year_pipelines_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Labels for the yearly pipeline count'
+ field :pipeline_times_values, [GraphQL::INT_TYPE], null: true,
+ description: 'Pipeline times'
+ field :pipeline_times_labels, [GraphQL::STRING_TYPE], null: true,
+ description: 'Pipeline times labels'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/ci_cd_setting_type.rb b/app/graphql/types/ci/ci_cd_setting_type.rb
new file mode 100644
index 00000000000..207c37f9538
--- /dev/null
+++ b/app/graphql/types/ci/ci_cd_setting_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class CiCdSettingType < BaseObject
+ graphql_name 'ProjectCiCdSetting'
+
+ authorize :admin_project
+
+ field :merge_pipelines_enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether merge pipelines are enabled.',
+ method: :merge_pipelines_enabled?
+ field :merge_trains_enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Whether merge trains are enabled.',
+ method: :merge_trains_enabled?
+ field :project, Types::ProjectType, null: true,
+ description: 'Project the CI/CD settings belong to.'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/config_type.rb b/app/graphql/types/ci/config/config_type.rb
new file mode 100644
index 00000000000..e54b345f3d3
--- /dev/null
+++ b/app/graphql/types/ci/config/config_type.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class ConfigType < BaseObject
+ graphql_name 'CiConfig'
+
+ field :errors, [GraphQL::STRING_TYPE], null: true,
+ description: 'Linting errors'
+ field :merged_yaml, GraphQL::STRING_TYPE, null: true,
+ description: 'Merged CI config YAML'
+ field :stages, [Types::Ci::Config::StageType], null: true,
+ description: 'Stages of the pipeline'
+ field :status, Types::Ci::Config::StatusEnum, null: true,
+ description: 'Status of linting, can be either valid or invalid'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/group_type.rb b/app/graphql/types/ci/config/group_type.rb
new file mode 100644
index 00000000000..8b0db2934a4
--- /dev/null
+++ b/app/graphql/types/ci/config/group_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class GroupType < BaseObject
+ graphql_name 'CiConfigGroup'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job group'
+ field :jobs, [Types::Ci::Config::JobType], null: true,
+ description: 'Jobs in group'
+ field :size, GraphQL::INT_TYPE, null: true,
+ description: 'Size of the job group'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/job_type.rb b/app/graphql/types/ci/config/job_type.rb
new file mode 100644
index 00000000000..59bcbd9ef49
--- /dev/null
+++ b/app/graphql/types/ci/config/job_type.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class JobType < BaseObject
+ graphql_name 'CiConfigJob'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job'
+ field :group_name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job group'
+ field :stage, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the job stage'
+ field :needs, [Types::Ci::Config::NeedType], null: true,
+ description: 'Builds that must complete before the jobs run'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/need_type.rb b/app/graphql/types/ci/config/need_type.rb
new file mode 100644
index 00000000000..a442450b9ae
--- /dev/null
+++ b/app/graphql/types/ci/config/need_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class NeedType < BaseObject
+ graphql_name 'CiConfigNeed'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the need'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/stage_type.rb b/app/graphql/types/ci/config/stage_type.rb
new file mode 100644
index 00000000000..20618bc41f8
--- /dev/null
+++ b/app/graphql/types/ci/config/stage_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ module Config
+ class StageType < BaseObject
+ graphql_name 'CiConfigStage'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the stage'
+ field :groups, [Types::Ci::Config::GroupType], null: true,
+ description: 'Groups of jobs for the stage'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/config/status_enum.rb b/app/graphql/types/ci/config/status_enum.rb
new file mode 100644
index 00000000000..92b04c61679
--- /dev/null
+++ b/app/graphql/types/ci/config/status_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Config
+ class StatusEnum < BaseEnum
+ graphql_name 'CiConfigStatus'
+ description 'Values for YAML processor result'
+
+ value 'VALID', 'The configuration file is valid', value: :valid
+ value 'INVALID', 'The configuration file is not valid', value: :invalid
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/detailed_status_type.rb b/app/graphql/types/ci/detailed_status_type.rb
index 6d8af400ac4..80d73e9b174 100644
--- a/app/graphql/types/ci/detailed_status_type.rb
+++ b/app/graphql/types/ci/detailed_status_type.rb
@@ -25,20 +25,22 @@ module Types
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.action_icon,
- method: obj.action_method,
- path: obj.action_path,
- title: obj.action_title
- }
- else
- nil
- end
- }
+ calls_gitaly: true,
+ description: 'Action information for the status. This includes method, button title, icon, path, and title'
+
+ def action
+ if object.has_action?
+ {
+ button_title: object.action_button_title,
+ icon: object.action_icon,
+ method: object.action_method,
+ path: object.action_path,
+ title: object.action_title
+ }
+ else
+ nil
+ end
+ end
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/ci/group_type.rb b/app/graphql/types/ci/group_type.rb
index d930ae311b7..03fd50d5dbb 100644
--- a/app/graphql/types/ci/group_type.rb
+++ b/app/graphql/types/ci/group_type.rb
@@ -13,8 +13,11 @@ module Types
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]) }
+ description: 'Detailed status of the group'
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
end
end
end
diff --git a/app/graphql/types/ci/job_artifact_file_type_enum.rb b/app/graphql/types/ci/job_artifact_file_type_enum.rb
new file mode 100644
index 00000000000..4b484dec590
--- /dev/null
+++ b/app/graphql/types/ci/job_artifact_file_type_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class JobArtifactFileTypeEnum < BaseEnum
+ graphql_name 'JobArtifactFileType'
+
+ ::Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS.keys.each do |file_type|
+ value file_type.to_s.upcase, value: file_type.to_s
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_artifact_type.rb b/app/graphql/types/ci/job_artifact_type.rb
new file mode 100644
index 00000000000..c34a12dcc61
--- /dev/null
+++ b/app/graphql/types/ci/job_artifact_type.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class JobArtifactType < BaseObject
+ graphql_name 'CiJobArtifact'
+
+ field :download_path, GraphQL::STRING_TYPE, null: true,
+ description: "URL for downloading the artifact's file"
+
+ field :file_type, ::Types::Ci::JobArtifactFileTypeEnum, null: true,
+ description: 'File type of the artifact'
+
+ def download_path
+ ::Gitlab::Routing.url_helpers.download_project_job_artifacts_path(
+ object.project,
+ object.job,
+ file_type: object.file_type
+ )
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index feaff4e81d8..5b6e8fe8567 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -6,18 +6,32 @@ module Types
class JobType < BaseObject
graphql_name 'CiJob'
- field :pipeline, Types::Ci::PipelineType, null: false,
- description: 'Pipeline the job belongs to',
- resolve: -> (build, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, build.pipeline_id).find }
+ field :pipeline, Types::Ci::PipelineType, null: true,
+ description: 'Pipeline the job belongs to'
field :name, GraphQL::STRING_TYPE, null: true,
- description: 'Name of the job'
+ description: 'Name of the job'
field :needs, JobType.connection_type, null: true,
- description: 'Builds that must complete before the jobs run'
+ 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]) }
+ description: 'Detailed status of the job'
field :scheduled_at, Types::TimeType, null: true,
- description: 'Schedule for the build'
+ description: 'Schedule for the build'
+ field :artifacts, Types::Ci::JobArtifactType.connection_type, null: true,
+ description: 'Artifacts generated by the job'
+
+ def pipeline
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
+ end
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
+
+ def artifacts
+ if object.is_a?(::Ci::Build)
+ object.job_artifacts
+ end
+ end
end
end
end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index c25db39f600..4709d5e8dd6 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -27,8 +27,7 @@ module Types
description: "Status of the pipeline (#{::Ci::Pipeline.all_state_names.compact.join(', ').upcase})"
field :detailed_status, Types::Ci::DetailedStatusType, null: false,
- description: 'Detailed status of the pipeline',
- resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
+ description: 'Detailed status of the pipeline'
field :config_source, PipelineConfigSourceEnum, null: true,
description: "Config source of the pipeline (#{::Enums::Ci::Pipeline.config_sources.keys.join(', ').upcase})"
@@ -60,8 +59,7 @@ module Types
resolver: Resolvers::Ci::PipelineStagesResolver
field :user, Types::UserType, null: true,
- description: 'Pipeline user',
- resolve: -> (pipeline, _args, _context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, pipeline.user_id).find }
+ description: 'Pipeline user'
field :retryable, GraphQL::BOOLEAN_TYPE,
description: 'Specifies if a pipeline can be retried',
@@ -91,11 +89,25 @@ module Types
method: :triggered_by_pipeline
field :path, GraphQL::STRING_TYPE, null: true,
- description: "Relative path to the pipeline's page",
- resolve: -> (obj, _args, _ctx) { ::Gitlab::Routing.url_helpers.project_pipeline_path(obj.project, obj) }
+ description: "Relative path to the pipeline's page"
field :project, Types::ProjectType, null: true,
description: 'Project the pipeline belongs to'
+
+ field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?,
+ description: 'Indicates if the pipeline is active'
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
+
+ def user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find
+ end
+
+ def path
+ ::Gitlab::Routing.url_helpers.project_pipeline_path(object.project, object)
+ end
end
end
end
diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb
index fc2c72d0d06..fd0bde90836 100644
--- a/app/graphql/types/ci/stage_type.rb
+++ b/app/graphql/types/ci/stage_type.rb
@@ -11,8 +11,11 @@ module Types
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]) }
+ description: 'Detailed status of the stage'
+
+ def detailed_status
+ object.detailed_status(context[:current_user])
+ end
end
end
end
diff --git a/app/graphql/types/commit_type.rb b/app/graphql/types/commit_type.rb
index c24b47f08ef..37d19b4148b 100644
--- a/app/graphql/types/commit_type.rb
+++ b/app/graphql/types/commit_type.rb
@@ -12,6 +12,8 @@ module Types
description: 'ID (global ID) of the commit'
field :sha, type: GraphQL::STRING_TYPE, null: false,
description: 'SHA1 ID of the commit'
+ field :short_id, type: GraphQL::STRING_TYPE, null: false,
+ description: 'Short SHA1 ID of the commit'
field :title, type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
description: 'Title of the commit message'
markdown_field :title_html, null: true
@@ -31,10 +33,7 @@ module Types
field :author_name, type: GraphQL::STRING_TYPE, null: true,
description: 'Commit authors name'
field :author_gravatar, type: GraphQL::STRING_TYPE, null: true,
- description: 'Commit authors gravatar',
- resolve: -> (commit, args, context) do
- GravatarService.new.execute(commit.author_email, 40)
- end
+ description: 'Commit authors gravatar'
# models/commit lazy loads the author by email
field :author, type: Types::UserType, null: true,
@@ -44,5 +43,9 @@ module Types
null: true,
description: 'Pipelines of the commit ordered latest first',
resolver: Resolvers::CommitPipelinesResolver
+
+ def author_gravatar
+ GravatarService.new.execute(object.author_email, 40)
+ end
end
end
diff --git a/app/graphql/types/concerns/gitlab_style_deprecations.rb b/app/graphql/types/concerns/gitlab_style_deprecations.rb
index 2c932f4214b..9f087f3812d 100644
--- a/app/graphql/types/concerns/gitlab_style_deprecations.rb
+++ b/app/graphql/types/concerns/gitlab_style_deprecations.rb
@@ -23,8 +23,8 @@ module GitlabStyleDeprecations
raise ArgumentError, '`milestone` must be a `String`' unless milestone.is_a?(String)
deprecated_in = "Deprecated in #{milestone}"
- kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}"
- kwargs[:description] += ". #{deprecated_in}: #{reason}" if kwargs[:description]
+ kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}."
+ kwargs[:description] += " #{deprecated_in}: #{reason}." if kwargs[:description]
kwargs
end
diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb
index 45d19fdbc50..8735f8a173d 100644
--- a/app/graphql/types/container_repository_type.rb
+++ b/app/graphql/types/container_repository_type.rb
@@ -19,9 +19,14 @@ module Types
field :status, Types::ContainerRepositoryStatusEnum, null: true, description: 'Status of the container repository.'
field :tags_count, GraphQL::INT_TYPE, null: false, description: 'Number of tags associated with this image.'
field :can_delete, GraphQL::BOOLEAN_TYPE, null: false, description: 'Can the current user delete the container repository.'
+ field :project, Types::ProjectType, null: false, description: 'Project of the container registry'
def can_delete
Ability.allowed?(current_user, :update_container_image, object)
end
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ 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 9af1f4db425..26fbac15b30 100644
--- a/app/graphql/types/design_management/design_collection_type.rb
+++ b/app/graphql/types/design_management/design_collection_type.rb
@@ -2,7 +2,7 @@
module Types
module DesignManagement
- class DesignCollectionType < BaseObject
+ class DesignCollectionType < ::Types::BaseObject
graphql_name 'DesignCollection'
description 'A collection of designs'
diff --git a/app/graphql/types/error_tracking/sentry_error_collection_type.rb b/app/graphql/types/error_tracking/sentry_error_collection_type.rb
index 798e0433d06..49d5d62c860 100644
--- a/app/graphql/types/error_tracking/sentry_error_collection_type.rb
+++ b/app/graphql/types/error_tracking/sentry_error_collection_type.rb
@@ -9,27 +9,12 @@ module Types
authorize :read_sentry_issue
field :errors,
- Types::ErrorTracking::SentryErrorType.connection_type,
- connection: false,
- null: true,
description: "Collection of Sentry Errors",
- extensions: [Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension],
- resolver: Resolvers::ErrorTracking::SentryErrorsResolver do
- argument :search_term,
- String,
- description: 'Search query for the Sentry error details',
- required: false
- argument :sort,
- String,
- description: 'Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default',
- required: false
- end
- field :detailed_error, Types::ErrorTracking::SentryDetailedErrorType,
- null: true,
+ resolver: Resolvers::ErrorTracking::SentryErrorsResolver
+ field :detailed_error,
description: 'Detailed version of a Sentry error on the project',
resolver: Resolvers::ErrorTracking::SentryDetailedErrorResolver
- field :error_stack_trace, Types::ErrorTracking::SentryErrorStackTraceType,
- null: true,
+ field :error_stack_trace,
description: 'Stack Trace of Sentry Error',
resolver: Resolvers::ErrorTracking::SentryErrorStackTraceResolver
field :external_url,
diff --git a/app/graphql/types/group_invitation_type.rb b/app/graphql/types/group_invitation_type.rb
index 0372ce178ff..efb0c8a41c8 100644
--- a/app/graphql/types/group_invitation_type.rb
+++ b/app/graphql/types/group_invitation_type.rb
@@ -11,7 +11,10 @@ module Types
description 'Represents a Group Invitation'
field :group, Types::GroupType, null: true,
- description: 'Group that a User is invited to',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.source_id).find }
+ description: 'Group that a User is invited to'
+
+ def group
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find
+ end
end
end
diff --git a/app/graphql/types/group_member_relation_enum.rb b/app/graphql/types/group_member_relation_enum.rb
new file mode 100644
index 00000000000..aa2e73d4944
--- /dev/null
+++ b/app/graphql/types/group_member_relation_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class GroupMemberRelationEnum < BaseEnum
+ graphql_name 'GroupMemberRelation'
+ description 'Group member relation'
+
+ ::GroupMembersFinder::RELATIONS.each do |member_relation|
+ value member_relation.to_s.upcase, value: member_relation, description: "#{member_relation.to_s.titleize} members"
+ end
+ end
+end
diff --git a/app/graphql/types/group_member_type.rb b/app/graphql/types/group_member_type.rb
index 6cca0a50647..204da5a302a 100644
--- a/app/graphql/types/group_member_type.rb
+++ b/app/graphql/types/group_member_type.rb
@@ -11,7 +11,10 @@ module Types
description 'Represents a Group Membership'
field :group, Types::GroupType, null: true,
- description: 'Group that a User is a member of',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.source_id).find }
+ description: 'Group that a User is a member of'
+
+ def group
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find
+ end
end
end
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index fb028184488..0ee8a19c1a3 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -12,10 +12,7 @@ module Types
description: 'Web URL of the group'
field :avatar_url, GraphQL::STRING_TYPE, null: true,
- description: 'Avatar URL of the group',
- resolve: -> (group, args, ctx) do
- group.avatar_url(only_path: false)
- end
+ description: 'Avatar URL of the group'
field :custom_emoji, Types::CustomEmojiType.connection_type, null: true,
description: 'Custom emoji within this namespace',
@@ -44,8 +41,7 @@ module Types
description: 'Indicates if a group is disabled from getting mentioned'
field :parent, GroupType, null: true,
- description: 'Parent group',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find }
+ description: 'Parent group'
field :issues,
Types::IssueType.connection_type,
@@ -92,10 +88,13 @@ module Types
field :container_repositories,
Types::ContainerRepositoryType.connection_type,
null: true,
- description: 'Container repositories of the project',
+ description: 'Container repositories of the group',
resolver: Resolvers::ContainerRepositoriesResolver,
authorize: :read_container_image
+ field :container_repositories_count, GraphQL::INT_TYPE, null: false,
+ description: 'Number of container repositories in the group'
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
LabelsFinder
@@ -120,6 +119,18 @@ module Types
.execute
end
+ def avatar_url
+ object.avatar_url(only_path: false)
+ end
+
+ def parent
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.parent_id).find
+ end
+
+ def container_repositories_count
+ group.container_repositories.size
+ end
+
private
def group
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 49c84f75e1a..83b8a834801 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -61,9 +61,11 @@ module Types
field :downvotes, GraphQL::INT_TYPE, null: false,
description: 'Number of downvotes the issue has received'
field :user_notes_count, GraphQL::INT_TYPE, null: false,
- description: 'Number of user notes of the issue'
+ description: 'Number of user notes of the issue',
+ resolver: Resolvers::UserNotesCountResolver
field :user_discussions_count, GraphQL::INT_TYPE, null: false,
- description: 'Number of user discussions in the issue'
+ description: 'Number of user discussions in the issue',
+ resolver: Resolvers::UserDiscussionsCountResolver
field :web_path, GraphQL::STRING_TYPE, null: false, method: :issue_path,
description: 'Web path of the issue'
field :web_url, GraphQL::STRING_TYPE, null: false,
@@ -119,26 +121,6 @@ module Types
field :moved_to, Types::IssueType, null: true,
description: 'Updated Issue after it got moved to another project'
- def user_notes_count
- BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_notes_count) do |ids, loader, args|
- counts = Note.count_for_collection(ids, 'Issue').index_by(&:noteable_id)
-
- ids.each do |id|
- loader.call(id, counts[id]&.count || 0)
- end
- end
- end
-
- def user_discussions_count
- BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_discussions_count) do |ids, loader, args|
- counts = Note.count_for_collection(ids, 'Issue', 'COUNT(DISTINCT discussion_id) as count').index_by(&:noteable_id)
-
- ids.each do |id|
- loader.call(id, counts[id]&.count || 0)
- end
- end
- end
-
def author
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
end
diff --git a/app/graphql/types/jira_import_type.rb b/app/graphql/types/jira_import_type.rb
index cf58a53b40d..b3854487cec 100644
--- a/app/graphql/types/jira_import_type.rb
+++ b/app/graphql/types/jira_import_type.rb
@@ -2,8 +2,7 @@
module Types
# rubocop: disable Graphql/AuthorizeTypes
- # Authorization is at project level for owners or admins,
- # so it is added directly to the Resolvers::JiraImportsResolver
+ # Authorization is at project level for owners or admins
class JiraImportType < BaseObject
graphql_name 'JiraImport'
diff --git a/app/graphql/types/jira_users_mapping_input_type.rb b/app/graphql/types/jira_users_mapping_input_type.rb
index 61cf1474493..d5b4b2f618a 100644
--- a/app/graphql/types/jira_users_mapping_input_type.rb
+++ b/app/graphql/types/jira_users_mapping_input_type.rb
@@ -8,7 +8,7 @@ module Types
argument :jira_account_id,
GraphQL::STRING_TYPE,
required: true,
- description: 'Jira account id of the user'
+ description: 'Jira account ID of the user'
argument :gitlab_id,
GraphQL::INT_TYPE,
required: false,
diff --git a/app/graphql/types/merge_request_connection_type.rb b/app/graphql/types/merge_request_connection_type.rb
new file mode 100644
index 00000000000..da06bb86929
--- /dev/null
+++ b/app/graphql/types/merge_request_connection_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class MergeRequestConnectionType < Types::CountableConnectionType
+ field :total_time_to_merge, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Total sum of time to merge, in seconds, for the collection of merge requests'
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def total_time_to_merge
+ object.items.reorder(nil).total_time_to_merge
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index e68d6706c43..816160e58f7 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -4,7 +4,7 @@ module Types
class MergeRequestType < BaseObject
graphql_name 'MergeRequest'
- connection_type_class(Types::CountableConnectionType)
+ connection_type_class(Types::MergeRequestConnectionType)
implements(Types::Notes::NoteableType)
implements(Types::CurrentUserTodos)
@@ -49,6 +49,8 @@ module Types
description: 'ID of the merge request target project'
field :source_branch, GraphQL::STRING_TYPE, null: false,
description: 'Source branch of the merge request'
+ field :source_branch_protected, GraphQL::BOOLEAN_TYPE, null: false, calls_gitaly: true,
+ description: 'Indicates if the source branch is protected'
field :target_branch, GraphQL::STRING_TYPE, null: false,
description: 'Target branch of the merge request'
field :work_in_progress, GraphQL::BOOLEAN_TYPE, method: :work_in_progress?, null: false,
@@ -67,9 +69,11 @@ module Types
field :merge_commit_sha, GraphQL::STRING_TYPE, null: true,
description: 'SHA of the merge request commit (set once merged)'
field :user_notes_count, GraphQL::INT_TYPE, null: true,
- description: 'User notes count of the merge request'
+ description: 'User notes count of the merge request',
+ resolver: Resolvers::UserNotesCountResolver
field :user_discussions_count, GraphQL::INT_TYPE, null: true,
- description: 'Number of user discussions in the merge request'
+ description: 'Number of user discussions in the merge request',
+ resolver: Resolvers::UserDiscussionsCountResolver
field :should_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :should_remove_source_branch?, null: true,
description: 'Indicates if the source branch of the merge request will be deleted after merge'
field :force_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :force_remove_source_branch?, null: true,
@@ -90,6 +94,8 @@ module Types
description: 'Indicates if there is a rebase currently in progress for the merge request'
field :default_merge_commit_message, GraphQL::STRING_TYPE, null: true,
description: 'Default merge commit message of the merge request'
+ field :default_merge_commit_message_with_description, GraphQL::STRING_TYPE, null: true,
+ description: 'Default merge commit message of the merge request with description'
field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false,
description: 'Indicates if a merge is currently occurring'
field :source_branch_exists, GraphQL::BOOLEAN_TYPE,
@@ -113,7 +119,7 @@ module Types
description: 'The pipeline running on the branch HEAD of the merge request'
field :pipelines,
null: true,
- description: 'Pipelines for the merge request',
+ description: 'Pipelines for the merge request. Note: for performance reasons, no more than the most recent 500 pipelines will be returned.',
resolver: Resolvers::MergeRequestPipelinesResolver
field :milestone, Types::MilestoneType, null: true,
@@ -130,8 +136,7 @@ module Types
description: 'Labels of the merge request'
field :discussion_locked, GraphQL::BOOLEAN_TYPE,
description: 'Indicates if comments on the merge request are locked to members only',
- null: false,
- resolve: -> (obj, _args, _ctx) { !!obj.discussion_locked }
+ null: false
field :time_estimate, GraphQL::INT_TYPE, null: false,
description: 'Time estimate of the merge request'
field :total_time_spent, GraphQL::INT_TYPE, null: false,
@@ -152,6 +157,18 @@ module Types
field :approved_by, Types::UserType.connection_type, null: true,
description: 'Users who approved the merge request'
+ field :squash_on_merge, GraphQL::BOOLEAN_TYPE, null: false, method: :squash_on_merge?,
+ description: 'Indicates if squash on merge is enabled'
+ field :available_auto_merge_strategies, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true,
+ description: 'Array of available auto merge strategies'
+ field :has_ci, GraphQL::BOOLEAN_TYPE, null: false, method: :has_ci?,
+ description: 'Indicates if the merge request has CI'
+ field :mergeable, GraphQL::BOOLEAN_TYPE, null: false, method: :mergeable?, calls_gitaly: true,
+ description: 'Indicates if the merge request is mergeable'
+ field :commits_without_merge_commits, Types::CommitType.connection_type, null: true,
+ calls_gitaly: true, description: 'Merge request commits excluding merge commits'
+ field :security_auto_fix, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Indicates if the merge request is created by @GitLab-Security-Bot.'
def approved_by
object.approved_by_users
@@ -194,6 +211,31 @@ module Types
def commit_count
object&.metrics&.commits_count
end
+
+ def source_branch_protected
+ object.source_project.present? && ProtectedBranch.protected?(object.source_project, object.source_branch)
+ end
+
+ def discussion_locked
+ !!object.discussion_locked
+ end
+
+ def default_merge_commit_message_with_description
+ object.default_merge_commit_message(include_description: true)
+ end
+
+ def available_auto_merge_strategies
+ AutoMergeService.new(object.project, current_user).available_strategies(object)
+ end
+
+ def commits_without_merge_commits
+ object.recent_commits.without_merge_commits
+ end
+
+ def security_auto_fix
+ object.author == User.security_bot
+ 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 75ccac6d590..9eea81c9d3e 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -31,6 +31,7 @@ module Types
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::CustomEmoji::Create, feature_flag: :custom_emoji
mount_mutation Mutations::Discussions::ToggleResolve
+ mount_mutation Mutations::Environments::CanaryIngress::Update
mount_mutation Mutations::Issues::Create
mount_mutation Mutations::Issues::SetAssignees
mount_mutation Mutations::Issues::SetConfidential
@@ -65,6 +66,8 @@ module Types
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Releases::Create
+ mount_mutation Mutations::Releases::Update
+ mount_mutation Mutations::Releases::Delete
mount_mutation Mutations::Terraform::State::Delete
mount_mutation Mutations::Terraform::State::Lock
mount_mutation Mutations::Terraform::State::Unlock
@@ -84,6 +87,7 @@ module Types
mount_mutation Mutations::DesignManagement::Move
mount_mutation Mutations::ContainerExpirationPolicies::Update
mount_mutation Mutations::ContainerRepositories::Destroy
+ mount_mutation Mutations::ContainerRepositories::DestroyTags
mount_mutation Mutations::Ci::PipelineCancel
mount_mutation Mutations::Ci::PipelineDestroy
mount_mutation Mutations::Ci::PipelineRetry
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index fbdf049b755..4dec6f4c5e6 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -21,6 +21,7 @@ module Types
field :description, GraphQL::STRING_TYPE, null: true,
description: 'Description of the namespace'
markdown_field :description_html, null: true
+
field :visibility, GraphQL::STRING_TYPE, null: true,
description: 'Visibility of the namespace'
field :lfs_enabled, GraphQL::BOOLEAN_TYPE, null: true, method: :lfs_enabled?,
@@ -30,12 +31,15 @@ module Types
field :root_storage_statistics, Types::RootStorageStatisticsType,
null: true,
- description: 'Aggregated storage statistics of the namespace. Only available for root namespaces',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchRootStorageStatisticsLoader.new(obj.id).find }
+ description: 'Aggregated storage statistics of the namespace. Only available for root namespaces'
field :projects, Types::ProjectType.connection_type, null: false,
description: 'Projects within this namespace',
resolver: ::Resolvers::NamespaceProjectsResolver
+
+ def root_storage_statistics
+ Gitlab::Graphql::Loaders::BatchRootStorageStatisticsLoader.new(object.id).find
+ end
end
end
diff --git a/app/graphql/types/notes/diff_position_type.rb b/app/graphql/types/notes/diff_position_type.rb
index cc00feba2e6..13d9be49484 100644
--- a/app/graphql/types/notes/diff_position_type.rb
+++ b/app/graphql/types/notes/diff_position_type.rb
@@ -21,25 +21,43 @@ module Types
# Fields for text positions
field :old_line, GraphQL::INT_TYPE, null: true,
- description: 'Line on start SHA that was changed',
- resolve: -> (position, _args, _ctx) { position.old_line if position.on_text? }
+ description: 'Line on start SHA that was changed'
field :new_line, GraphQL::INT_TYPE, null: true,
- description: 'Line on HEAD SHA that was changed',
- resolve: -> (position, _args, _ctx) { position.new_line if position.on_text? }
+ description: 'Line on HEAD SHA that was changed'
# Fields for image positions
field :x, GraphQL::INT_TYPE, null: true,
- description: 'X position of the note',
- resolve: -> (position, _args, _ctx) { position.x if position.on_image? }
+ description: 'X position of the note'
field :y, GraphQL::INT_TYPE, null: true,
- description: 'Y position of the note',
- resolve: -> (position, _args, _ctx) { position.y if position.on_image? }
+ description: 'Y position of the note'
field :width, GraphQL::INT_TYPE, null: true,
- description: 'Total width of the image',
- resolve: -> (position, _args, _ctx) { position.width if position.on_image? }
+ description: 'Total width of the image'
field :height, GraphQL::INT_TYPE, null: true,
- description: 'Total height of the image',
- resolve: -> (position, _args, _ctx) { position.height if position.on_image? }
+ description: 'Total height of the image'
+
+ def old_line
+ object.old_line if object.on_text?
+ end
+
+ def new_line
+ object.new_line if object.on_text?
+ end
+
+ def x
+ object.x if object.on_image?
+ end
+
+ def y
+ object.y if object.on_image?
+ end
+
+ def width
+ object.width if object.on_image?
+ end
+
+ def height
+ object.height if object.on_image?
+ end
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index 5d41f0032bd..f4e05e19eca 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -16,13 +16,11 @@ module Types
field :project, Types::ProjectType,
null: true,
- description: 'Project associated with the note',
- resolve: -> (note, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, note.project_id).find }
+ description: 'Project associated with the note'
field :author, Types::UserType,
null: false,
- description: 'User who wrote this note',
- resolve: -> (note, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, note.author_id).find }
+ description: 'User who wrote this note'
field :system, GraphQL::BOOLEAN_TYPE,
null: false,
@@ -52,6 +50,14 @@ module Types
def system_note_icon_name
SystemNoteHelper.system_note_icon_name(object) if object.system?
end
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
end
end
end
diff --git a/app/graphql/types/permission_types/merge_request.rb b/app/graphql/types/permission_types/merge_request.rb
index e9c89b0c92e..52c11fe5588 100644
--- a/app/graphql/types/permission_types/merge_request.rb
+++ b/app/graphql/types/permission_types/merge_request.rb
@@ -19,7 +19,9 @@ module Types
permission_field field_name, method: :"can_#{field_name}?", calls_gitaly: true
end
- permission_field :can_merge, calls_gitaly: true, resolve: -> (object, args, context) do
+ permission_field :can_merge, calls_gitaly: true
+
+ def can_merge
object.can_be_merged_by?(context[:current_user])
end
end
diff --git a/app/graphql/types/project_member_relation_enum.rb b/app/graphql/types/project_member_relation_enum.rb
new file mode 100644
index 00000000000..fbad23b956f
--- /dev/null
+++ b/app/graphql/types/project_member_relation_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class ProjectMemberRelationEnum < BaseEnum
+ graphql_name 'ProjectMemberRelation'
+ description 'Project member relation'
+
+ ::MembersFinder::RELATIONS.each do |member_relation|
+ value member_relation.to_s.upcase, value: member_relation, description: "#{member_relation.to_s.titleize} members"
+ end
+ end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 5a436886117..a7d9548610e 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -67,33 +67,25 @@ module Types
description: 'E-mail address of the service desk.'
field :avatar_url, GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
- description: 'URL to avatar image file of the project',
- resolve: -> (project, args, ctx) do
- project.avatar_url(only_path: false)
- end
+ description: 'URL to avatar image file of the project'
%i[issues merge_requests wiki snippets].each do |feature|
field "#{feature}_enabled", GraphQL::BOOLEAN_TYPE, null: true,
- description: "Indicates if #{feature.to_s.titleize.pluralize} are enabled for the current user",
- resolve: -> (project, args, ctx) do
- project.feature_available?(feature, ctx[:current_user])
- end
+ description: "Indicates if #{feature.to_s.titleize.pluralize} are enabled for the current user"
+
+ define_method "#{feature}_enabled" do
+ object.feature_available?(feature, context[:current_user])
+ end
end
field :jobs_enabled, GraphQL::BOOLEAN_TYPE, null: true,
- description: 'Indicates if CI/CD pipeline jobs are enabled for the current user',
- resolve: -> (project, args, ctx) do
- project.feature_available?(:builds, ctx[:current_user])
- end
+ description: 'Indicates if CI/CD pipeline jobs are enabled for the current user'
field :public_jobs, GraphQL::BOOLEAN_TYPE, method: :public_builds, null: true,
description: 'Indicates if there is public access to pipelines and job details of the project, including output logs and artifacts'
field :open_issues_count, GraphQL::INT_TYPE, null: true,
- description: 'Number of open issues for the project',
- resolve: -> (project, args, ctx) do
- project.open_issues_count if project.feature_available?(:issues, ctx[:current_user])
- end
+ description: 'Number of open issues for the project'
field :import_status, GraphQL::STRING_TYPE, null: true,
description: 'Status of import background job of the project'
@@ -115,6 +107,8 @@ module Types
description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically'
field :suggestion_commit_message, GraphQL::STRING_TYPE, null: true,
description: 'The commit message used to apply merge request suggestions'
+ field :squash_read_only, GraphQL::BOOLEAN_TYPE, null: false, method: :squash_readonly?,
+ description: 'Indicates if squash readonly is enabled'
field :namespace, Types::NamespaceType, null: true,
description: 'Namespace of the project'
@@ -123,8 +117,7 @@ module Types
field :statistics, Types::ProjectStatisticsType,
null: true,
- description: 'Statistics of the project',
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(obj.id).find }
+ description: 'Statistics of the project'
field :repository, Types::RepositoryType, null: true,
description: 'Git repository of the project'
@@ -198,6 +191,11 @@ module Types
description: 'Build pipeline of the project',
resolver: Resolvers::ProjectPipelineResolver
+ field :ci_cd_settings,
+ Types::Ci::CiCdSettingType,
+ null: true,
+ description: 'CI/CD settings for the project'
+
field :sentry_detailed_error,
Types::ErrorTracking::SentryDetailedErrorType,
null: true,
@@ -238,8 +236,7 @@ module Types
field :jira_imports,
Types::JiraImportType.connection_type,
null: true,
- description: 'Jira imports into the project',
- resolver: Resolvers::Projects::JiraImportsResolver
+ description: 'Jira imports into the project'
field :services,
Types::Projects::ServiceType.connection_type,
@@ -296,6 +293,9 @@ module Types
description: 'Container repositories of the project',
resolver: Resolvers::ContainerRepositoriesResolver
+ field :container_repositories_count, GraphQL::INT_TYPE, null: false,
+ description: 'Number of container repositories in the project'
+
field :label,
Types::LabelType,
null: true,
@@ -311,6 +311,13 @@ module Types
description: 'Terraform states associated with the project',
resolver: Resolvers::Terraform::StatesResolver
+ field :pipeline_analytics, Types::Ci::AnalyticsType, null: true,
+ description: 'Pipeline analytics',
+ resolver: Resolvers::ProjectPipelineStatisticsResolver
+
+ field :total_pipeline_duration, GraphQL::INT_TYPE, null: true,
+ description: 'Total pipeline duration for all of the pipelines in a project'
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args|
LabelsFinder
@@ -335,6 +342,30 @@ module Types
.execute
end
+ def avatar_url
+ object.avatar_url(only_path: false)
+ end
+
+ def jobs_enabled
+ object.feature_available?(:builds, context[:current_user])
+ end
+
+ def open_issues_count
+ object.open_issues_count if object.feature_available?(:issues, context[:current_user])
+ end
+
+ def statistics
+ Gitlab::Graphql::Loaders::BatchProjectStatisticsLoader.new(object.id).find
+ end
+
+ def container_repositories_count
+ project.container_repositories.size
+ end
+
+ def total_pipeline_duration
+ object.all_pipelines.total_duration
+ end
+
private
def project
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index d194b0979b3..05bb371088c 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -24,7 +24,6 @@ module Types
field :current_user, Types::UserType,
null: true,
- resolve: -> (_obj, _args, context) { context[:current_user] },
description: "Get information about current user"
field :namespace, Types::NamespaceType,
@@ -92,6 +91,11 @@ module Types
description: 'Get runner setup instructions',
resolver: Resolvers::Ci::RunnerSetupResolver
+ field :ci_config, Types::Ci::Config::ConfigType, null: true,
+ description: 'Get linted and processed contents of a CI config. Should not be requested more than once per request.',
+ resolver: Resolvers::Ci::ConfigResolver,
+ complexity: 126 # AUTHENTICATED_COMPLEXITY / 2 + 1
+
def design_management
DesignManagementObject.new(nil)
end
@@ -116,6 +120,10 @@ module Types
id = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
GitlabSchema.find_by_gid(id)
end
+
+ def current_user
+ context[:current_user]
+ end
end
end
diff --git a/app/graphql/types/snippets/blob_viewer_type.rb b/app/graphql/types/snippets/blob_viewer_type.rb
index 50d0b0522d6..a2ffa144066 100644
--- a/app/graphql/types/snippets/blob_viewer_type.rb
+++ b/app/graphql/types/snippets/blob_viewer_type.rb
@@ -17,14 +17,12 @@ module Types
field :collapsed, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob should be displayed collapsed',
method: :collapsed?,
- null: false,
- resolve: -> (viewer, _args, _ctx) { !!viewer&.collapsed? }
+ null: false
field :too_large, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob too large to be displayed',
method: :too_large?,
- null: false,
- resolve: -> (viewer, _args, _ctx) { !!viewer&.too_large? }
+ null: false
field :render_error, GraphQL::STRING_TYPE,
description: 'Error rendering the blob content',
@@ -38,6 +36,14 @@ module Types
field :loading_partial_name, GraphQL::STRING_TYPE,
description: 'Loading partial name',
null: false
+
+ def collapsed
+ !!object&.collapsed?
+ end
+
+ def too_large
+ !!object&.too_large?
+ end
end
end
end
diff --git a/app/graphql/types/sort_enum.rb b/app/graphql/types/sort_enum.rb
index d0a6eecb672..c3a76330fe9 100644
--- a/app/graphql/types/sort_enum.rb
+++ b/app/graphql/types/sort_enum.rb
@@ -7,10 +7,10 @@ module Types
# 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, deprecated: { reason: 'Use UPDATED_DESC', milestone: '13.5' }
+ value 'updated_asc', 'Updated at ascending order', value: :updated_asc, deprecated: { reason: 'Use UPDATED_ASC', milestone: '13.5' }
+ value 'created_desc', 'Created at descending order', value: :created_desc, deprecated: { reason: 'Use CREATED_DESC', milestone: '13.5' }
+ value 'created_asc', 'Created at ascending order', value: :created_asc, 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
diff --git a/app/graphql/types/terraform/state_type.rb b/app/graphql/types/terraform/state_type.rb
index 05b6d130f19..d97e673bf31 100644
--- a/app/graphql/types/terraform/state_type.rb
+++ b/app/graphql/types/terraform/state_type.rb
@@ -19,9 +19,7 @@ module Types
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 }
+ description: 'The user currently holding a lock on the Terraform state'
field :locked_at, Types::TimeType,
null: true,
@@ -39,6 +37,10 @@ module Types
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the Terraform state was updated'
+
+ def locked_by_user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.locked_by_user_id).find
+ end
end
end
end
diff --git a/app/graphql/types/terraform/state_version_type.rb b/app/graphql/types/terraform/state_version_type.rb
index b1fbe42ecaf..a3af5c876ca 100644
--- a/app/graphql/types/terraform/state_version_type.rb
+++ b/app/graphql/types/terraform/state_version_type.rb
@@ -3,6 +3,8 @@
module Types
module Terraform
class StateVersionType < BaseObject
+ include ::API::Helpers::RelatedResourcesHelpers
+
graphql_name 'TerraformStateVersion'
authorize :read_terraform_state
@@ -13,15 +15,20 @@ module Types
field :created_by_user, Types::UserType,
null: true,
- authorize: :read_user,
- description: 'The user that created this version',
- resolve: -> (version, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, version.created_by_user_id).find }
+ description: 'The user that created this version'
+
+ field :download_path, GraphQL::STRING_TYPE,
+ null: true,
+ description: "URL for downloading the version's JSON file"
field :job, Types::Ci::JobType,
null: true,
- authorize: :read_build,
- description: 'The job that created this version',
- resolve: -> (version, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Build, version.ci_build_id).find }
+ description: 'The job that created this version'
+
+ field :serial, GraphQL::INT_TYPE,
+ null: true,
+ description: 'Serial number of the version',
+ method: :version
field :created_at, Types::TimeType,
null: false,
@@ -30,6 +37,22 @@ module Types
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the version was updated'
+
+ def created_by_user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.created_by_user_id).find
+ end
+
+ def download_path
+ expose_path api_v4_projects_terraform_state_versions_path(
+ id: object.project_id,
+ name: object.terraform_state.name,
+ serial: object.version
+ )
+ end
+
+ def job
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Build, object.ci_build_id).find
+ end
end
end
end
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
index 4f21da3d897..3694980ef93 100644
--- a/app/graphql/types/todo_type.rb
+++ b/app/graphql/types/todo_type.rb
@@ -16,19 +16,16 @@ module Types
field :project, Types::ProjectType,
description: 'The project this todo is associated with',
null: true,
- authorize: :read_project,
- resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, todo.project_id).find }
+ authorize: :read_project
field :group, Types::GroupType,
description: 'Group this todo is associated with',
null: true,
- authorize: :read_group,
- resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, todo.group_id).find }
+ authorize: :read_group
field :author, Types::UserType,
description: 'The author of this todo',
- null: false,
- resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, todo.author_id).find }
+ null: false
field :action, Types::TodoActionEnum,
description: 'Action of the todo',
@@ -50,5 +47,17 @@ module Types
field :created_at, Types::TimeType,
description: 'Timestamp this todo was created',
null: false
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
+
+ def group
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.group_id).find
+ end
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
end
end
diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb
index cc6bf7b4f00..a7b90d2533b 100644
--- a/app/graphql/types/tree/blob_type.rb
+++ b/app/graphql/types/tree/blob_type.rb
@@ -15,13 +15,14 @@ module Types
field :web_path, GraphQL::STRING_TYPE, null: true,
description: 'Web path of the blob'
field :lfs_oid, GraphQL::STRING_TYPE, null: true,
- description: 'LFS ID of the blob',
- resolve: -> (blob, args, ctx) do
- Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(blob.repository, blob.id).find
- end
+ description: 'LFS ID of the blob'
field :mode, GraphQL::STRING_TYPE, null: true,
description: 'Blob mode in numeric format'
- # rubocop: enable Graphql/AuthorizeTypes
+
+ def lfs_oid
+ Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(object.repository, object.id).find
+ end
end
+ # rubocop: enable Graphql/AuthorizeTypes
end
end
diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb
index b9fb6b28e71..fecd6c0f309 100644
--- a/app/graphql/types/tree/tree_type.rb
+++ b/app/graphql/types/tree/tree_type.rb
@@ -8,27 +8,32 @@ module Types
# Complexity 10 as it triggers a Gitaly call on each render
field :last_commit, Types::CommitType,
- null: true, complexity: 10, calls_gitaly: true, resolver: Resolvers::LastCommitResolver,
- description: 'Last commit for the tree'
+ null: true, complexity: 10, calls_gitaly: true, resolver: Resolvers::LastCommitResolver,
+ description: 'Last commit for the tree'
field :trees, Types::Tree::TreeEntryType.connection_type, null: false,
- description: 'Trees of the tree',
- resolve: -> (obj, args, ctx) do
- Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
- end
+ description: 'Trees of the tree'
field :submodules, Types::Tree::SubmoduleType.connection_type, null: false,
description: 'Sub-modules of the tree',
- calls_gitaly: true, resolve: -> (obj, args, ctx) do
- Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(obj.submodules, obj)
- end
+ calls_gitaly: true
field :blobs, Types::Tree::BlobType.connection_type, null: false,
description: 'Blobs of the tree',
- calls_gitaly: true, resolve: -> (obj, args, ctx) do
- Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository)
- end
- # rubocop: enable Graphql/AuthorizeTypes
+ calls_gitaly: true
+
+ def trees
+ Gitlab::Graphql::Representation::TreeEntry.decorate(object.trees, object.repository)
+ end
+
+ def submodules
+ Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(object.submodules, object)
+ end
+
+ def blobs
+ Gitlab::Graphql::Representation::TreeEntry.decorate(object.blobs, object.repository)
+ end
end
+ # rubocop: enable Graphql/AuthorizeTypes
end
end
diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb
index 783a0d8425a..93503268319 100644
--- a/app/graphql/types/user_type.rb
+++ b/app/graphql/types/user_type.rb
@@ -19,7 +19,10 @@ module Types
field :state, Types::UserStateEnum, null: false,
description: 'State of the user'
field :email, GraphQL::STRING_TYPE, null: true,
- description: 'User email', method: :public_email
+ description: 'User email', method: :public_email,
+ deprecated: { reason: 'Use public_email', milestone: '13.7' }
+ field :public_email, GraphQL::STRING_TYPE, null: true,
+ description: "User's public email"
field :avatar_url, GraphQL::STRING_TYPE, null: true,
description: "URL of the user's avatar"
field :web_url, GraphQL::STRING_TYPE, null: false,
@@ -37,19 +40,24 @@ module Types
feature_flag: :user_group_counts
field :status, Types::UserStatusType, null: true,
description: 'User status'
+ field :location, ::GraphQL::STRING_TYPE, null: true,
+ description: 'The location of the user.'
field :project_memberships, Types::ProjectMemberType.connection_type, null: true,
description: 'Project memberships of the user'
field :starred_projects, Types::ProjectType.connection_type, null: true,
description: 'Projects starred by the user',
resolver: Resolvers::UserStarredProjectsResolver
- # Merge request field: MRs can be either authored or assigned:
+ # Merge request field: MRs can be authored, assigned, or assigned-for-review:
field :authored_merge_requests,
resolver: Resolvers::AuthoredMergeRequestsResolver,
description: 'Merge Requests authored by the user'
field :assigned_merge_requests,
resolver: Resolvers::AssignedMergeRequestsResolver,
description: 'Merge Requests assigned to the user'
+ field :review_requested_merge_requests,
+ resolver: Resolvers::ReviewRequestedMergeRequestsResolver,
+ description: 'Merge Requests assigned to the user for review'
field :snippets,
Types::SnippetType.connection_type,