diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /app/graphql | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) | |
download | gitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'app/graphql')
133 files changed, 1461 insertions, 386 deletions
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index 8369d0e120f..84941fcde02 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -10,6 +10,7 @@ class GitlabSchema < GraphQL::Schema DEFAULT_MAX_DEPTH = 15 AUTHENTICATED_MAX_DEPTH = 20 + use GraphQL::Subscriptions::ActionCableSubscriptions use GraphQL::Pagination::Connections use BatchLoader::GraphQL use Gitlab::Graphql::Pagination::Connections @@ -24,6 +25,7 @@ class GitlabSchema < GraphQL::Schema query Types::QueryType mutation Types::MutationType + subscription Types::SubscriptionType default_max_page_size 100 @@ -168,7 +170,7 @@ class GitlabSchema < GraphQL::Schema end end -GitlabSchema.prepend_if_ee('EE::GitlabSchema') # rubocop: disable Cop/InjectEnterpriseEditionModule +GitlabSchema.prepend_mod_with('GitlabSchema') # rubocop: disable Cop/InjectEnterpriseEditionModule # Force the schema to load as a workaround for intermittent errors we # see due to a lack of thread safety. diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb new file mode 100644 index 00000000000..671c7c2cd25 --- /dev/null +++ b/app/graphql/graphql_triggers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module GraphqlTriggers + def self.issuable_assignees_updated(issuable) + GitlabSchema.subscriptions.trigger('issuableAssigneesUpdated', { issuable_id: issuable.to_gid }, issuable) + end +end diff --git a/app/graphql/mutations/alert_management/http_integration/create.rb b/app/graphql/mutations/alert_management/http_integration/create.rb index 2d7bffb4333..54803855bcf 100644 --- a/app/graphql/mutations/alert_management/http_integration/create.rb +++ b/app/graphql/mutations/alert_management/http_integration/create.rb @@ -34,4 +34,4 @@ module Mutations end end -Mutations::AlertManagement::HttpIntegration::Create.prepend_if_ee('::EE::Mutations::AlertManagement::HttpIntegration::Create') +Mutations::AlertManagement::HttpIntegration::Create.prepend_mod_with('Mutations::AlertManagement::HttpIntegration::Create') diff --git a/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb b/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb index e33b7bb399a..efa92bfe895 100644 --- a/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb +++ b/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb @@ -33,4 +33,4 @@ module Mutations end end -Mutations::AlertManagement::HttpIntegration::HttpIntegrationBase.prepend_if_ee('::EE::Mutations::AlertManagement::HttpIntegration::HttpIntegrationBase') +Mutations::AlertManagement::HttpIntegration::HttpIntegrationBase.prepend_mod_with('Mutations::AlertManagement::HttpIntegration::HttpIntegrationBase') diff --git a/app/graphql/mutations/alert_management/http_integration/update.rb b/app/graphql/mutations/alert_management/http_integration/update.rb index b1e4ce841ee..4e6e7995c10 100644 --- a/app/graphql/mutations/alert_management/http_integration/update.rb +++ b/app/graphql/mutations/alert_management/http_integration/update.rb @@ -32,4 +32,4 @@ module Mutations end end -Mutations::AlertManagement::HttpIntegration::Update.prepend_if_ee('::EE::Mutations::AlertManagement::HttpIntegration::Update') +Mutations::AlertManagement::HttpIntegration::Update.prepend_mod_with('Mutations::AlertManagement::HttpIntegration::Update') diff --git a/app/graphql/mutations/boards/create.rb b/app/graphql/mutations/boards/create.rb index 003c4f7761b..44fc22cf883 100644 --- a/app/graphql/mutations/boards/create.rb +++ b/app/graphql/mutations/boards/create.rb @@ -30,4 +30,4 @@ module Mutations end end -Mutations::Boards::Create.prepend_if_ee('::EE::Mutations::Boards::Create') +Mutations::Boards::Create.prepend_mod_with('Mutations::Boards::Create') diff --git a/app/graphql/mutations/boards/issues/issue_move_list.rb b/app/graphql/mutations/boards/issues/issue_move_list.rb index f32205643da..4c9752c6343 100644 --- a/app/graphql/mutations/boards/issues/issue_move_list.rb +++ b/app/graphql/mutations/boards/issues/issue_move_list.rb @@ -86,4 +86,4 @@ module Mutations end end -Mutations::Boards::Issues::IssueMoveList.prepend_if_ee('EE::Mutations::Boards::Issues::IssueMoveList') +Mutations::Boards::Issues::IssueMoveList.prepend_mod_with('Mutations::Boards::Issues::IssueMoveList') diff --git a/app/graphql/mutations/boards/lists/base_update.rb b/app/graphql/mutations/boards/lists/base_update.rb new file mode 100644 index 00000000000..b06cb3b1e32 --- /dev/null +++ b/app/graphql/mutations/boards/lists/base_update.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Mutations + module Boards + module Lists + class BaseUpdate < BaseMutation + argument :position, GraphQL::INT_TYPE, + required: false, + description: 'Position of list within the board.' + + argument :collapsed, GraphQL::BOOLEAN_TYPE, + required: false, + description: 'Indicates if the list is collapsed for this user.' + + def resolve(list: nil, **args) + if list.nil? || !can_read_list?(list) + raise_resource_not_available_error! + end + + update_result = update_list(list, args) + + { + list: update_result.payload[:list], + errors: update_result.errors + } + end + + private + + def update_list(list, args) + raise NotImplementedError + end + + def can_read_list?(list) + raise NotImplementedError + end + end + end + end +end diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb index 673fa95fc56..590a905ab7b 100644 --- a/app/graphql/mutations/boards/lists/create.rb +++ b/app/graphql/mutations/boards/lists/create.rb @@ -34,4 +34,4 @@ module Mutations end end -Mutations::Boards::Lists::Create.prepend_if_ee('::EE::Mutations::Boards::Lists::Create') +Mutations::Boards::Lists::Create.prepend_mod_with('Mutations::Boards::Lists::Create') diff --git a/app/graphql/mutations/boards/lists/update.rb b/app/graphql/mutations/boards/lists/update.rb index 504082ec22c..d17dd5162a0 100644 --- a/app/graphql/mutations/boards/lists/update.rb +++ b/app/graphql/mutations/boards/lists/update.rb @@ -3,7 +3,7 @@ module Mutations module Boards module Lists - class Update < BaseMutation + class Update < BaseUpdate graphql_name 'UpdateBoardList' argument :list_id, Types::GlobalIDType[List], @@ -11,29 +11,11 @@ module Mutations loads: Types::BoardListType, description: 'Global ID of the list.' - argument :position, GraphQL::INT_TYPE, - required: false, - description: 'Position of list within the board.' - - argument :collapsed, GraphQL::BOOLEAN_TYPE, - required: false, - description: 'Indicates if the list is collapsed for this user.' - field :list, Types::BoardListType, null: true, description: 'Mutated list.' - def resolve(list: nil, **args) - raise_resource_not_available_error! unless can_read_list?(list) - update_result = update_list(list, args) - - { - list: update_result[:list], - errors: list.errors.full_messages - } - end - private def update_list(list, args) @@ -42,8 +24,6 @@ module Mutations end def can_read_list?(list) - return false unless list.present? - Ability.allowed?(current_user, :read_issue_board_list, list.board) end end diff --git a/app/graphql/mutations/boards/update.rb b/app/graphql/mutations/boards/update.rb index 628b3a3fadb..f1a1d57306b 100644 --- a/app/graphql/mutations/boards/update.rb +++ b/app/graphql/mutations/boards/update.rb @@ -42,4 +42,4 @@ module Mutations end end -Mutations::Boards::Update.prepend_if_ee('::EE::Mutations::Boards::Update') +Mutations::Boards::Update.prepend_mod_with('Mutations::Boards::Update') diff --git a/app/graphql/mutations/ci/ci_cd_settings_update.rb b/app/graphql/mutations/ci/ci_cd_settings_update.rb index d7451babaea..a484c2438a4 100644 --- a/app/graphql/mutations/ci/ci_cd_settings_update.rb +++ b/app/graphql/mutations/ci/ci_cd_settings_update.rb @@ -36,4 +36,4 @@ module Mutations end end -Mutations::Ci::CiCdSettingsUpdate.prepend_if_ee('::EE::Mutations::Ci::CiCdSettingsUpdate') +Mutations::Ci::CiCdSettingsUpdate.prepend_mod_with('Mutations::Ci::CiCdSettingsUpdate') diff --git a/app/graphql/mutations/ci/job/base.rb b/app/graphql/mutations/ci/job/base.rb new file mode 100644 index 00000000000..3359def159a --- /dev/null +++ b/app/graphql/mutations/ci/job/base.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module Job + class Base < BaseMutation + JobID = ::Types::GlobalIDType[::Ci::Build] + + argument :id, JobID, + required: true, + description: 'The ID of the job to mutate.' + + def find_object(id: ) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = JobID.coerce_isolated_input(id) + GlobalID::Locator.locate(id) + end + end + end + end +end diff --git a/app/graphql/mutations/ci/job/play.rb b/app/graphql/mutations/ci/job/play.rb new file mode 100644 index 00000000000..f87904f8b25 --- /dev/null +++ b/app/graphql/mutations/ci/job/play.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module Job + class Play < Base + graphql_name 'JobPlay' + + field :job, + Types::Ci::JobType, + null: true, + description: 'The job after the mutation.' + + authorize :update_build + + def resolve(id:) + job = authorized_find!(id: id) + project = job.project + + ::Ci::PlayBuildService.new(project, current_user).execute(job) + { + job: job, + errors: errors_on_object(job) + } + end + end + end + end +end diff --git a/app/graphql/mutations/ci/job/retry.rb b/app/graphql/mutations/ci/job/retry.rb new file mode 100644 index 00000000000..a61d5dddb40 --- /dev/null +++ b/app/graphql/mutations/ci/job/retry.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module Job + class Retry < Base + graphql_name 'JobRetry' + + field :job, + Types::Ci::JobType, + null: true, + description: 'The job after the mutation.' + + authorize :update_build + + def resolve(id:) + job = authorized_find!(id: id) + project = job.project + + ::Ci::RetryBuildService.new(project, current_user).execute(job) + { + job: job, + errors: errors_on_object(job) + } + end + end + end + end +end diff --git a/app/graphql/mutations/commits/create.rb b/app/graphql/mutations/commits/create.rb index 84933fee5d2..2e06e1ea0c4 100644 --- a/app/graphql/mutations/commits/create.rb +++ b/app/graphql/mutations/commits/create.rb @@ -5,6 +5,11 @@ module Mutations class Create < BaseMutation include FindsProject + class UrlHelpers + include GitlabRoutingHelper + include Gitlab::Routing + end + graphql_name 'CommitCreate' argument :project_path, GraphQL::ID_TYPE, @@ -29,6 +34,11 @@ module Mutations required: true, description: 'Array of action hashes to commit as a batch.' + field :commit_pipeline_path, + GraphQL::STRING_TYPE, + null: true, + description: "ETag path for the commit's pipeline." + field :commit, Types::CommitType, null: true, @@ -50,6 +60,7 @@ module Mutations { commit: (project.repository.commit(result[:result]) if result[:status] == :success), + commit_pipeline_path: UrlHelpers.new.graphql_etag_pipeline_sha_path(result[:result]), errors: Array.wrap(result[:message]) } end diff --git a/app/graphql/mutations/concerns/mutations/assignable.rb b/app/graphql/mutations/concerns/mutations/assignable.rb index d3ab0a1779a..e214a57500c 100644 --- a/app/graphql/mutations/concerns/mutations/assignable.rb +++ b/app/graphql/mutations/concerns/mutations/assignable.rb @@ -33,9 +33,9 @@ module Mutations def assign!(resource, users, operation_mode) update_service_class.new( - resource.project, - current_user, - assignee_ids: assignee_ids(resource, users, operation_mode) + project: resource.project, + current_user: current_user, + params: { assignee_ids: assignee_ids(resource, users, operation_mode) } ).execute(resource) end diff --git a/app/graphql/mutations/concerns/mutations/resolves_issuable.rb b/app/graphql/mutations/concerns/mutations/resolves_issuable.rb index 0fe2d09de6d..fd9031d3ea7 100644 --- a/app/graphql/mutations/concerns/mutations/resolves_issuable.rb +++ b/app/graphql/mutations/concerns/mutations/resolves_issuable.rb @@ -38,4 +38,4 @@ module Mutations end end -Mutations::ResolvesIssuable.prepend_if_ee('::EE::Mutations::ResolvesIssuable') +Mutations::ResolvesIssuable.prepend_mod_with('Mutations::ResolvesIssuable') diff --git a/app/graphql/mutations/issues/common_mutation_arguments.rb b/app/graphql/mutations/issues/common_mutation_arguments.rb index 4b5b246281f..65768b85d14 100644 --- a/app/graphql/mutations/issues/common_mutation_arguments.rb +++ b/app/graphql/mutations/issues/common_mutation_arguments.rb @@ -22,6 +22,11 @@ module Mutations as: :discussion_locked, required: false, description: copy_field_description(Types::IssueType, :discussion_locked) + + argument :type, Types::IssueTypeEnum, + as: :issue_type, + required: false, + description: copy_field_description(Types::IssueType, :type) end end end diff --git a/app/graphql/mutations/issues/create.rb b/app/graphql/mutations/issues/create.rb index 37fddd92832..3a57e2434a5 100644 --- a/app/graphql/mutations/issues/create.rb +++ b/app/graphql/mutations/issues/create.rb @@ -73,7 +73,7 @@ module Mutations project = authorized_find!(project_path) params = build_create_issue_params(attributes.merge(author_id: current_user.id)) - issue = ::Issues::CreateService.new(project, current_user, params).execute + issue = ::Issues::CreateService.new(project: project, current_user: current_user, params: params).execute if issue.spam? issue.errors.add(:base, 'Spam detected.') @@ -102,4 +102,4 @@ module Mutations end end -Mutations::Issues::Create.prepend_if_ee('::EE::Mutations::Issues::Create') +Mutations::Issues::Create.prepend_mod_with('Mutations::Issues::Create') diff --git a/app/graphql/mutations/issues/move.rb b/app/graphql/mutations/issues/move.rb index 0f2af99bf61..cb4f0f42b38 100644 --- a/app/graphql/mutations/issues/move.rb +++ b/app/graphql/mutations/issues/move.rb @@ -18,7 +18,7 @@ module Mutations target_project = resolve_project(full_path: target_project_path).sync begin - moved_issue = ::Issues::MoveService.new(source_project, current_user).execute(issue, target_project) + moved_issue = ::Issues::MoveService.new(project: source_project, current_user: current_user).execute(issue, target_project) rescue ::Issues::MoveService::MoveError => error errors = error.message end diff --git a/app/graphql/mutations/issues/set_confidential.rb b/app/graphql/mutations/issues/set_confidential.rb index 75befddc261..8e88b31d9ed 100644 --- a/app/graphql/mutations/issues/set_confidential.rb +++ b/app/graphql/mutations/issues/set_confidential.rb @@ -14,7 +14,7 @@ module Mutations issue = authorized_find!(project_path: project_path, iid: iid) project = issue.project - ::Issues::UpdateService.new(project, current_user, confidential: confidential) + ::Issues::UpdateService.new(project: project, current_user: current_user, params: { confidential: confidential }) .execute(issue) { diff --git a/app/graphql/mutations/issues/set_due_date.rb b/app/graphql/mutations/issues/set_due_date.rb index da7892f4ed4..9cefac96b25 100644 --- a/app/graphql/mutations/issues/set_due_date.rb +++ b/app/graphql/mutations/issues/set_due_date.rb @@ -7,14 +7,23 @@ module Mutations argument :due_date, Types::TimeType, - required: true, - description: 'The desired due date for the issue.' + required: false, + description: 'The desired due date for the issue, ' \ + 'due date will be removed if absent or set to null' + + def ready?(**args) + unless args.key?(:due_date) + raise Gitlab::Graphql::Errors::ArgumentError, 'Argument dueDate must be provided (`null` accepted)' + end + + super + end def resolve(project_path:, iid:, due_date:) issue = authorized_find!(project_path: project_path, iid: iid) project = issue.project - ::Issues::UpdateService.new(project, current_user, due_date: due_date) + ::Issues::UpdateService.new(project: project, current_user: current_user, params: { due_date: due_date }) .execute(issue) { diff --git a/app/graphql/mutations/issues/set_locked.rb b/app/graphql/mutations/issues/set_locked.rb index 611226e48ad..3a696a64dad 100644 --- a/app/graphql/mutations/issues/set_locked.rb +++ b/app/graphql/mutations/issues/set_locked.rb @@ -13,7 +13,7 @@ module Mutations def resolve(project_path:, iid:, locked:) issue = authorized_find!(project_path: project_path, iid: iid) - ::Issues::UpdateService.new(issue.project, current_user, discussion_locked: locked) + ::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: { discussion_locked: locked }) .execute(issue) { diff --git a/app/graphql/mutations/issues/set_severity.rb b/app/graphql/mutations/issues/set_severity.rb index bc386e07178..778563ba053 100644 --- a/app/graphql/mutations/issues/set_severity.rb +++ b/app/graphql/mutations/issues/set_severity.rb @@ -12,7 +12,7 @@ module Mutations issue = authorized_find!(project_path: project_path, iid: iid) project = issue.project - ::Issues::UpdateService.new(project, current_user, severity: severity) + ::Issues::UpdateService.new(project: project, current_user: current_user, params: { severity: severity }) .execute(issue) { diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb index eea2cd49aa0..eb16b7b38d0 100644 --- a/app/graphql/mutations/issues/update.rb +++ b/app/graphql/mutations/issues/update.rb @@ -31,7 +31,7 @@ module Mutations issue = authorized_find!(project_path: project_path, iid: iid) project = issue.project - ::Issues::UpdateService.new(project, current_user, args).execute(issue) + ::Issues::UpdateService.new(project: project, current_user: current_user, params: args).execute(issue) { issue: issue, @@ -42,4 +42,4 @@ module Mutations end end -Mutations::Issues::Update.prepend_if_ee('::EE::Mutations::Issues::Update') +Mutations::Issues::Update.prepend_mod_with('Mutations::Issues::Update') diff --git a/app/graphql/mutations/labels/create.rb b/app/graphql/mutations/labels/create.rb index ccbd1c37cbf..4da628d53ea 100644 --- a/app/graphql/mutations/labels/create.rb +++ b/app/graphql/mutations/labels/create.rb @@ -20,10 +20,21 @@ module Mutations required: false, description: 'Description of the label.' + argument :remove_on_close, GraphQL::BOOLEAN_TYPE, + required: false, + description: copy_field_description(Types::LabelType, :remove_on_close) + argument :color, GraphQL::STRING_TYPE, required: false, default_value: Label::DEFAULT_COLOR, - description: "The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the CSS color names in https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords." + see: { + 'List of color keywords at mozilla.org' => + 'https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords' + }, + description: <<~DESC + The color of the label given in 6-digit hex notation with leading '#' sign + (for example, `#FFAABB`) or one of the CSS color names. + DESC authorize :admin_label diff --git a/app/graphql/mutations/merge_requests/accept.rb b/app/graphql/mutations/merge_requests/accept.rb index da94dcd8890..9994f793a01 100644 --- a/app/graphql/mutations/merge_requests/accept.rb +++ b/app/graphql/mutations/merge_requests/accept.rb @@ -47,7 +47,7 @@ module Mutations merge_request = authorized_find!(project_path: project_path, iid: iid) project = merge_request.target_project merge_params = args.compact.with_indifferent_access - merge_service = ::MergeRequests::MergeService.new(project, current_user, merge_params) + merge_service = ::MergeRequests::MergeService.new(project: project, current_user: current_user, params: merge_params) if error = validate(merge_request, merge_service, merge_params) return { merge_request: merge_request, errors: [error] } diff --git a/app/graphql/mutations/merge_requests/create.rb b/app/graphql/mutations/merge_requests/create.rb index 9ac8f70be95..4849c198677 100644 --- a/app/graphql/mutations/merge_requests/create.rb +++ b/app/graphql/mutations/merge_requests/create.rb @@ -42,7 +42,7 @@ module Mutations project = authorized_find!(project_path) params = attributes.merge(author_id: current_user.id) - merge_request = ::MergeRequests::CreateService.new(project, current_user, params).execute + merge_request = ::MergeRequests::CreateService.new(project: project, current_user: current_user, params: params).execute { merge_request: merge_request.valid? ? merge_request : nil, diff --git a/app/graphql/mutations/merge_requests/reviewer_rereview.rb b/app/graphql/mutations/merge_requests/reviewer_rereview.rb index f6f4881654e..d1d5118e271 100644 --- a/app/graphql/mutations/merge_requests/reviewer_rereview.rb +++ b/app/graphql/mutations/merge_requests/reviewer_rereview.rb @@ -15,7 +15,7 @@ module Mutations def resolve(project_path:, iid:, user:) merge_request = authorized_find!(project_path: project_path, iid: iid) - result = ::MergeRequests::RequestReviewService.new(merge_request.project, current_user).execute(merge_request, user) + result = ::MergeRequests::RequestReviewService.new(project: merge_request.project, current_user: current_user).execute(merge_request, user) { merge_request: merge_request, diff --git a/app/graphql/mutations/merge_requests/set_draft.rb b/app/graphql/mutations/merge_requests/set_draft.rb new file mode 100644 index 00000000000..80006c6f70e --- /dev/null +++ b/app/graphql/mutations/merge_requests/set_draft.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Mutations + module MergeRequests + class SetDraft < Base + graphql_name 'MergeRequestSetDraft' + + argument :draft, + GraphQL::BOOLEAN_TYPE, + required: true, + description: <<~DESC + Whether or not to set the merge request as a draft. + DESC + + def resolve(project_path:, iid:, draft: nil) + merge_request = authorized_find!(project_path: project_path, iid: iid) + project = merge_request.project + + ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { wip_event: wip_event(draft) }) + .execute(merge_request) + + { + merge_request: merge_request, + errors: errors_on_object(merge_request) + } + end + + private + + def wip_event(wip) + wip ? 'wip' : 'unwip' + end + end + end +end diff --git a/app/graphql/mutations/merge_requests/set_labels.rb b/app/graphql/mutations/merge_requests/set_labels.rb index 712c68c9425..a77c2731a05 100644 --- a/app/graphql/mutations/merge_requests/set_labels.rb +++ b/app/graphql/mutations/merge_requests/set_labels.rb @@ -9,14 +9,14 @@ module Mutations [::Types::GlobalIDType[Label]], required: true, description: <<~DESC - The Label IDs to set. Replaces existing labels by default. + The Label IDs to set. Replaces existing labels by default. DESC argument :operation_mode, Types::MutationOperationModeEnum, required: false, description: <<~DESC - Changes the operation mode. Defaults to REPLACE. + Changes the operation mode. Defaults to REPLACE. DESC def resolve(project_path:, iid:, label_ids:, operation_mode: Types::MutationOperationModeEnum.enum[:replace]) @@ -38,7 +38,7 @@ module Mutations :label_ids end - ::MergeRequests::UpdateService.new(project, current_user, attribute_name => label_ids) + ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { attribute_name => label_ids }) .execute(merge_request) { diff --git a/app/graphql/mutations/merge_requests/set_locked.rb b/app/graphql/mutations/merge_requests/set_locked.rb index c49d5186a03..e9e607551a6 100644 --- a/app/graphql/mutations/merge_requests/set_locked.rb +++ b/app/graphql/mutations/merge_requests/set_locked.rb @@ -9,14 +9,14 @@ module Mutations GraphQL::BOOLEAN_TYPE, required: true, description: <<~DESC - Whether or not to lock the merge request. + Whether or not to lock the merge request. DESC def resolve(project_path:, iid:, locked:) merge_request = authorized_find!(project_path: project_path, iid: iid) project = merge_request.project - ::MergeRequests::UpdateService.new(project, current_user, discussion_locked: locked) + ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { discussion_locked: locked }) .execute(merge_request) { diff --git a/app/graphql/mutations/merge_requests/set_milestone.rb b/app/graphql/mutations/merge_requests/set_milestone.rb index abcb1bda1f3..ed5139c4af9 100644 --- a/app/graphql/mutations/merge_requests/set_milestone.rb +++ b/app/graphql/mutations/merge_requests/set_milestone.rb @@ -10,14 +10,14 @@ module Mutations required: false, loads: Types::MilestoneType, description: <<~DESC - The milestone to assign to the merge request. + The milestone to assign to the merge request. DESC def resolve(project_path:, iid:, milestone: nil) merge_request = authorized_find!(project_path: project_path, iid: iid) project = merge_request.project - ::MergeRequests::UpdateService.new(project, current_user, milestone: milestone) + ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { milestone: milestone }) .execute(merge_request) { diff --git a/app/graphql/mutations/merge_requests/set_wip.rb b/app/graphql/mutations/merge_requests/set_wip.rb index beb042ce93f..6f52b240840 100644 --- a/app/graphql/mutations/merge_requests/set_wip.rb +++ b/app/graphql/mutations/merge_requests/set_wip.rb @@ -16,7 +16,7 @@ module Mutations merge_request = authorized_find!(project_path: project_path, iid: iid) project = merge_request.project - ::MergeRequests::UpdateService.new(project, current_user, wip_event: wip_event(merge_request, wip)) + ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { wip_event: wip_event(merge_request, wip) }) .execute(merge_request) { diff --git a/app/graphql/mutations/merge_requests/update.rb b/app/graphql/mutations/merge_requests/update.rb index 6a94d2f37b2..246e468c34c 100644 --- a/app/graphql/mutations/merge_requests/update.rb +++ b/app/graphql/mutations/merge_requests/update.rb @@ -29,7 +29,7 @@ module Mutations attributes = args.compact ::MergeRequests::UpdateService - .new(merge_request.project, current_user, attributes) + .new(project: merge_request.project, current_user: current_user, params: attributes) .execute(merge_request) errors = errors_on_object(merge_request) diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb index ca21c3418fc..75c80cfbd3e 100644 --- a/app/graphql/mutations/namespace/package_settings/update.rb +++ b/app/graphql/mutations/namespace/package_settings/update.rb @@ -25,6 +25,16 @@ module Mutations required: false, description: copy_field_description(Types::Namespace::PackageSettingsType, :maven_duplicate_exception_regex) + argument :generic_duplicates_allowed, + GraphQL::BOOLEAN_TYPE, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicates_allowed) + + argument :generic_duplicate_exception_regex, + Types::UntrustedRegexp, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicate_exception_regex) + field :package_settings, Types::Namespace::PackageSettingsType, null: true, diff --git a/app/graphql/mutations/security/ci_configuration/configure_sast.rb b/app/graphql/mutations/security/ci_configuration/configure_sast.rb index e4a3f815396..237aff1f052 100644 --- a/app/graphql/mutations/security/ci_configuration/configure_sast.rb +++ b/app/graphql/mutations/security/ci_configuration/configure_sast.rb @@ -7,6 +7,11 @@ module Mutations include FindsProject graphql_name 'ConfigureSast' + description <<~DESC + Configure SAST for a project by enabling SAST in a new or modified + `.gitlab-ci.yml` file in a new branch. The new branch and a URL to + create a Merge Request are a part of the response. + DESC argument :project_path, GraphQL::ID_TYPE, required: true, @@ -16,12 +21,12 @@ module Mutations required: true, description: 'SAST CI configuration for the project.' - field :status, GraphQL::STRING_TYPE, null: false, - description: 'Status of creating the commit for the supplied SAST CI configuration.' - field :success_path, GraphQL::STRING_TYPE, null: true, description: 'Redirect path to use when the response is successful.' + field :branch, GraphQL::STRING_TYPE, null: true, + description: 'Branch that has the new/modified `.gitlab-ci.yml` file.' + authorize :push_code def resolve(project_path:, configuration:) @@ -35,9 +40,9 @@ module Mutations def prepare_response(result) { - status: result[:status], - success_path: result[:success_path], - errors: Array(result[:errors]) + branch: result.payload[:branch], + success_path: result.payload[:success_path], + errors: result.errors } end end diff --git a/app/graphql/mutations/security/ci_configuration/configure_secret_detection.rb b/app/graphql/mutations/security/ci_configuration/configure_secret_detection.rb new file mode 100644 index 00000000000..32ad670edaa --- /dev/null +++ b/app/graphql/mutations/security/ci_configuration/configure_secret_detection.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Mutations + module Security + module CiConfiguration + class ConfigureSecretDetection < BaseMutation + include FindsProject + + graphql_name 'ConfigureSecretDetection' + description <<~DESC + Configure Secret Detection for a project by enabling Secret Detection + in a new or modified `.gitlab-ci.yml` file in a new branch. The new + branch and a URL to create a Merge Request are a part of the + response. + DESC + + argument :project_path, GraphQL::ID_TYPE, + required: true, + description: 'Full path of the project.' + + field :success_path, GraphQL::STRING_TYPE, null: true, + description: 'Redirect path to use when the response is successful.' + + field :branch, GraphQL::STRING_TYPE, null: true, + description: 'Branch that has the new/modified `.gitlab-ci.yml` file.' + + authorize :push_code + + def resolve(project_path:) + project = authorized_find!(project_path) + + result = ::Security::CiConfiguration::SecretDetectionCreateService.new(project, current_user).execute + prepare_response(result) + end + + private + + def prepare_response(result) + { + branch: result.payload[:branch], + success_path: result.payload[:success_path], + errors: result.errors + } + end + end + end + end +end diff --git a/app/graphql/queries/burndown_chart/burnup.query.graphql b/app/graphql/queries/burndown_chart/burnup.query.graphql new file mode 100644 index 00000000000..7a389a6def5 --- /dev/null +++ b/app/graphql/queries/burndown_chart/burnup.query.graphql @@ -0,0 +1,70 @@ +query BurnupTimesSeriesData($id: ID!, $isIteration: Boolean = false, $weight: Boolean = false) { + milestone(id: $id) @skip(if: $isIteration) { + __typename + id + title + report { + __typename + burnupTimeSeries { + __typename + date + completedCount @skip(if: $weight) + scopeCount @skip(if: $weight) + completedWeight @include(if: $weight) + scopeWeight @include(if: $weight) + } + stats { + __typename + total { + __typename + count @skip(if: $weight) + weight @include(if: $weight) + } + complete { + __typename + count @skip(if: $weight) + weight @include(if: $weight) + } + incomplete { + __typename + count @skip(if: $weight) + weight @include(if: $weight) + } + } + } + } + iteration(id: $id) @include(if: $isIteration) { + __typename + id + title + report { + __typename + burnupTimeSeries { + __typename + date + completedCount @skip(if: $weight) + scopeCount @skip(if: $weight) + completedWeight @include(if: $weight) + scopeWeight @include(if: $weight) + } + stats { + __typename + total { + __typename + count @skip(if: $weight) + weight @include(if: $weight) + } + complete { + __typename + count @skip(if: $weight) + weight @include(if: $weight) + } + incomplete { + __typename + count @skip(if: $weight) + weight @include(if: $weight) + } + } + } + } +} diff --git a/app/graphql/queries/epic/epic_children.query.graphql b/app/graphql/queries/epic/epic_children.query.graphql index c12778109d0..5ee27052f95 100644 --- a/app/graphql/queries/epic/epic_children.query.graphql +++ b/app/graphql/queries/epic/epic_children.query.graphql @@ -16,6 +16,10 @@ fragment RelatedTreeBaseEpic on Epic { adminEpic createEpic } + descendantWeightSum { + closedIssues + openedIssues + } descendantCounts { __typename openedEpics diff --git a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql index 959bf7dc91d..873ecc81466 100644 --- a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql +++ b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql @@ -27,6 +27,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) { __typename id iid + complete usesNeeds downstream { __typename diff --git a/app/graphql/resolvers/ci/runner_resolver.rb b/app/graphql/resolvers/ci/runner_resolver.rb new file mode 100644 index 00000000000..ca94e28b2e9 --- /dev/null +++ b/app/graphql/resolvers/ci/runner_resolver.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + class RunnerResolver < BaseResolver + include LooksAhead + + type Types::Ci::RunnerType, null: true + extras [:lookahead] + description 'Runner information.' + + argument :id, + type: ::Types::GlobalIDType[::Ci::Runner], + required: true, + description: 'Runner ID.' + + def resolve_with_lookahead(id:) + find_runner(id: id) + end + + private + + def find_runner(id:) + runner_id = GitlabSchema.parse_gid(id, expected_type: ::Ci::Runner).model_id.to_i + preload_tag_list = lookahead.selects?(:tag_list) + + BatchLoader::GraphQL.for(runner_id).batch(key: { preload_tag_list: preload_tag_list }) do |ids, loader, batch| + results = ::Ci::Runner.id_in(ids) + results = results.with_tags if batch[:key][:preload_tag_list] + + results.each { |record| loader.call(record.id, record) } + end + end + end + end +end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb new file mode 100644 index 00000000000..710706325cc --- /dev/null +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + class RunnersResolver < BaseResolver + type Types::Ci::RunnerType.connection_type, null: true + + argument :status, ::Types::Ci::RunnerStatusEnum, + required: false, + description: 'Filter runners by status.' + + argument :type, ::Types::Ci::RunnerTypeEnum, + required: false, + description: 'Filter runners by type.' + + argument :tag_list, [GraphQL::STRING_TYPE], + required: false, + description: 'Filter by tags associated with the runner (comma-separated or array).' + + argument :sort, ::Types::Ci::RunnerSortEnum, + required: false, + description: 'Sort order of results.' + + def resolve(**args) + ::Ci::RunnersFinder + .new(current_user: current_user, params: runners_finder_params(args)) + .execute + end + + private + + def runners_finder_params(params) + { + status_status: params[:status]&.to_s, + type_type: params[:type], + tag_name: params[:tag_list], + search: params[:search], + sort: params[:sort]&.to_s + }.compact + end + end + end +end diff --git a/app/graphql/resolvers/ci/template_resolver.rb b/app/graphql/resolvers/ci/template_resolver.rb new file mode 100644 index 00000000000..dd910116544 --- /dev/null +++ b/app/graphql/resolvers/ci/template_resolver.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + class TemplateResolver < BaseResolver + type Types::Ci::TemplateType, null: true + + argument :name, GraphQL::STRING_TYPE, required: true, + description: 'Name of the CI/CD template to search for.' + + alias_method :project, :object + + def resolve(name: nil) + ::TemplateFinder.new(:gitlab_ci_ymls, project, name: name).execute + end + end + end +end diff --git a/app/graphql/resolvers/concerns/board_issue_filterable.rb b/app/graphql/resolvers/concerns/board_issue_filterable.rb index 3484a1cc4ba..88de69a3844 100644 --- a/app/graphql/resolvers/concerns/board_issue_filterable.rb +++ b/app/graphql/resolvers/concerns/board_issue_filterable.rb @@ -32,4 +32,4 @@ module BoardIssueFilterable end end -::BoardIssueFilterable.prepend_if_ee('::EE::Resolvers::BoardIssueFilterable') +::BoardIssueFilterable.prepend_mod_with('Resolvers::BoardIssueFilterable') diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb index 0ff3997f3bc..aa08d62c6a5 100644 --- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb +++ b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb @@ -7,57 +7,57 @@ module IssueResolverArguments include LooksAhead argument :iid, GraphQL::STRING_TYPE, - required: false, - description: 'IID of the issue. For example, "1".' + required: false, + description: 'IID of the issue. For example, "1".' argument :iids, [GraphQL::STRING_TYPE], - required: false, - description: 'List of IIDs of issues. For example, [1, 2].' + required: false, + description: 'List of IIDs of issues. For example, ["1", "2"].' argument :label_name, [GraphQL::STRING_TYPE, null: true], - required: false, - description: 'Labels applied to this issue.' + required: false, + description: 'Labels applied to this issue.' argument :milestone_title, [GraphQL::STRING_TYPE, null: true], - required: false, - description: 'Milestone applied to this issue.' + required: false, + description: 'Milestone applied to this issue.' argument :author_username, GraphQL::STRING_TYPE, - required: false, - description: 'Username of the author of the issue.' + required: false, + description: 'Username of the author of the issue.' argument :assignee_username, GraphQL::STRING_TYPE, - required: false, - description: 'Username of a user assigned to the issue.', - deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' } + required: false, + description: 'Username of a user assigned to the issue.', + deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' } argument :assignee_usernames, [GraphQL::STRING_TYPE], - required: false, - description: 'Usernames of users assigned to the issue.' + required: false, + description: 'Usernames of users assigned to the issue.' argument :assignee_id, GraphQL::STRING_TYPE, - required: false, - description: 'ID of a user assigned to the issues, "none" and "any" values are supported.' + required: false, + description: 'ID of a user assigned to the issues, "none" and "any" values are supported.' argument :created_before, Types::TimeType, - required: false, - description: 'Issues created before this date.' + required: false, + description: 'Issues created before this date.' argument :created_after, Types::TimeType, - required: false, - description: 'Issues created after this date.' + required: false, + description: 'Issues created after this date.' argument :updated_before, Types::TimeType, - required: false, - description: 'Issues updated before this date.' + required: false, + description: 'Issues updated before this date.' argument :updated_after, Types::TimeType, - required: false, - description: 'Issues updated after this date.' + required: false, + description: 'Issues updated after this date.' argument :closed_before, Types::TimeType, - required: false, - description: 'Issues closed before this date.' + required: false, + description: 'Issues closed before this date.' argument :closed_after, Types::TimeType, - required: false, - description: 'Issues closed after this date.' + required: false, + description: 'Issues closed after this date.' argument :search, GraphQL::STRING_TYPE, - required: false, - description: 'Search query for issue title or description.' + required: false, + description: 'Search query for issue title or description.' argument :types, [Types::IssueTypeEnum], - as: :issue_types, - description: 'Filter issues by the given issue types.', - required: false + as: :issue_types, + description: 'Filter issues by the given issue types.', + required: false argument :not, Types::Issues::NegatedIssueFilterInputType, - description: 'List of negated params.', + description: 'Negated arguments.', prepare: ->(negated_args, ctx) { negated_args.to_h }, required: false end diff --git a/app/graphql/resolvers/design_management/versions_resolver.rb b/app/graphql/resolvers/design_management/versions_resolver.rb index 619448cbc18..08b29d884b0 100644 --- a/app/graphql/resolvers/design_management/versions_resolver.rb +++ b/app/graphql/resolvers/design_management/versions_resolver.rb @@ -62,6 +62,7 @@ module Resolvers ::DesignManagement::VersionsFinder .new(design_or_collection, current_user, params) .execute + .with_author end def by_id(gid) diff --git a/app/graphql/resolvers/environments_resolver.rb b/app/graphql/resolvers/environments_resolver.rb index df04e70e250..ee604e7b307 100644 --- a/app/graphql/resolvers/environments_resolver.rb +++ b/app/graphql/resolvers/environments_resolver.rb @@ -21,8 +21,8 @@ module Resolvers def resolve(**args) return unless project.present? - EnvironmentsFinder.new(project, context[:current_user], args).execute - rescue EnvironmentsFinder::InvalidStatesError => exception + Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute + rescue Environments::EnvironmentsFinder::InvalidStatesError => exception raise Gitlab::Graphql::Errors::ArgumentError, exception.message end end diff --git a/app/graphql/resolvers/group_packages_resolver.rb b/app/graphql/resolvers/group_packages_resolver.rb index d441cd80249..d91fe84317d 100644 --- a/app/graphql/resolvers/group_packages_resolver.rb +++ b/app/graphql/resolvers/group_packages_resolver.rb @@ -1,8 +1,19 @@ # frozen_string_literal: true +# rubocop: disable Graphql/ResolverType module Resolvers - class GroupPackagesResolver < BaseResolver - type Types::Packages::PackageType.connection_type, null: true + class GroupPackagesResolver < PackagesBaseResolver + # The GraphQL type is defined in the extended class + + argument :sort, Types::Packages::PackageGroupSortEnum, + description: 'Sort packages by this criteria.', + required: false, + default_value: :created_desc + + GROUP_SORT_TO_PARAMS_MAP = SORT_TO_PARAMS_MAP.merge({ + project_path_desc: { order_by: 'project_path', sort: 'desc' }, + project_path_asc: { order_by: 'project_path', sort: 'asc' } + }).freeze def ready?(**args) context[self.class] ||= { executions: 0 } @@ -12,16 +23,11 @@ module Resolvers super end - def resolve(**args) + def resolve(sort:, **filters) return unless packages_available? - ::Packages::GroupPackagesFinder.new(current_user, object).execute - end - - private - - def packages_available? - ::Gitlab.config.packages.enabled + ::Packages::GroupPackagesFinder.new(current_user, object, filters.merge(GROUP_SORT_TO_PARAMS_MAP.fetch(sort))).execute end end end +# rubocop: enable Graphql/ResolverType diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb index 7a67f115abf..93e679b2d0c 100644 --- a/app/graphql/resolvers/issues_resolver.rb +++ b/app/graphql/resolvers/issues_resolver.rb @@ -55,4 +55,4 @@ module Resolvers end end -Resolvers::IssuesResolver.prepend_if_ee('::EE::Resolvers::IssuesResolver') +Resolvers::IssuesResolver.prepend_mod_with('Resolvers::IssuesResolver') diff --git a/app/graphql/resolvers/namespace_projects_resolver.rb b/app/graphql/resolvers/namespace_projects_resolver.rb index 7320c3ce141..86286a744bd 100644 --- a/app/graphql/resolvers/namespace_projects_resolver.rb +++ b/app/graphql/resolvers/namespace_projects_resolver.rb @@ -64,4 +64,4 @@ module Resolvers end end -Resolvers::NamespaceProjectsResolver.prepend_if_ee('::EE::Resolvers::NamespaceProjectsResolver') +Resolvers::NamespaceProjectsResolver.prepend_mod_with('Resolvers::NamespaceProjectsResolver') diff --git a/app/graphql/resolvers/packages_base_resolver.rb b/app/graphql/resolvers/packages_base_resolver.rb new file mode 100644 index 00000000000..3378cc32c9f --- /dev/null +++ b/app/graphql/resolvers/packages_base_resolver.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Resolvers + class PackagesBaseResolver < BaseResolver + type Types::Packages::PackageType.connection_type, null: true + + argument :sort, Types::Packages::PackageSortEnum, + description: 'Sort packages by this criteria.', + required: false, + default_value: :created_desc + + argument :package_name, GraphQL::STRING_TYPE, + description: 'Search a package by name.', + required: false, + default_value: nil + + argument :package_type, Types::Packages::PackageTypeEnum, + description: 'Filter a package by type.', + required: false, + default_value: nil + + argument :status, Types::Packages::PackageStatusEnum, + description: 'Filter a package by status.', + required: false, + default_value: nil + + argument :include_versionless, GraphQL::BOOLEAN_TYPE, + description: 'Include versionless packages.', + required: false, + default_value: false + + SORT_TO_PARAMS_MAP = { + created_desc: { order_by: 'created', sort: 'desc' }, + created_asc: { order_by: 'created', sort: 'asc' }, + name_desc: { order_by: 'name', sort: 'desc' }, + name_asc: { order_by: 'name', sort: 'asc' }, + version_desc: { order_by: 'version', sort: 'desc' }, + version_asc: { order_by: 'version', sort: 'asc' }, + type_desc: { order_by: 'type', sort: 'desc' }, + type_asc: { order_by: 'type', sort: 'asc' } + }.freeze + + def resolve + raise NotImplementedError + end + + private + + def packages_available? + ::Gitlab.config.packages.enabled + end + end +end diff --git a/app/graphql/resolvers/project_packages_resolver.rb b/app/graphql/resolvers/project_packages_resolver.rb index 288e14b41d0..6d66c2fe460 100644 --- a/app/graphql/resolvers/project_packages_resolver.rb +++ b/app/graphql/resolvers/project_packages_resolver.rb @@ -1,19 +1,15 @@ # frozen_string_literal: true +# rubocop: disable Graphql/ResolverType module Resolvers - class ProjectPackagesResolver < BaseResolver - type Types::Packages::PackageType.connection_type, null: true + class ProjectPackagesResolver < PackagesBaseResolver + # The GraphQL type is defined in the extended class - def resolve(**args) + def resolve(sort:, **filters) return unless packages_available? - ::Packages::PackagesFinder.new(object).execute - end - - private - - def packages_available? - ::Gitlab.config.packages.enabled + ::Packages::PackagesFinder.new(object, filters.merge(SORT_TO_PARAMS_MAP.fetch(sort))).execute end end end +# rubocop: enable Graphql/ResolverType diff --git a/app/graphql/resolvers/projects/services_resolver.rb b/app/graphql/resolvers/projects/services_resolver.rb index ec31a7dbe6d..db3037ec591 100644 --- a/app/graphql/resolvers/projects/services_resolver.rb +++ b/app/graphql/resolvers/projects/services_resolver.rb @@ -21,7 +21,7 @@ module Resolvers alias_method :project, :object def resolve(active: nil, type: nil) - servs = project.services + servs = project.integrations servs = servs.by_active_flag(active) unless active.nil? servs = servs.by_type(type) unless type.blank? servs diff --git a/app/graphql/resolvers/release_resolver.rb b/app/graphql/resolvers/release_resolver.rb index 20ef01f8593..67ff5fed0bb 100644 --- a/app/graphql/resolvers/release_resolver.rb +++ b/app/graphql/resolvers/release_resolver.rb @@ -15,8 +15,6 @@ module Resolvers end def resolve(tag_name:) - return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true) - ReleasesFinder.new( project, current_user, diff --git a/app/graphql/resolvers/releases_resolver.rb b/app/graphql/resolvers/releases_resolver.rb index 01c1e9b11e7..358f3c33836 100644 --- a/app/graphql/resolvers/releases_resolver.rb +++ b/app/graphql/resolvers/releases_resolver.rb @@ -23,8 +23,6 @@ module Resolvers }.freeze def resolve(sort:) - return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true) - ReleasesFinder.new( project, current_user, diff --git a/app/graphql/resolvers/repository_branch_names_resolver.rb b/app/graphql/resolvers/repository_branch_names_resolver.rb index 45cfe229b2f..c0a5ea0366f 100644 --- a/app/graphql/resolvers/repository_branch_names_resolver.rb +++ b/app/graphql/resolvers/repository_branch_names_resolver.rb @@ -10,8 +10,16 @@ module Resolvers required: true, description: 'The pattern to search for branch names by.' - def resolve(search_pattern:) - Repositories::BranchNamesFinder.new(object, search: search_pattern).execute + argument :offset, GraphQL::INT_TYPE, + required: true, + description: 'The number of branch names to skip.' + + argument :limit, GraphQL::INT_TYPE, + required: true, + description: 'The number of branch names to return.' + + def resolve(search_pattern:, offset:, limit:) + Repositories::BranchNamesFinder.new(object, offset: offset, limit: limit, search: search_pattern).execute end end end diff --git a/app/graphql/subscriptions/base_subscription.rb b/app/graphql/subscriptions/base_subscription.rb new file mode 100644 index 00000000000..5f7931787df --- /dev/null +++ b/app/graphql/subscriptions/base_subscription.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Subscriptions + class BaseSubscription < GraphQL::Schema::Subscription + object_class Types::BaseObject + field_class Types::BaseField + + def initialize(object:, context:, field:) + super + + # Reset user so that we don't use a stale user for authorization + current_user.reset if current_user + end + + def authorized?(*) + raise NotImplementedError + end + + private + + def unauthorized! + unsubscribe if context.query.subscription_update? + + raise GraphQL::ExecutionError, 'Unauthorized subscription' + end + + def current_user + context[:current_user] + end + end +end diff --git a/app/graphql/subscriptions/issuable_updated.rb b/app/graphql/subscriptions/issuable_updated.rb new file mode 100644 index 00000000000..c1d82bfcf9c --- /dev/null +++ b/app/graphql/subscriptions/issuable_updated.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Subscriptions + class IssuableUpdated < BaseSubscription + include Gitlab::Graphql::Laziness + + payload_type Types::IssuableType + + argument :issuable_id, Types::GlobalIDType[Issuable], + required: true, + description: 'ID of the issuable.' + + def subscribe(issuable_id:) + nil + end + + def authorized?(issuable_id:) + # TODO: remove this check when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + raise Gitlab::Graphql::Errors::ArgumentError, 'Invalid IssuableID' unless issuable_id.is_a?(GlobalID) + + issuable = force(GitlabSchema.find_by_gid(issuable_id)) + + unauthorized! unless issuable && Ability.allowed?(current_user, :"read_#{issuable.to_ability_name}", issuable) + + true + end + end +end diff --git a/app/graphql/types/access_level_enum.rb b/app/graphql/types/access_level_enum.rb index b7eb35ddfc9..299952e4685 100644 --- a/app/graphql/types/access_level_enum.rb +++ b/app/graphql/types/access_level_enum.rb @@ -5,12 +5,12 @@ module Types graphql_name 'AccessLevelEnum' description 'Access level to a resource' - value 'NO_ACCESS', value: Gitlab::Access::NO_ACCESS, description: 'No access' - value 'MINIMAL_ACCESS', value: Gitlab::Access::MINIMAL_ACCESS, description: 'Minimal access' - value 'GUEST', value: Gitlab::Access::GUEST, description: 'Guest access' - value 'REPORTER', value: Gitlab::Access::REPORTER, description: 'Reporter access' - value 'DEVELOPER', value: Gitlab::Access::DEVELOPER, description: 'Developer access' - value 'MAINTAINER', value: Gitlab::Access::MAINTAINER, description: 'Maintainer access' - value 'OWNER', value: Gitlab::Access::OWNER, description: 'Owner access' + value 'NO_ACCESS', value: Gitlab::Access::NO_ACCESS, description: 'No access.' + value 'MINIMAL_ACCESS', value: Gitlab::Access::MINIMAL_ACCESS, description: 'Minimal access.' + value 'GUEST', value: Gitlab::Access::GUEST, description: 'Guest access.' + value 'REPORTER', value: Gitlab::Access::REPORTER, description: 'Reporter access.' + value 'DEVELOPER', value: Gitlab::Access::DEVELOPER, description: 'Developer access.' + value 'MAINTAINER', value: Gitlab::Access::MAINTAINER, description: 'Maintainer access.' + value 'OWNER', value: Gitlab::Access::OWNER, description: 'Owner access.' end end diff --git a/app/graphql/types/alert_management/http_integration_type.rb b/app/graphql/types/alert_management/http_integration_type.rb index 0d5bb50a77c..bba9cb1bbfc 100644 --- a/app/graphql/types/alert_management/http_integration_type.rb +++ b/app/graphql/types/alert_management/http_integration_type.rb @@ -21,4 +21,4 @@ module Types end end -Types::AlertManagement::HttpIntegrationType.prepend_ee_mod +Types::AlertManagement::HttpIntegrationType.prepend_mod diff --git a/app/graphql/types/alert_management/status_enum.rb b/app/graphql/types/alert_management/status_enum.rb index 9d2c7316254..32a578cb155 100644 --- a/app/graphql/types/alert_management/status_enum.rb +++ b/app/graphql/types/alert_management/status_enum.rb @@ -7,7 +7,7 @@ module Types description 'Alert status values' ::AlertManagement::Alert.status_names.each do |status| - value status.to_s.upcase, value: status, description: "#{status.to_s.titleize} status" + value status.to_s.upcase, value: status, description: "#{::AlertManagement::Alert::STATUS_DESCRIPTIONS[status]}." end end end diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb index ff9a5a0611d..536a32f2bdd 100644 --- a/app/graphql/types/base_argument.rb +++ b/app/graphql/types/base_argument.rb @@ -4,10 +4,11 @@ module Types class BaseArgument < GraphQL::Schema::Argument include GitlabStyleDeprecations - attr_reader :deprecation + attr_reader :deprecation, :doc_reference def initialize(*args, **kwargs, &block) @deprecation = gitlab_deprecation(kwargs) + @doc_reference = kwargs.delete(:see) super(*args, **kwargs, &block) end diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb index 518a902a5d7..7ef1cbddbd9 100644 --- a/app/graphql/types/base_enum.rb +++ b/app/graphql/types/base_enum.rb @@ -17,6 +17,9 @@ module Types # declarative_enum MyDeclarativeEnum # end # + # Disabling descriptions rubocop for a false positive here + # rubocop: disable Graphql/Descriptions + # def declarative_enum(enum_mod, use_name: true, use_description: true) graphql_name(enum_mod.name) if use_name description(enum_mod.description) if use_description @@ -25,6 +28,7 @@ module Types value(key.to_s.upcase, **content) end end + # rubocop: enable Graphql/Descriptions # Helper to define an enum member for each element of a Rails AR enum def from_rails_enum(enum, description:) diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb index 7c939f94dde..47caf83eb1c 100644 --- a/app/graphql/types/base_field.rb +++ b/app/graphql/types/base_field.rb @@ -8,10 +8,11 @@ module Types DEFAULT_COMPLEXITY = 1 - attr_reader :deprecation + attr_reader :deprecation, :doc_reference def initialize(**kwargs, &block) @calls_gitaly = !!kwargs.delete(:calls_gitaly) + @doc_reference = kwargs.delete(:see) @constant_complexity = kwargs[:complexity].is_a?(Integer) && kwargs[:complexity] > 0 @requires_argument = !!kwargs.delete(:requires_argument) @authorize = Array.wrap(kwargs.delete(:authorize)) diff --git a/app/graphql/types/blob_viewer_type.rb b/app/graphql/types/blob_viewer_type.rb new file mode 100644 index 00000000000..8d863c32bc7 --- /dev/null +++ b/app/graphql/types/blob_viewer_type.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Types + class BlobViewerType < BaseObject # rubocop:disable Graphql/AuthorizeTypes + graphql_name 'BlobViewer' + description 'Represents how the blob content should be displayed' + + field :type, Types::BlobViewers::TypeEnum, + description: 'Type of blob viewer.', + null: false + + field :load_async, GraphQL::BOOLEAN_TYPE, + description: 'Shows whether the blob content is loaded asynchronously.', + null: false + + field :collapsed, GraphQL::BOOLEAN_TYPE, + description: 'Shows whether the blob should be displayed collapsed.', + method: :collapsed?, + null: false + + field :too_large, GraphQL::BOOLEAN_TYPE, + description: 'Shows whether the blob is too large to be displayed.', + method: :too_large?, + null: false + + field :render_error, GraphQL::STRING_TYPE, + description: 'Error rendering the blob content.', + null: true + + field :file_type, GraphQL::STRING_TYPE, + description: 'Content file type.', + method: :partial_name, + null: false + + 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 diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb index f215aa255de..dc10716dcb0 100644 --- a/app/graphql/types/board_list_type.rb +++ b/app/graphql/types/board_list_type.rb @@ -50,4 +50,4 @@ module Types # rubocop: enable Graphql/AuthorizeTypes end -Types::BoardListType.prepend_if_ee('::EE::Types::BoardListType') +Types::BoardListType.prepend_mod_with('Types::BoardListType') diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb index 42d8eecc366..292809b0d64 100644 --- a/app/graphql/types/board_type.rb +++ b/app/graphql/types/board_type.rb @@ -41,4 +41,4 @@ module Types end end -Types::BoardType.prepend_if_ee('::EE::Types::BoardType') +Types::BoardType.prepend_mod_with('Types::BoardType') diff --git a/app/graphql/types/boards/board_issue_input_base_type.rb b/app/graphql/types/boards/board_issue_input_base_type.rb index 7cf2dcb9c82..633221e61d1 100644 --- a/app/graphql/types/boards/board_issue_input_base_type.rb +++ b/app/graphql/types/boards/board_issue_input_base_type.rb @@ -4,6 +4,10 @@ module Types module Boards # rubocop: disable Graphql/AuthorizeTypes class BoardIssueInputBaseType < BoardIssuableInputBaseType + argument :iids, [GraphQL::STRING_TYPE], + required: false, + description: 'List of IIDs of issues. For example ["1", "2"].' + argument :milestone_title, GraphQL::STRING_TYPE, required: false, description: 'Filter by milestone title.' @@ -19,4 +23,4 @@ module Types end end -Types::Boards::BoardIssueInputBaseType.prepend_if_ee('::EE::Types::Boards::BoardIssueInputBaseType') +Types::Boards::BoardIssueInputBaseType.prepend_mod_with('Types::Boards::BoardIssueInputBaseType') diff --git a/app/graphql/types/boards/board_issue_input_type.rb b/app/graphql/types/boards/board_issue_input_type.rb index 8c0e37e5cb7..7580b0378fe 100644 --- a/app/graphql/types/boards/board_issue_input_type.rb +++ b/app/graphql/types/boards/board_issue_input_type.rb @@ -8,10 +8,7 @@ module Types argument :not, NegatedBoardIssueInputType, required: false, prepare: ->(negated_args, ctx) { negated_args.to_h }, - description: <<~MD - List of negated arguments. - Warning: this argument is experimental and a subject to change in future. - MD + description: 'List of negated arguments.' argument :search, GraphQL::STRING_TYPE, required: false, @@ -24,4 +21,4 @@ module Types end end -Types::Boards::BoardIssueInputType.prepend_if_ee('::EE::Types::Boards::BoardIssueInputType') +Types::Boards::BoardIssueInputType.prepend_mod_with('Types::Boards::BoardIssueInputType') diff --git a/app/graphql/types/boards/negated_board_issue_input_type.rb b/app/graphql/types/boards/negated_board_issue_input_type.rb index a0fab2ae969..834d94d4de6 100644 --- a/app/graphql/types/boards/negated_board_issue_input_type.rb +++ b/app/graphql/types/boards/negated_board_issue_input_type.rb @@ -7,4 +7,4 @@ module Types end end -Types::Boards::NegatedBoardIssueInputType.prepend_if_ee('::EE::Types::Boards::NegatedBoardIssueInputType') +Types::Boards::NegatedBoardIssueInputType.prepend_mod_with('Types::Boards::NegatedBoardIssueInputType') diff --git a/app/graphql/types/ci/code_quality_degradation_severity_enum.rb b/app/graphql/types/ci/code_quality_degradation_severity_enum.rb new file mode 100644 index 00000000000..742ac922198 --- /dev/null +++ b/app/graphql/types/ci/code_quality_degradation_severity_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ci + class CodeQualityDegradationSeverityEnum < BaseEnum + graphql_name 'CodeQualityDegradationSeverity' + + ::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.keys.each do |status| + value status.upcase, + description: "Code Quality degradation has a status of #{status}.", + value: status + end + end + end +end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index 94a256fed3d..5ed4d823aee 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -8,6 +8,8 @@ module Types connection_type_class(Types::CountableConnectionType) + expose_permissions Types::PermissionTypes::Ci::Job + field :id, ::Types::GlobalIDType[::CommitStatus].as('JobID'), null: true, description: 'ID of the job.' field :pipeline, Types::Ci::PipelineType, null: true, @@ -23,7 +25,7 @@ module Types field :stage, Types::Ci::StageType, null: true, description: 'Stage of the job.' field :allow_failure, ::GraphQL::BOOLEAN_TYPE, null: false, - description: 'Whether this job is allowed to fail.' + description: 'Whether the job is allowed to fail.' field :duration, GraphQL::INT_TYPE, null: true, description: 'Duration of the job in seconds.' field :tags, [GraphQL::STRING_TYPE], null: true, @@ -41,6 +43,12 @@ module Types field :scheduled_at, Types::TimeType, null: true, description: 'Schedule for the build.' + # Life-cycle durations: + field :queued_duration, + type: Types::DurationType, + null: true, + description: 'How long the job was enqueued before starting.' + field :detailed_status, Types::Ci::DetailedStatusType, null: true, description: 'Detailed status of the job.' field :artifacts, Types::Ci::JobArtifactType.connection_type, null: true, @@ -63,8 +71,16 @@ module Types description: 'Indicates the job can be canceled.' field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?, description: 'Indicates the job is active.' + field :stuck, GraphQL::BOOLEAN_TYPE, null: false, method: :stuck?, + description: 'Indicates the job is stuck.' field :coverage, GraphQL::FLOAT_TYPE, null: true, description: 'Coverage level of the job.' + field :created_by_tag, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Whether the job was created by a tag.' + field :manual_job, GraphQL::BOOLEAN_TYPE, null: true, + description: 'Whether the job has a manual action.' + field :triggered, GraphQL::BOOLEAN_TYPE, null: true, + description: 'Whether the job was triggered.' def pipeline Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find @@ -123,6 +139,18 @@ module Types def coverage object&.coverage end + + def created_by_tag + object.tag? + end + + def manual_job + object.try(:action?) + end + + def triggered + object.try(:trigger_request) + end end end end diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb index e0b2020dcc1..2800454a999 100644 --- a/app/graphql/types/ci/pipeline_status_enum.rb +++ b/app/graphql/types/ci/pipeline_status_enum.rb @@ -5,7 +5,7 @@ module Types class PipelineStatusEnum < BaseEnum ::Ci::Pipeline.all_state_names.each do |state_symbol| value state_symbol.to_s.upcase, - description: ::Ci::Pipeline::STATUSES_DESCRIPTION[state_symbol], + description: "#{::Ci::Pipeline::STATUSES_DESCRIPTION[state_symbol]}.", value: state_symbol.to_s end end diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index 2e83f6c1f5a..2eeddaca6ba 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -24,6 +24,9 @@ module Types field :before_sha, GraphQL::STRING_TYPE, null: true, description: 'Base SHA of the source branch.' + field :complete, GraphQL::BOOLEAN_TYPE, null: false, method: :complete?, + description: 'Indicates if a pipeline is complete.' + field :status, PipelineStatusEnum, null: false, description: "Status of the pipeline (#{::Ci::Pipeline.all_state_names.compact.join(', ').upcase})" @@ -39,6 +42,9 @@ module Types field :duration, GraphQL::INT_TYPE, null: true, description: 'Duration of the pipeline in seconds.' + field :queued_duration, Types::DurationType, null: true, + description: 'How long the pipeline was queued before starting.' + field :coverage, GraphQL::FLOAT_TYPE, null: true, description: 'Coverage percentage.' @@ -57,12 +63,17 @@ module Types field :committed_at, Types::TimeType, null: true, description: "Timestamp of the pipeline's commit." - field :stages, Types::Ci::StageType.connection_type, null: true, + field :stages, + type: Types::Ci::StageType.connection_type, + null: true, + authorize: :read_commit_status, description: 'Stages of the pipeline.', extras: [:lookahead], resolver: Resolvers::Ci::PipelineStagesResolver - field :user, Types::UserType, null: true, + field :user, + type: Types::UserType, + null: true, description: 'Pipeline user.' field :retryable, GraphQL::BOOLEAN_TYPE, @@ -78,12 +89,14 @@ module Types field :jobs, ::Types::Ci::JobType.connection_type, null: true, + authorize: :read_commit_status, description: 'Jobs belonging to the pipeline.', resolver: ::Resolvers::Ci::JobsResolver field :job, type: ::Types::Ci::JobType, null: true, + authorize: :read_commit_status, description: 'A specific job in this pipeline, either by name or ID.' do argument :id, type: ::Types::GlobalIDType[::CommitStatus], @@ -95,7 +108,10 @@ module Types description: 'Name of the job.' end - field :source_job, Types::Ci::JobType, null: true, + field :source_job, + type: Types::Ci::JobType, + null: true, + authorize: :read_commit_status, description: 'Job where pipeline was triggered from.' field :downstream, Types::Ci::PipelineType.connection_type, null: true, @@ -166,4 +182,4 @@ module Types end end -Types::Ci::PipelineType.prepend_if_ee('::EE::Types::Ci::PipelineType') +Types::Ci::PipelineType.prepend_mod_with('Types::Ci::PipelineType') diff --git a/app/graphql/types/ci/runner_access_level_enum.rb b/app/graphql/types/ci/runner_access_level_enum.rb new file mode 100644 index 00000000000..e98f80336f1 --- /dev/null +++ b/app/graphql/types/ci/runner_access_level_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ci + class RunnerAccessLevelEnum < BaseEnum + graphql_name 'CiRunnerAccessLevel' + + ::Ci::Runner.access_levels.keys.each do |type| + value type.upcase, + description: "A runner that is #{type.tr('_', ' ')}.", + value: type + end + end + end +end diff --git a/app/graphql/types/ci/runner_sort_enum.rb b/app/graphql/types/ci/runner_sort_enum.rb new file mode 100644 index 00000000000..550e870316a --- /dev/null +++ b/app/graphql/types/ci/runner_sort_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module Ci + class RunnerSortEnum < BaseEnum + graphql_name 'CiRunnerSort' + description 'Values for sorting runners' + + value 'CONTACTED_ASC', 'Ordered by contacted_at in ascending order.', value: :contacted_asc + value 'CREATED_DESC', 'Ordered by created_date in descending order.', value: :created_date + end + end +end diff --git a/app/graphql/types/ci/runner_status_enum.rb b/app/graphql/types/ci/runner_status_enum.rb new file mode 100644 index 00000000000..ad69175e44a --- /dev/null +++ b/app/graphql/types/ci/runner_status_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ci + class RunnerStatusEnum < BaseEnum + graphql_name 'CiRunnerStatus' + + ::Ci::Runner::AVAILABLE_STATUSES.each do |status| + value status.to_s.upcase, + description: "A runner that is #{status.to_s.tr('_', ' ')}.", + value: status.to_sym + end + end + end +end diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb new file mode 100644 index 00000000000..3abed7289d5 --- /dev/null +++ b/app/graphql/types/ci/runner_type.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Types + module Ci + class RunnerType < BaseObject + graphql_name 'CiRunner' + authorize :read_runner + + field :id, ::Types::GlobalIDType[::Ci::Runner], null: false, + description: 'ID of the runner.' + field :description, GraphQL::STRING_TYPE, null: true, + description: 'Description of the runner.' + field :contacted_at, Types::TimeType, null: true, + description: 'Last contact from the runner.', + method: :contacted_at + field :maximum_timeout, GraphQL::INT_TYPE, null: true, + description: 'Maximum timeout (in seconds) for jobs processed by the runner.' + field :access_level, ::Types::Ci::RunnerAccessLevelEnum, null: false, + description: 'Access level of the runner.' + field :active, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates the runner is allowed to receive jobs.' + field :status, ::Types::Ci::RunnerStatusEnum, null: false, + description: 'Status of the runner.' + field :version, GraphQL::STRING_TYPE, null: false, + description: 'Version of the runner.' + field :short_sha, GraphQL::STRING_TYPE, null: true, + description: %q(First eight characters of the runner's token used to authenticate new job requests. Used as the runner's unique ID.) + field :revision, GraphQL::STRING_TYPE, null: false, + description: 'Revision of the runner.' + field :locked, GraphQL::BOOLEAN_TYPE, null: true, + description: 'Indicates the runner is locked.' + field :run_untagged, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates the runner is able to run untagged jobs.' + field :ip_address, GraphQL::STRING_TYPE, null: false, + description: 'IP address of the runner.' + field :runner_type, ::Types::Ci::RunnerTypeEnum, null: false, + description: 'Type of the runner.' + field :tag_list, [GraphQL::STRING_TYPE], null: true, + description: 'Tags associated with the runner.' + end + end +end diff --git a/app/graphql/types/ci/runner_type_enum.rb b/app/graphql/types/ci/runner_type_enum.rb new file mode 100644 index 00000000000..f771635f4ed --- /dev/null +++ b/app/graphql/types/ci/runner_type_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ci + class RunnerTypeEnum < BaseEnum + graphql_name 'CiRunnerType' + + ::Ci::Runner.runner_types.keys.each do |type| + value type.upcase, + description: "A runner that is #{type.tr('_', ' ')}.", + value: type + end + end + end +end diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb index 56b4f248697..1be9e3192a9 100644 --- a/app/graphql/types/ci/stage_type.rb +++ b/app/graphql/types/ci/stage_type.rb @@ -2,20 +2,26 @@ module Types module Ci - # rubocop: disable Graphql/AuthorizeTypes class StageType < BaseObject graphql_name 'CiStage' + authorize :read_commit_status - field :name, GraphQL::STRING_TYPE, null: true, - description: 'Name of the stage.' - field :groups, Ci::GroupType.connection_type, null: true, - extras: [:lookahead], - description: 'Group of jobs for the stage.' - field :detailed_status, Types::Ci::DetailedStatusType, null: true, - description: 'Detailed status of the stage.' - field :jobs, Ci::JobType.connection_type, null: true, - description: 'Jobs for the stage.', - method: 'latest_statuses' + field :name, + type: GraphQL::STRING_TYPE, + null: true, + description: 'Name of the stage.' + field :groups, + type: Ci::GroupType.connection_type, + null: true, + extras: [:lookahead], + description: 'Group of jobs for the stage.' + field :detailed_status, Types::Ci::DetailedStatusType, + null: true, + description: 'Detailed status of the stage.' + field :jobs, Ci::JobType.connection_type, + null: true, + description: 'Jobs for the stage.', + method: 'latest_statuses' def detailed_status object.detailed_status(current_user) @@ -37,33 +43,6 @@ module Types key = indexed[stage_id] groups = ::Ci::Group.fabricate(project, key.stage, statuses) - if Feature.enabled?(:ci_no_empty_groups, project) - groups.each do |group| - rejected = group.jobs.reject { |job| Ability.allowed?(current_user, :read_commit_status, job) } - group.jobs.select! { |job| Ability.allowed?(current_user, :read_commit_status, job) } - next unless group.jobs.empty? - - exc = StandardError.new('Empty Ci::Group') - traces = rejected.map do |job| - trace = [] - policy = Ability.policy_for(current_user, job) - policy.debug(:read_commit_status, trace) - trace - end - extra = { - current_user_id: current_user&.id, - project_id: project.id, - pipeline_id: pl.id, - stage_id: stage_id, - group_name: group.name, - rejected_job_ids: rejected.map(&:id), - rejected_traces: traces - } - Gitlab::ErrorTracking.track_exception(exc, extra) - end - groups.reject! { |group| group.jobs.empty? } - end - loader.call(key, groups) end end diff --git a/app/graphql/types/ci/template_type.rb b/app/graphql/types/ci/template_type.rb new file mode 100644 index 00000000000..5f07fa16928 --- /dev/null +++ b/app/graphql/types/ci/template_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + module Ci + # rubocop: disable Graphql/AuthorizeTypes + class TemplateType < BaseObject + graphql_name 'CiTemplate' + description 'GitLab CI/CD configuration template.' + + field :name, GraphQL::STRING_TYPE, null: false, + description: 'Name of the CI template.' + field :content, GraphQL::STRING_TYPE, null: false, + description: 'Contents of the CI template.' + end + end +end diff --git a/app/graphql/types/container_expiration_policy_cadence_enum.rb b/app/graphql/types/container_expiration_policy_cadence_enum.rb index bb8bdf2197b..ac923f64b52 100644 --- a/app/graphql/types/container_expiration_policy_cadence_enum.rb +++ b/app/graphql/types/container_expiration_policy_cadence_enum.rb @@ -11,7 +11,7 @@ module Types }.freeze ::ContainerExpirationPolicy.cadence_options.each do |option, description| - value OPTIONS_MAPPING[option], description, value: option.to_s + value OPTIONS_MAPPING[option], description: description, value: option.to_s end end end diff --git a/app/graphql/types/container_expiration_policy_keep_enum.rb b/app/graphql/types/container_expiration_policy_keep_enum.rb index 7632df61092..ca6fbbcf5ae 100644 --- a/app/graphql/types/container_expiration_policy_keep_enum.rb +++ b/app/graphql/types/container_expiration_policy_keep_enum.rb @@ -12,7 +12,7 @@ module Types }.freeze ::ContainerExpirationPolicy.keep_n_options.each do |option, description| - value OPTIONS_MAPPING[option], description, value: option + value OPTIONS_MAPPING[option], description: description, value: option end end end diff --git a/app/graphql/types/container_expiration_policy_older_than_enum.rb b/app/graphql/types/container_expiration_policy_older_than_enum.rb index da70534b0d7..7364910f8cd 100644 --- a/app/graphql/types/container_expiration_policy_older_than_enum.rb +++ b/app/graphql/types/container_expiration_policy_older_than_enum.rb @@ -10,7 +10,7 @@ module Types }.freeze ::ContainerExpirationPolicy.older_than_options.each do |option, description| - value OPTIONS_MAPPING[option], description, value: option.to_s + value OPTIONS_MAPPING[option], description: description, value: option.to_s end end end diff --git a/app/graphql/types/design_management/version_type.rb b/app/graphql/types/design_management/version_type.rb index 4bc71aef0f4..265d6185110 100644 --- a/app/graphql/types/design_management/version_type.rb +++ b/app/graphql/types/design_management/version_type.rb @@ -32,6 +32,10 @@ module Types null: false, description: 'A particular design as of this version, provided it is visible at this version.', resolver: ::Resolvers::DesignManagement::Version::DesignsAtVersionResolver.single + + field :author, Types::UserType, null: false, description: 'Author of the version.' + field :created_at, Types::TimeType, null: false, + description: 'Timestamp of when the version was created.' end end end diff --git a/app/graphql/types/duration_type.rb b/app/graphql/types/duration_type.rb new file mode 100644 index 00000000000..260a2975ec1 --- /dev/null +++ b/app/graphql/types/duration_type.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Types + class DurationType < BaseScalar + graphql_name 'Duration' + description <<~DESC + Duration between two instants, represented as a fractional number of seconds. + + For example: 12.3334 + DESC + + def self.coerce_input(value, ctx) + case value + when Float + value + when Integer + value.to_f + when NilClass + raise GraphQL::CoercionError, 'Cannot be nil' + else + raise GraphQL::CoercionError, "Expected number: got #{value.class}" + end + end + + def self.coerce_result(value, ctx) + value.to_f + end + end +end diff --git a/app/graphql/types/group_member_relation_enum.rb b/app/graphql/types/group_member_relation_enum.rb index aa2e73d4944..54e2a175f33 100644 --- a/app/graphql/types/group_member_relation_enum.rb +++ b/app/graphql/types/group_member_relation_enum.rb @@ -6,7 +6,7 @@ module Types 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" + value member_relation.to_s.upcase, value: member_relation, description: "#{::GroupMembersFinder::RELATIONS_DESCRIPTIONS[member_relation]}." end end end diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index a44281b2bdf..27f4ae47c41 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -144,7 +144,7 @@ module Types resolver: Resolvers::GroupLabelsResolver field :timelogs, ::Types::TimelogType.connection_type, null: false, - description: 'Time logged on issues in the group and its subgroups.', + description: 'Time logged on issues and merge requests in the group and its subgroups.', extras: [:lookahead], complexity: 5, resolver: ::Resolvers::TimelogResolver @@ -169,4 +169,4 @@ module Types end end -Types::GroupType.prepend_if_ee('EE::Types::GroupType') +Types::GroupType.prepend_mod_with('Types::GroupType') diff --git a/app/graphql/types/issuable_type.rb b/app/graphql/types/issuable_type.rb new file mode 100644 index 00000000000..6ca74087f8a --- /dev/null +++ b/app/graphql/types/issuable_type.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Types + class IssuableType < BaseUnion + graphql_name 'Issuable' + description 'Represents an issuable.' + + possible_types Types::IssueType, Types::MergeRequestType + + def self.resolve_type(object, context) + case object + when Issue + Types::IssueType + when MergeRequest + Types::MergeRequestType + else + raise 'Unsupported issuable type' + end + end + end +end + +Types::IssuableType.prepend_mod_with('Types::IssuableType') diff --git a/app/graphql/types/issue_connection_type.rb b/app/graphql/types/issue_connection_type.rb index 2e0f05f741e..7abb2253fac 100644 --- a/app/graphql/types/issue_connection_type.rb +++ b/app/graphql/types/issue_connection_type.rb @@ -6,4 +6,4 @@ module Types end end -Types::IssueConnectionType.prepend_if_ee('::EE::Types::IssueConnectionType') +Types::IssueConnectionType.prepend_mod_with('Types::IssueConnectionType') diff --git a/app/graphql/types/issue_sort_enum.rb b/app/graphql/types/issue_sort_enum.rb index bf900fe3525..e730a51b60e 100644 --- a/app/graphql/types/issue_sort_enum.rb +++ b/app/graphql/types/issue_sort_enum.rb @@ -13,4 +13,4 @@ module Types end end -Types::IssueSortEnum.prepend_if_ee('::EE::Types::IssueSortEnum') +Types::IssueSortEnum.prepend_mod_with('Types::IssueSortEnum') diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb index 34c824fe9fb..0ccd1e2cebd 100644 --- a/app/graphql/types/issue_type.rb +++ b/app/graphql/types/issue_type.rb @@ -153,4 +153,4 @@ module Types end end -Types::IssueType.prepend_if_ee('::EE::Types::IssueType') +Types::IssueType.prepend_mod_with('Types::IssueType') diff --git a/app/graphql/types/issues/negated_issue_filter_input_type.rb b/app/graphql/types/issues/negated_issue_filter_input_type.rb index 10bf6f21792..8a2e75ed9ba 100644 --- a/app/graphql/types/issues/negated_issue_filter_input_type.rb +++ b/app/graphql/types/issues/negated_issue_filter_input_type.rb @@ -24,4 +24,4 @@ module Types end end -Types::Issues::NegatedIssueFilterInputType.prepend_if_ee('::EE::Types::Issues::NegatedIssueFilterInputType') +Types::Issues::NegatedIssueFilterInputType.prepend_mod_with('Types::Issues::NegatedIssueFilterInputType') diff --git a/app/graphql/types/label_type.rb b/app/graphql/types/label_type.rb index 4e8718a80da..cb6b0312aa3 100644 --- a/app/graphql/types/label_type.rb +++ b/app/graphql/types/label_type.rb @@ -23,5 +23,7 @@ module Types description: 'When this label was created.' field :updated_at, Types::TimeType, null: false, description: 'When this label was last updated.' + field :remove_on_close, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Whether the label should be removed from an issue when the issue is closed.' end end diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index c8ccf9d8aff..4eeeaa4f5d0 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -54,6 +54,9 @@ module Types 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, + deprecated: { reason: 'Use `draft`', milestone: '13.12' }, + description: 'Indicates if the merge request is a draft.' + field :draft, GraphQL::BOOLEAN_TYPE, method: :draft?, null: false, description: 'Indicates if the merge request is a draft.' field :merge_when_pipeline_succeeds, GraphQL::BOOLEAN_TYPE, null: true, description: 'Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS).' @@ -130,7 +133,10 @@ module Types field :milestone, Types::MilestoneType, null: true, description: 'The milestone of the merge request.' - field :assignees, Types::UserType.connection_type, null: true, complexity: 5, + field :assignees, + type: Types::MergeRequests::AssigneeType.connection_type, + null: true, + complexity: 5, description: 'Assignees of the merge request.' field :reviewers, type: Types::MergeRequests::ReviewerType.connection_type, @@ -257,4 +263,4 @@ module Types end end -Types::MergeRequestType.prepend_if_ee('::EE::Types::MergeRequestType') +Types::MergeRequestType.prepend_mod_with('Types::MergeRequestType') diff --git a/app/graphql/types/merge_requests/assignee_type.rb b/app/graphql/types/merge_requests/assignee_type.rb new file mode 100644 index 00000000000..8448477370e --- /dev/null +++ b/app/graphql/types/merge_requests/assignee_type.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + module MergeRequests + class AssigneeType < ::Types::UserType + include FindClosest + include ::Types::MergeRequests::InteractsWithMergeRequest + + graphql_name 'MergeRequestAssignee' + description 'A user assigned to a merge request.' + authorize :read_user + end + end +end diff --git a/app/graphql/types/merge_requests/interacts_with_merge_request.rb b/app/graphql/types/merge_requests/interacts_with_merge_request.rb new file mode 100644 index 00000000000..d685ac4d3c9 --- /dev/null +++ b/app/graphql/types/merge_requests/interacts_with_merge_request.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Types + module MergeRequests + module InteractsWithMergeRequest + extend ActiveSupport::Concern + + included do + field :merge_request_interaction, + type: ::Types::UserMergeRequestInteractionType, + null: true, + extras: [:parent], + description: "Details of this user's interactions with the merge request." + end + + def merge_request_interaction(parent:) + merge_request = closest_parent(::Types::MergeRequestType, parent) + return unless merge_request + + Users::MergeRequestInteraction.new(user: object, merge_request: merge_request) + end + end + end +end diff --git a/app/graphql/types/merge_requests/reviewer_type.rb b/app/graphql/types/merge_requests/reviewer_type.rb index 09ced39844a..1ced821c839 100644 --- a/app/graphql/types/merge_requests/reviewer_type.rb +++ b/app/graphql/types/merge_requests/reviewer_type.rb @@ -4,23 +4,11 @@ module Types module MergeRequests class ReviewerType < ::Types::UserType include FindClosest + include ::Types::MergeRequests::InteractsWithMergeRequest graphql_name 'MergeRequestReviewer' - description 'A user from whom a merge request review has been requested.' + description 'A user assigned to a merge request as a reviewer.' authorize :read_user - - field :merge_request_interaction, - type: ::Types::UserMergeRequestInteractionType, - null: true, - extras: [:parent], - description: "Details of this user's interactions with the merge request." - - def merge_request_interaction(parent:) - merge_request = closest_parent(::Types::MergeRequestType, parent) - return unless merge_request - - Users::MergeRequestInteraction.new(user: object, merge_request: merge_request) - end end end end diff --git a/app/graphql/types/metadata/kas_type.rb b/app/graphql/types/metadata/kas_type.rb new file mode 100644 index 00000000000..8af4c23270b --- /dev/null +++ b/app/graphql/types/metadata/kas_type.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Types + module Metadata + class KasType < ::Types::BaseObject + graphql_name 'Kas' + + authorize :read_instance_metadata + + field :enabled, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates whether the Kubernetes Agent Server is enabled.' + field :version, GraphQL::STRING_TYPE, null: true, + description: 'KAS version.' + field :external_url, GraphQL::STRING_TYPE, null: true, + description: 'The URL used by the Agents to communicate with KAS.' + end + end +end diff --git a/app/graphql/types/metadata_type.rb b/app/graphql/types/metadata_type.rb index 0c360d4f292..851c2a3f1e3 100644 --- a/app/graphql/types/metadata_type.rb +++ b/app/graphql/types/metadata_type.rb @@ -10,5 +10,7 @@ module Types description: 'Version.' field :revision, GraphQL::STRING_TYPE, null: false, description: 'Revision.' + field :kas, ::Types::Metadata::KasType, null: false, + description: 'Metadata about KAS.' end end diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb index 91a5109c748..eafede26c9e 100644 --- a/app/graphql/types/milestone_type.rb +++ b/app/graphql/types/milestone_type.rb @@ -57,11 +57,9 @@ module Types description: 'Milestone statistics.' def stats - return unless Feature.enabled?(:graphql_milestone_stats, milestone.project || milestone.group, default_enabled: true) - milestone end end end -Types::MilestoneType.prepend_if_ee('::EE::Types::MilestoneType') +Types::MilestoneType.prepend_mod_with('Types::MilestoneType') diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 5a9c7b32deb..54a06ed5342 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -16,6 +16,7 @@ module Types mount_mutation Mutations::AlertManagement::HttpIntegration::ResetToken mount_mutation Mutations::AlertManagement::HttpIntegration::Destroy mount_mutation Mutations::Security::CiConfiguration::ConfigureSast + mount_mutation Mutations::Security::CiConfiguration::ConfigureSecretDetection mount_mutation Mutations::AlertManagement::PrometheusIntegration::Create mount_mutation Mutations::AlertManagement::PrometheusIntegration::Update mount_mutation Mutations::AlertManagement::PrometheusIntegration::ResetToken @@ -51,7 +52,10 @@ module Types mount_mutation Mutations::MergeRequests::SetLocked mount_mutation Mutations::MergeRequests::SetMilestone mount_mutation Mutations::MergeRequests::SetSubscription - mount_mutation Mutations::MergeRequests::SetWip, calls_gitaly: true + mount_mutation Mutations::MergeRequests::SetWip, + calls_gitaly: true, + deprecated: { reason: 'Use mergeRequestSetDraft', milestone: '13.12' } + mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true mount_mutation Mutations::MergeRequests::SetAssignees mount_mutation Mutations::MergeRequests::ReviewerRereview mount_mutation Mutations::Metrics::Dashboard::Annotations::Create @@ -93,10 +97,12 @@ module Types mount_mutation Mutations::Ci::Pipeline::Destroy mount_mutation Mutations::Ci::Pipeline::Retry mount_mutation Mutations::Ci::CiCdSettingsUpdate + mount_mutation Mutations::Ci::Job::Play + mount_mutation Mutations::Ci::Job::Retry mount_mutation Mutations::Namespace::PackageSettings::Update mount_mutation Mutations::UserCallouts::Create end end ::Types::MutationType.prepend(::Types::DeprecatedMutations) -::Types::MutationType.prepend_if_ee('::EE::Types::MutationType') +::Types::MutationType.prepend_mod_with('Types::MutationType') diff --git a/app/graphql/types/namespace/package_settings_type.rb b/app/graphql/types/namespace/package_settings_type.rb index 0720a1cfb4b..af091515979 100644 --- a/app/graphql/types/namespace/package_settings_type.rb +++ b/app/graphql/types/namespace/package_settings_type.rb @@ -10,5 +10,7 @@ module Types field :maven_duplicates_allowed, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates whether duplicate Maven packages are allowed for this namespace.' field :maven_duplicate_exception_regex, Types::UntrustedRegexp, null: true, description: 'When maven_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.' + field :generic_duplicates_allowed, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates whether duplicate generic packages are allowed for this namespace.' + field :generic_duplicate_exception_regex, Types::UntrustedRegexp, null: true, description: 'When generic_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.' end end diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb index da983399a11..96eff8a46b0 100644 --- a/app/graphql/types/namespace_type.rb +++ b/app/graphql/types/namespace_type.rb @@ -48,4 +48,4 @@ module Types end end -Types::NamespaceType.prepend_if_ee('EE::Types::NamespaceType') +Types::NamespaceType.prepend_mod_with('Types::NamespaceType') diff --git a/app/graphql/types/notes/noteable_type.rb b/app/graphql/types/notes/noteable_type.rb index f8626d249a1..a82a76f9c87 100644 --- a/app/graphql/types/notes/noteable_type.rb +++ b/app/graphql/types/notes/noteable_type.rb @@ -28,4 +28,4 @@ module Types end end -Types::Notes::NoteableType.prepend_if_ee('::EE::Types::Notes::NoteableType') +Types::Notes::NoteableType.prepend_mod_with('Types::Notes::NoteableType') diff --git a/app/graphql/types/notes/position_type_enum.rb b/app/graphql/types/notes/position_type_enum.rb index 9939f6511ce..18934636670 100644 --- a/app/graphql/types/notes/position_type_enum.rb +++ b/app/graphql/types/notes/position_type_enum.rb @@ -6,8 +6,8 @@ module Types graphql_name 'DiffPositionType' description 'Type of file the position refers to' - value 'text', description: "A text file" - value 'image', description: "An image" + value 'text', description: "A text file." + value 'image', description: "An image." end end end diff --git a/app/graphql/types/packages/maven/metadatum_type.rb b/app/graphql/types/packages/maven/metadatum_type.rb new file mode 100644 index 00000000000..bdb250ef96b --- /dev/null +++ b/app/graphql/types/packages/maven/metadatum_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module Packages + module Maven + class MetadatumType < BaseObject + graphql_name 'MavenMetadata' + description 'Maven metadata' + + authorize :read_package + + field :id, ::Types::GlobalIDType[::Packages::Maven::Metadatum], null: false, description: 'ID of the metadatum.' + field :created_at, Types::TimeType, null: false, description: 'Date of creation.' + field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' + field :path, GraphQL::STRING_TYPE, null: false, description: 'Path of the Maven package.' + field :app_group, GraphQL::STRING_TYPE, null: false, description: 'App group of the Maven package.' + field :app_version, GraphQL::STRING_TYPE, null: true, description: 'App version of the Maven package.' + field :app_name, GraphQL::STRING_TYPE, null: false, description: 'App name of the Maven package.' + end + end + end +end diff --git a/app/graphql/types/packages/metadata_type.rb b/app/graphql/types/packages/metadata_type.rb index 4ab6707df88..94880cb9b22 100644 --- a/app/graphql/types/packages/metadata_type.rb +++ b/app/graphql/types/packages/metadata_type.rb @@ -6,7 +6,7 @@ module Types graphql_name 'PackageMetadata' description 'Represents metadata associated with a Package' - possible_types ::Types::Packages::Composer::MetadatumType, ::Types::Packages::Conan::MetadatumType + possible_types ::Types::Packages::Composer::MetadatumType, ::Types::Packages::Conan::MetadatumType, ::Types::Packages::Maven::MetadatumType, ::Types::Packages::Nuget::MetadatumType def self.resolve_type(object, context) case object @@ -14,6 +14,10 @@ module Types ::Types::Packages::Composer::MetadatumType when ::Packages::Conan::Metadatum ::Types::Packages::Conan::MetadatumType + when ::Packages::Maven::Metadatum + ::Types::Packages::Maven::MetadatumType + when ::Packages::Nuget::Metadatum + ::Types::Packages::Nuget::MetadatumType else # NOTE: This method must be kept in sync with `PackageWithoutVersionsType#metadata`, # which must never produce data that this discriminator cannot handle. diff --git a/app/graphql/types/packages/nuget/metadatum_type.rb b/app/graphql/types/packages/nuget/metadatum_type.rb new file mode 100644 index 00000000000..63fae2fb197 --- /dev/null +++ b/app/graphql/types/packages/nuget/metadatum_type.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Types + module Packages + module Nuget + class MetadatumType < BaseObject + graphql_name 'NugetMetadata' + description 'Nuget metadata' + + authorize :read_package + + field :id, ::Types::GlobalIDType[::Packages::Nuget::Metadatum], null: false, description: 'ID of the metadatum.' + field :license_url, GraphQL::STRING_TYPE, null: false, description: 'License URL of the Nuget package.' + field :project_url, GraphQL::STRING_TYPE, null: false, description: 'Project URL of the Nuget package.' + field :icon_url, GraphQL::STRING_TYPE, null: false, description: 'Icon URL of the Nuget package.' + end + end + end +end diff --git a/app/graphql/types/packages/package_group_sort_enum.rb b/app/graphql/types/packages/package_group_sort_enum.rb new file mode 100644 index 00000000000..70fb27ec0db --- /dev/null +++ b/app/graphql/types/packages/package_group_sort_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Packages + class PackageGroupSortEnum < PackageSortEnum + graphql_name 'PackageGroupSort' + description 'Values for sorting group packages' + + # The following enums are not available till we enable the new Arel node: + # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58657#note_552632305 + # value 'PROJECT_PATH_DESC', 'Project by descending order.', value: :project_path_desc + # value 'PROJECT_PATH_ASC', 'Project by ascending order.', value: :project_path_asc + end + end +end diff --git a/app/graphql/types/packages/package_sort_enum.rb b/app/graphql/types/packages/package_sort_enum.rb new file mode 100644 index 00000000000..ee14cf7a9e6 --- /dev/null +++ b/app/graphql/types/packages/package_sort_enum.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Types + module Packages + class PackageSortEnum < BaseEnum + graphql_name 'PackageSort' + description 'Values for sorting package' + + value 'CREATED_DESC', 'Ordered by created_at in descending order.', value: :created_desc + value 'CREATED_ASC', 'Ordered by created_at in ascending order.', value: :created_asc + value 'NAME_DESC', 'Ordered by name in descending order.', value: :name_desc + value 'NAME_ASC', 'Ordered by name in ascending order.', value: :name_asc + value 'VERSION_DESC', 'Ordered by version in descending order.', value: :version_desc + value 'VERSION_ASC', 'Ordered by version in ascending order.', value: :version_asc + value 'TYPE_DESC', 'Ordered by type in descending order.', value: :type_desc + value 'TYPE_ASC', 'Ordered by type in ascending order.', value: :type_asc + end + end +end diff --git a/app/graphql/types/packages/package_status_enum.rb b/app/graphql/types/packages/package_status_enum.rb new file mode 100644 index 00000000000..2e6ea5d0a50 --- /dev/null +++ b/app/graphql/types/packages/package_status_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module Packages + class PackageStatusEnum < BaseEnum + graphql_name 'PackageStatus' + + ::Packages::Package.statuses.keys.each do |status| + value status.to_s.upcase, description: "Packages with a #{status} status", value: status.to_s + end + end + end +end diff --git a/app/graphql/types/packages/package_type.rb b/app/graphql/types/packages/package_type.rb index a263ca1577a..b349b655fa5 100644 --- a/app/graphql/types/packages/package_type.rb +++ b/app/graphql/types/packages/package_type.rb @@ -25,6 +25,7 @@ module Types field :versions, ::Types::Packages::PackageType.connection_type, null: true, description: 'The other versions of the package.', deprecated: { reason: 'This field is now only returned in the PackageDetailsType', milestone: '13.11' } + field :status, Types::Packages::PackageStatusEnum, null: false, description: 'Package status.' def project Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find @@ -44,6 +45,10 @@ module Types object.composer_metadatum when 'conan' object.conan_metadatum + when 'maven' + object.maven_metadatum + when 'nuget' + object.nuget_metadatum else nil end diff --git a/app/graphql/types/packages/package_type_enum.rb b/app/graphql/types/packages/package_type_enum.rb index e2b5cf3163e..17145d8e000 100644 --- a/app/graphql/types/packages/package_type_enum.rb +++ b/app/graphql/types/packages/package_type_enum.rb @@ -5,12 +5,13 @@ module Types class PackageTypeEnum < BaseEnum PACKAGE_TYPE_NAMES = { pypi: 'PyPI', - npm: 'npm' + npm: 'npm', + terraform_module: 'Terraform Module' }.freeze ::Packages::Package.package_types.keys.each do |package_type| type_name = PACKAGE_TYPE_NAMES.fetch(package_type.to_sym, package_type.capitalize) - value package_type.to_s.upcase, "Packages from the #{type_name} package manager", value: package_type.to_s + value package_type.to_s.upcase, description: "Packages from the #{type_name} package manager", value: package_type.to_s end end end diff --git a/app/graphql/types/permission_types/ci/job.rb b/app/graphql/types/permission_types/ci/job.rb new file mode 100644 index 00000000000..c9a85317e67 --- /dev/null +++ b/app/graphql/types/permission_types/ci/job.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + module PermissionTypes + module Ci + class Job < BasePermissionType + graphql_name 'JobPermissions' + + abilities :read_job_artifacts, :read_build + ability_field :update_build, calls_gitaly: true + end + end + end +end diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb index 5747e63d195..f6a5563d367 100644 --- a/app/graphql/types/permission_types/project.rb +++ b/app/graphql/types/permission_types/project.rb @@ -27,3 +27,5 @@ module Types end end end + +::Types::PermissionTypes::Project.prepend_mod_with('Types::PermissionTypes::Project') diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 21534f40499..a2852588e89 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -30,8 +30,12 @@ module Types markdown_field :description_html, null: true field :tag_list, GraphQL::STRING_TYPE, null: true, + deprecated: { reason: 'Use `topics`', milestone: '13.12' }, description: 'List of project topics (not Git tags).' + field :topics, [GraphQL::STRING_TYPE], null: true, + description: 'List of project topics.' + field :ssh_url_to_repo, GraphQL::STRING_TYPE, null: true, description: 'URL to connect to the project via SSH.' field :http_url_to_repo, GraphQL::STRING_TYPE, null: true, @@ -180,14 +184,15 @@ module Types resolver: Resolvers::IssuesResolver.single field :packages, - description: 'Packages of the project.', - resolver: Resolvers::ProjectPackagesResolver + description: 'Packages of the project.', + resolver: Resolvers::ProjectPackagesResolver field :jobs, - Types::Ci::JobType.connection_type, - null: true, - description: 'Jobs of a project. This field can only be resolved for one project in any single request.', - resolver: Resolvers::ProjectJobsResolver + type: Types::Ci::JobType.connection_type, + null: true, + authorize: :read_commit_status, + description: 'Jobs of a project. This field can only be resolved for one project in any single request.', + resolver: Resolvers::ProjectJobsResolver field :pipelines, null: true, @@ -337,6 +342,10 @@ module Types description: 'Pipeline analytics.', resolver: Resolvers::ProjectPipelineStatisticsResolver + field :ci_template, Types::Ci::TemplateType, null: true, + description: 'Find a single CI/CD template by name.', + resolver: Resolvers::Ci::TemplateResolver + def label(title:) BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args| LabelsFinder @@ -386,4 +395,4 @@ module Types end end -Types::ProjectType.prepend_if_ee('::EE::Types::ProjectType') +Types::ProjectType.prepend_mod_with('Types::ProjectType') diff --git a/app/graphql/types/projects/service_type_enum.rb b/app/graphql/types/projects/service_type_enum.rb index fcb36fc233d..0a57cd48df4 100644 --- a/app/graphql/types/projects/service_type_enum.rb +++ b/app/graphql/types/projects/service_type_enum.rb @@ -5,7 +5,7 @@ module Types class ServiceTypeEnum < BaseEnum graphql_name 'ServiceType' - ::Service.available_services_types(include_dev: false).each do |service_type| + ::Integration.available_services_types(include_dev: false).each do |service_type| value service_type.underscore.upcase, value: service_type, description: "#{service_type} type" end end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 8af0db644dd..8b7b9f0107b 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -79,8 +79,14 @@ module Types field :issue, Types::IssueType, null: true, - description: 'Find an Issue.' do - argument :id, ::Types::GlobalIDType[::Issue], required: true, description: 'The global ID of the Issue.' + description: 'Find an issue.' do + argument :id, ::Types::GlobalIDType[::Issue], required: true, description: 'The global ID of the issue.' + end + + field :merge_request, Types::MergeRequestType, + null: true, + description: 'Find a merge request.' do + argument :id, ::Types::GlobalIDType[::MergeRequest], required: true, description: 'The global ID of the merge request.' end field :instance_statistics_measurements, @@ -106,6 +112,19 @@ module Types field :runner_platforms, resolver: Resolvers::Ci::RunnerPlatformsResolver field :runner_setup, resolver: Resolvers::Ci::RunnerSetupResolver + field :runner, Types::Ci::RunnerType, + null: true, + resolver: Resolvers::Ci::RunnerResolver, + extras: [:lookahead], + description: "Find a runner.", + feature_flag: :runner_graphql_query + + field :runners, Types::Ci::RunnerType.connection_type, + null: true, + resolver: Resolvers::Ci::RunnersResolver, + description: "Find runners visible to the current user.", + feature_flag: :runner_graphql_query + field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_COMPLEXITY / 2 + 1 def design_management @@ -119,6 +138,13 @@ module Types GitlabSchema.find_by_gid(id) end + def merge_request(id:) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[::MergeRequest].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) + end + def milestone(id:) # TODO: remove this line when the compatibility layer is removed # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 @@ -147,4 +173,4 @@ module Types end end -Types::QueryType.prepend_if_ee('EE::Types::QueryType') +Types::QueryType.prepend_mod_with('Types::QueryType') diff --git a/app/graphql/types/release_assets_type.rb b/app/graphql/types/release_assets_type.rb index 79c132358e0..d847d9842d5 100644 --- a/app/graphql/types/release_assets_type.rb +++ b/app/graphql/types/release_assets_type.rb @@ -13,7 +13,7 @@ module Types field :count, GraphQL::INT_TYPE, null: true, method: :assets_count, description: 'Number of assets of the release.' - field :links, Types::ReleaseAssetLinkType.connection_type, null: true, + field :links, Types::ReleaseAssetLinkType.connection_type, null: true, method: :sorted_links, description: 'Asset links of the release.' field :sources, Types::ReleaseSourceType.connection_type, null: true, description: 'Sources of the release.' diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 912fc5f643a..8ed97d7e663 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -32,6 +32,66 @@ module Types field :web_path, GraphQL::STRING_TYPE, null: true, description: 'Web path of the blob.' + field :ide_edit_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit this blob in the Web IDE.' + + field :fork_and_edit_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit this blob using a forked project.' + + field :ide_fork_and_edit_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit this blob in the Web IDE using a forked project.' + + field :size, GraphQL::INT_TYPE, null: true, + description: 'Size (in bytes) of the blob.' + + field :raw_size, GraphQL::INT_TYPE, null: true, + description: 'Size (in bytes) of the blob, or the blob target if stored externally.' + + field :raw_blob, GraphQL::STRING_TYPE, null: true, method: :data, + description: 'The raw content of the blob.' + + field :raw_text_blob, GraphQL::STRING_TYPE, null: true, method: :text_only_data, + description: 'The raw content of the blob, if the blob is text data.' + + field :stored_externally, GraphQL::BOOLEAN_TYPE, null: true, method: :stored_externally?, + description: "Whether the blob's content is stored externally (for instance, in LFS)." + + field :edit_blob_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit the blob in the old-style editor.' + + field :raw_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to download the raw blob.' + + field :external_storage_url, GraphQL::STRING_TYPE, null: true, + description: 'Web path to download the raw blob via external storage, if enabled.' + + field :replace_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to replace the blob content.' + + field :file_type, GraphQL::STRING_TYPE, null: true, + description: 'The expected format of the blob based on the extension.' + + field :simple_viewer, type: Types::BlobViewerType, + description: 'Blob content simple viewer.', + null: false + + field :rich_viewer, type: Types::BlobViewerType, + description: 'Blob content rich viewer.', + null: true + + field :plain_data, GraphQL::STRING_TYPE, + description: 'Blob plain highlighted data.', + null: true, + calls_gitaly: true + + field :can_modify_blob, GraphQL::BOOLEAN_TYPE, null: true, method: :can_modify_blob?, + calls_gitaly: true, + description: 'Whether the current user can modify the blob.' + + def raw_text_blob + object.data unless object.binary? + end + def lfs_oid Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(object.repository, object.id).find end diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb index 963a4296c4f..9d896888fa7 100644 --- a/app/graphql/types/repository_type.rb +++ b/app/graphql/types/repository_type.rb @@ -19,5 +19,9 @@ module Types field :branch_names, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true, complexity: 170, description: 'Names of branches available in this repository that match the search pattern.', resolver: Resolvers::RepositoryBranchNamesResolver + field :disk_path, GraphQL::STRING_TYPE, + description: 'Shows a disk path of the repository.', + null: true, + authorize: :read_storage_disk_path end end diff --git a/app/graphql/types/snippets/blob_viewer_type.rb b/app/graphql/types/snippets/blob_viewer_type.rb index 9e77457c843..8b73234bbd9 100644 --- a/app/graphql/types/snippets/blob_viewer_type.rb +++ b/app/graphql/types/snippets/blob_viewer_type.rb @@ -2,48 +2,10 @@ module Types module Snippets - class BlobViewerType < BaseObject # rubocop:disable Graphql/AuthorizeTypes + # Kept to avoid changing the type of existing fields. New fields should use + # ::Types::BlobViewerType directly + class BlobViewerType < ::Types::BlobViewerType # rubocop:disable Graphql/AuthorizeTypes graphql_name 'SnippetBlobViewer' - description 'Represents how the blob content should be displayed' - - field :type, Types::BlobViewers::TypeEnum, - description: 'Type of blob viewer.', - null: false - - field :load_async, GraphQL::BOOLEAN_TYPE, - description: 'Shows whether the blob content is loaded asynchronously.', - null: false - - field :collapsed, GraphQL::BOOLEAN_TYPE, - description: 'Shows whether the blob should be displayed collapsed.', - method: :collapsed?, - null: false - - field :too_large, GraphQL::BOOLEAN_TYPE, - description: 'Shows whether the blob too large to be displayed.', - method: :too_large?, - null: false - - field :render_error, GraphQL::STRING_TYPE, - description: 'Error rendering the blob content.', - null: true - - field :file_type, GraphQL::STRING_TYPE, - description: 'Content file type.', - method: :partial_name, - null: false - - 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/snippets/type_enum.rb b/app/graphql/types/snippets/type_enum.rb index 243f05359db..0ddd73d278d 100644 --- a/app/graphql/types/snippets/type_enum.rb +++ b/app/graphql/types/snippets/type_enum.rb @@ -3,8 +3,8 @@ module Types module Snippets class TypeEnum < BaseEnum - value 'personal', value: 'personal' - value 'project', value: 'project' + value 'personal', description: 'Snippet created independent of any project.', value: 'personal' + value 'project', description: 'Snippet related to a specific project.', value: 'project' end end end diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb new file mode 100644 index 00000000000..5356a998f0d --- /dev/null +++ b/app/graphql/types/subscription_type.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Types + class SubscriptionType < ::Types::BaseObject + graphql_name 'Subscription' + + field :issuable_assignees_updated, subscription: Subscriptions::IssuableUpdated, null: true, + description: 'Triggered when the assignees of an issuable are updated.' + end +end diff --git a/app/graphql/types/timelog_type.rb b/app/graphql/types/timelog_type.rb index 465e3c492bc..99a619f1b1d 100644 --- a/app/graphql/types/timelog_type.rb +++ b/app/graphql/types/timelog_type.rb @@ -26,6 +26,11 @@ module Types null: true, description: 'The issue that logged time was added to.' + field :merge_request, + Types::MergeRequestType, + null: true, + description: 'The merge request that logged time was added to.' + field :note, Types::Notes::NoteType, null: true, @@ -38,5 +43,9 @@ module Types def issue Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.issue_id).find end + + def spent_at + object.spent_at || object.created_at + end end end diff --git a/app/graphql/types/todo_target_enum.rb b/app/graphql/types/todo_target_enum.rb index ebf65e99936..ce61bc8a926 100644 --- a/app/graphql/types/todo_target_enum.rb +++ b/app/graphql/types/todo_target_enum.rb @@ -10,4 +10,4 @@ module Types end end -Types::TodoTargetEnum.prepend_if_ee('::EE::Types::TodoTargetEnum') +Types::TodoTargetEnum.prepend_mod_with('Types::TodoTargetEnum') diff --git a/app/graphql/types/tree/type_enum.rb b/app/graphql/types/tree/type_enum.rb index 6560d91e9e5..7acb83a2a8a 100644 --- a/app/graphql/types/tree/type_enum.rb +++ b/app/graphql/types/tree/type_enum.rb @@ -6,9 +6,9 @@ module Types graphql_name 'EntryType' description 'Type of a tree entry' - value 'tree', value: :tree - value 'blob', value: :blob - value 'commit', value: :commit + value 'tree', description: 'Directory tree type.', value: :tree + value 'blob', description: 'File tree type.', value: :blob + value 'commit', description: 'Commit tree type.', value: :commit end end end diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb new file mode 100644 index 00000000000..e5abc033155 --- /dev/null +++ b/app/graphql/types/user_interface.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +module Types + module UserInterface + include Types::BaseInterface + + graphql_name 'User' + description 'Representation of a GitLab user.' + + field :user_permissions, + type: Types::PermissionTypes::User, + description: 'Permissions for the current user on the resource.', + null: false, + method: :itself + + field :id, + type: GraphQL::ID_TYPE, + null: false, + description: 'ID of the user.' + field :bot, + type: GraphQL::BOOLEAN_TYPE, + null: false, + description: 'Indicates if the user is a bot.', + method: :bot? + field :username, + type: GraphQL::STRING_TYPE, + null: false, + description: 'Username of the user. Unique within this instance of GitLab.' + field :name, + type: GraphQL::STRING_TYPE, + null: false, + description: 'Human-readable name of the user.' + field :state, + type: Types::UserStateEnum, + null: false, + description: 'State of the user.' + field :email, + type: GraphQL::STRING_TYPE, + null: true, + description: 'User email.', method: :public_email, + deprecated: { reason: :renamed, replacement: 'User.publicEmail', milestone: '13.7' } + field :public_email, + type: GraphQL::STRING_TYPE, + null: true, + description: "User's public email." + field :avatar_url, + type: GraphQL::STRING_TYPE, + null: true, + description: "URL of the user's avatar." + field :web_url, + type: GraphQL::STRING_TYPE, + null: false, + description: 'Web URL of the user.' + field :web_path, + type: GraphQL::STRING_TYPE, + null: false, + description: 'Web path of the user.' + field :todos, + resolver: Resolvers::TodoResolver, + description: 'To-do items of the user.' + field :group_memberships, + type: Types::GroupMemberType.connection_type, + null: true, + description: 'Group memberships of the user.' + field :group_count, + resolver: Resolvers::Users::GroupCountResolver, + description: 'Group count for the user.' + field :status, + type: Types::UserStatusType, + null: true, + description: 'User status.' + field :location, + type: ::GraphQL::STRING_TYPE, + null: true, + description: 'The location of the user.' + field :project_memberships, + type: Types::ProjectMemberType.connection_type, + null: true, + description: 'Project memberships of the user.' + field :starred_projects, + description: 'Projects starred by the user.', + resolver: Resolvers::UserStarredProjectsResolver + + # 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, + description: 'Snippets authored by the user.', + resolver: Resolvers::Users::SnippetsResolver + field :callouts, + Types::UserCalloutType.connection_type, + null: true, + description: 'User callouts that belong to the user.' + + definition_methods do + def resolve_type(object, context) + # in the absense of other information, we cannot tell - just default to + # the core user type. + ::Types::UserType + end + end + end +end diff --git a/app/graphql/types/user_merge_request_interaction_type.rb b/app/graphql/types/user_merge_request_interaction_type.rb index 5ff0d79f13e..b9ff489e0d6 100644 --- a/app/graphql/types/user_merge_request_interaction_type.rb +++ b/app/graphql/types/user_merge_request_interaction_type.rb @@ -44,4 +44,4 @@ module Types end end -::Types::UserMergeRequestInteractionType.prepend_if_ee('EE::Types::UserMergeRequestInteractionType') +::Types::UserMergeRequestInteractionType.prepend_mod_with('Types::UserMergeRequestInteractionType') diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 3d7db80ae11..a6f5b7e7456 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -1,102 +1,13 @@ # frozen_string_literal: true module Types - class UserType < BaseObject - graphql_name 'User' - description 'Representation of a GitLab user.' + class UserType < ::Types::BaseObject + graphql_name 'UserCore' + description 'Core represention of a GitLab user.' + implements ::Types::UserInterface authorize :read_user present_using UserPresenter - - expose_permissions Types::PermissionTypes::User - - field :id, - type: GraphQL::ID_TYPE, - null: false, - description: 'ID of the user.' - field :bot, - type: GraphQL::BOOLEAN_TYPE, - null: false, - description: 'Indicates if the user is a bot.', - method: :bot? - field :username, - type: GraphQL::STRING_TYPE, - null: false, - description: 'Username of the user. Unique within this instance of GitLab.' - field :name, - type: GraphQL::STRING_TYPE, - null: false, - description: 'Human-readable name of the user.' - field :state, - type: Types::UserStateEnum, - null: false, - description: 'State of the user.' - field :email, - type: GraphQL::STRING_TYPE, - null: true, - description: 'User email.', method: :public_email, - deprecated: { reason: :renamed, replacement: 'User.publicEmail', milestone: '13.7' } - field :public_email, - type: GraphQL::STRING_TYPE, - null: true, - description: "User's public email." - field :avatar_url, - type: GraphQL::STRING_TYPE, - null: true, - description: "URL of the user's avatar." - field :web_url, - type: GraphQL::STRING_TYPE, - null: false, - description: 'Web URL of the user.' - field :web_path, - type: GraphQL::STRING_TYPE, - null: false, - description: 'Web path of the user.' - field :todos, - resolver: Resolvers::TodoResolver, - description: 'To-do items of the user.' - field :group_memberships, - type: Types::GroupMemberType.connection_type, - null: true, - description: 'Group memberships of the user.' - field :group_count, - resolver: Resolvers::Users::GroupCountResolver, - description: 'Group count for the user.', - feature_flag: :user_group_counts - field :status, - type: Types::UserStatusType, - null: true, - description: 'User status.' - field :location, - type: ::GraphQL::STRING_TYPE, - null: true, - description: 'The location of the user.' - field :project_memberships, - type: Types::ProjectMemberType.connection_type, - null: true, - description: 'Project memberships of the user.' - field :starred_projects, - description: 'Projects starred by the user.', - resolver: Resolvers::UserStarredProjectsResolver - - # 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, - description: 'Snippets authored by the user.', - resolver: Resolvers::Users::SnippetsResolver - field :callouts, - Types::UserCalloutType.connection_type, - null: true, - description: 'User callouts that belong to the user.' end end |