diff options
Diffstat (limited to 'app/graphql')
66 files changed, 486 insertions, 240 deletions
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb index 671c7c2cd25..290cd4d7146 100644 --- a/app/graphql/graphql_triggers.rb +++ b/app/graphql/graphql_triggers.rb @@ -4,4 +4,8 @@ module GraphqlTriggers def self.issuable_assignees_updated(issuable) GitlabSchema.subscriptions.trigger('issuableAssigneesUpdated', { issuable_id: issuable.to_gid }, issuable) end + + def self.issue_crm_contacts_updated(issue) + GitlabSchema.subscriptions.trigger('issueCrmContactsUpdated', { issuable_id: issue.to_gid }, issue) + end end diff --git a/app/graphql/mutations/issues/set_crm_contacts.rb b/app/graphql/mutations/issues/set_crm_contacts.rb index 7a9e6237eaa..4e49a45d52a 100644 --- a/app/graphql/mutations/issues/set_crm_contacts.rb +++ b/app/graphql/mutations/issues/set_crm_contacts.rb @@ -5,7 +5,7 @@ module Mutations class SetCrmContacts < Base graphql_name 'IssueSetCrmContacts' - argument :crm_contact_ids, + argument :contact_ids, [::Types::GlobalIDType[::CustomerRelations::Contact]], required: true, description: 'Customer relations contact IDs to set. Replaces existing contacts by default.' @@ -15,27 +15,27 @@ module Mutations required: false, description: 'Changes the operation mode. Defaults to REPLACE.' - def resolve(project_path:, iid:, crm_contact_ids:, operation_mode: Types::MutationOperationModeEnum.enum[:replace]) + def resolve(project_path:, iid:, contact_ids:, operation_mode: Types::MutationOperationModeEnum.enum[:replace]) issue = authorized_find!(project_path: project_path, iid: iid) project = issue.project raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless Feature.enabled?(:customer_relations, project.group, default_enabled: :yaml) - crm_contact_ids = crm_contact_ids.compact.map do |crm_contact_id| - raise Gitlab::Graphql::Errors::ArgumentError, "Contact #{crm_contact_id} is invalid." unless crm_contact_id.respond_to?(:model_id) + contact_ids = contact_ids.compact.map do |contact_id| + raise Gitlab::Graphql::Errors::ArgumentError, "Contact #{contact_id} is invalid." unless contact_id.respond_to?(:model_id) - crm_contact_id.model_id.to_i + contact_id.model_id.to_i end attribute_name = case operation_mode when Types::MutationOperationModeEnum.enum[:append] - :add_crm_contact_ids + :add_ids when Types::MutationOperationModeEnum.enum[:remove] - :remove_crm_contact_ids + :remove_ids else - :crm_contact_ids + :replace_ids end - response = ::Issues::SetCrmContactsService.new(project: project, current_user: current_user, params: { attribute_name => crm_contact_ids }) + response = ::Issues::SetCrmContactsService.new(project: project, current_user: current_user, params: { attribute_name => contact_ids }) .execute(issue) { diff --git a/app/graphql/mutations/jira_import/start.rb b/app/graphql/mutations/jira_import/start.rb index 143a9558e38..4929d6f394a 100644 --- a/app/graphql/mutations/jira_import/start.rb +++ b/app/graphql/mutations/jira_import/start.rb @@ -14,15 +14,15 @@ module Mutations null: true, description: 'Jira import data after mutation.' - argument :project_path, GraphQL::Types::ID, - required: true, - description: 'Project to import the Jira project into.' argument :jira_project_key, GraphQL::Types::String, required: true, description: 'Project key of the importer Jira project.' argument :jira_project_name, GraphQL::Types::String, required: false, description: 'Project name of the importer Jira project.' + argument :project_path, GraphQL::Types::ID, + required: true, + description: 'Project to import the Jira project into.' argument :users_mapping, [Types::JiraUsersMappingInputType], required: false, diff --git a/app/graphql/mutations/merge_requests/accept.rb b/app/graphql/mutations/merge_requests/accept.rb index d16b2327f2d..7ce850901af 100644 --- a/app/graphql/mutations/merge_requests/accept.rb +++ b/app/graphql/mutations/merge_requests/accept.rb @@ -26,12 +26,12 @@ module Mutations argument :commit_message, ::GraphQL::Types::String, required: false, description: 'Custom merge commit message.' - argument :squash_commit_message, ::GraphQL::Types::String, - required: false, - description: 'Custom squash commit message (if squash is true).' argument :sha, ::GraphQL::Types::String, required: true, description: 'HEAD SHA at the time when this merge was requested.' + argument :squash_commit_message, ::GraphQL::Types::String, + required: false, + description: 'Custom squash commit message (if squash is true).' argument :should_remove_source_branch, ::GraphQL::Types::Boolean, required: false, diff --git a/app/graphql/mutations/user_callouts/create.rb b/app/graphql/mutations/user_callouts/create.rb index ff6e5cd28dd..1be99ea0ecd 100644 --- a/app/graphql/mutations/user_callouts/create.rb +++ b/app/graphql/mutations/user_callouts/create.rb @@ -15,7 +15,7 @@ module Mutations description: 'User callout dismissed.' def resolve(feature_name:) - callout = Users::DismissUserCalloutService.new( + callout = Users::DismissCalloutService.new( container: nil, current_user: current_user, params: { feature_name: feature_name } ).execute errors = errors_on_object(callout) diff --git a/app/graphql/queries/container_registry/get_container_repositories.query.graphql b/app/graphql/queries/container_registry/get_container_repositories.query.graphql index df0b590acac..40e2934a038 100644 --- a/app/graphql/queries/container_registry/get_container_repositories.query.graphql +++ b/app/graphql/queries/container_registry/get_container_repositories.query.graphql @@ -10,6 +10,7 @@ query getProjectContainerRepositories( ) { project(fullPath: $fullPath) @skip(if: $isGroupPage) { __typename + id containerRepositoriesCount containerRepositories( name: $name @@ -43,6 +44,7 @@ query getProjectContainerRepositories( } group(fullPath: $fullPath) @include(if: $isGroupPage) { __typename + id containerRepositoriesCount containerRepositories( name: $name diff --git a/app/graphql/queries/design_management/design_permissions.query.graphql b/app/graphql/queries/design_management/design_permissions.query.graphql index 55dfa35129c..a81afd47625 100644 --- a/app/graphql/queries/design_management/design_permissions.query.graphql +++ b/app/graphql/queries/design_management/design_permissions.query.graphql @@ -4,6 +4,7 @@ query permissions($fullPath: ID!, $iid: String!) { id issue(iid: $iid) { __typename + id userPermissions { __typename createDesign diff --git a/app/graphql/queries/design_management/get_design_list.query.graphql b/app/graphql/queries/design_management/get_design_list.query.graphql index 01503a9572f..f0caa7c5b4c 100644 --- a/app/graphql/queries/design_management/get_design_list.query.graphql +++ b/app/graphql/queries/design_management/get_design_list.query.graphql @@ -4,6 +4,7 @@ query getDesignList($fullPath: ID!, $iid: String!, $atVersion: ID) { id issue(iid: $iid) { __typename + id designCollection { __typename copyState diff --git a/app/graphql/queries/epic/epic_children.query.graphql b/app/graphql/queries/epic/epic_children.query.graphql deleted file mode 100644 index be82813dddb..00000000000 --- a/app/graphql/queries/epic/epic_children.query.graphql +++ /dev/null @@ -1,132 +0,0 @@ -fragment PageInfo on PageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor -} - -fragment RelatedTreeBaseEpic on Epic { - id - iid - title - webPath - relativePosition - userPermissions { - __typename - adminEpic - createEpic - } - descendantWeightSum { - closedIssues - openedIssues - } - descendantCounts { - __typename - openedEpics - closedEpics - openedIssues - closedIssues - } - healthStatus { - __typename - issuesAtRisk - issuesOnTrack - issuesNeedingAttention - } -} - -fragment EpicNode on Epic { - ...RelatedTreeBaseEpic - state - reference(full: true) - relationPath - createdAt - closedAt - confidential - hasChildren - hasIssues - group { - __typename - fullPath - } -} - -query childItems( - $fullPath: ID! - $iid: ID - $pageSize: Int = 100 - $epicEndCursor: String = "" - $issueEndCursor: String = "" -) { - group(fullPath: $fullPath) { - __typename - id - path - fullPath - epic(iid: $iid) { - __typename - ...RelatedTreeBaseEpic - children(first: $pageSize, after: $epicEndCursor) { - __typename - edges { - __typename - node { - __typename - ...EpicNode - } - } - pageInfo { - __typename - ...PageInfo - } - } - issues(first: $pageSize, after: $issueEndCursor) { - __typename - edges { - __typename - node { - __typename - iid - epicIssueId - title - blocked - closedAt - state - createdAt - confidential - dueDate - weight - webPath - reference(full: true) - relationPath - relativePosition - assignees { - __typename - edges { - __typename - node { - __typename - webUrl - name - username - avatarUrl - } - } - } - milestone { - __typename - title - startDate - dueDate - } - healthStatus - } - } - pageInfo { - __typename - ...PageInfo - } - } - } - } -} diff --git a/app/graphql/queries/epic/epic_details.query.graphql b/app/graphql/queries/epic/epic_details.query.graphql index 406d630b180..eb4757a845a 100644 --- a/app/graphql/queries/epic/epic_details.query.graphql +++ b/app/graphql/queries/epic/epic_details.query.graphql @@ -1,14 +1,17 @@ query epicDetails($fullPath: ID!, $iid: ID!) { group(fullPath: $fullPath) { __typename + id epic(iid: $iid) { __typename + id participants { __typename edges { __typename node { __typename + id name avatarUrl webUrl diff --git a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql index 4e4caa1e27c..dd5c9e07488 100644 --- a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql +++ b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql @@ -5,16 +5,19 @@ fragment LinkedPipelineData on Pipeline { path status: detailedStatus { __typename + id group label icon } sourceJob { __typename + id name } project { __typename + id name fullPath } @@ -23,6 +26,7 @@ fragment LinkedPipelineData on Pipeline { query getPipelineDetails($projectPath: ID!, $iid: ID!) { project(fullPath: $projectPath) { __typename + id pipeline(iid: $iid) { __typename id @@ -45,11 +49,14 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) { __typename nodes { __typename + id name status: detailedStatus { __typename + id action { __typename + id icon path title @@ -59,8 +66,10 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) { __typename nodes { __typename + id status: detailedStatus { __typename + id label group icon @@ -71,17 +80,20 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) { __typename nodes { __typename + id name scheduledAt needs { __typename nodes { __typename + id name } } status: detailedStatus { __typename + id icon tooltip hasDetails @@ -89,6 +101,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) { group action { __typename + id buttonTitle icon path diff --git a/app/graphql/queries/releases/all_releases.query.graphql b/app/graphql/queries/releases/all_releases.query.graphql index ab8cbcb8aa3..150f59832f3 100644 --- a/app/graphql/queries/releases/all_releases.query.graphql +++ b/app/graphql/queries/releases/all_releases.query.graphql @@ -11,6 +11,7 @@ query allReleases( ) { project(fullPath: $fullPath) { __typename + id releases(first: $first, last: $last, before: $before, after: $after, sort: $sort) { __typename nodes { @@ -50,6 +51,7 @@ query allReleases( __typename nodes { __typename + id filepath collectedAt sha @@ -67,12 +69,14 @@ query allReleases( } commit { __typename + id sha webUrl title } author { __typename + id webUrl avatarUrl username diff --git a/app/graphql/queries/repository/path_last_commit.query.graphql b/app/graphql/queries/repository/path_last_commit.query.graphql index b5c5f653429..bcb07ae3182 100644 --- a/app/graphql/queries/repository/path_last_commit.query.graphql +++ b/app/graphql/queries/repository/path_last_commit.query.graphql @@ -1,13 +1,14 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { project(fullPath: $projectPath) { - id __typename + id repository { __typename tree(path: $path, ref: $ref) { __typename lastCommit { __typename + id sha title titleHtml @@ -19,6 +20,7 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { authorGravatar author { __typename + id name avatarUrl webPath @@ -30,8 +32,10 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { __typename node { __typename + id detailedStatus { __typename + id detailsPath icon tooltip diff --git a/app/graphql/queries/snippet/project_permissions.query.graphql b/app/graphql/queries/snippet/project_permissions.query.graphql index 0c38e4f8a07..e0de79f4449 100644 --- a/app/graphql/queries/snippet/project_permissions.query.graphql +++ b/app/graphql/queries/snippet/project_permissions.query.graphql @@ -1,6 +1,7 @@ query CanCreateProjectSnippet($fullPath: ID!) { project(fullPath: $fullPath) { __typename + id userPermissions { __typename createSnippet diff --git a/app/graphql/queries/snippet/snippet.query.graphql b/app/graphql/queries/snippet/snippet.query.graphql index ebfc135c51c..24b268ec853 100644 --- a/app/graphql/queries/snippet/snippet.query.graphql +++ b/app/graphql/queries/snippet/snippet.query.graphql @@ -49,6 +49,7 @@ query GetSnippetQuery($ids: [SnippetID!]) { } project { __typename + id fullPath webUrl } diff --git a/app/graphql/queries/snippet/user_permissions.query.graphql b/app/graphql/queries/snippet/user_permissions.query.graphql index a4914189807..4d131c48feb 100644 --- a/app/graphql/queries/snippet/user_permissions.query.graphql +++ b/app/graphql/queries/snippet/user_permissions.query.graphql @@ -1,6 +1,7 @@ query CanCreatePersonalSnippet { currentUser { __typename + id userPermissions { __typename createSnippet diff --git a/app/graphql/resolvers/base_issues_resolver.rb b/app/graphql/resolvers/base_issues_resolver.rb index 54ebb697cb2..dca93444907 100644 --- a/app/graphql/resolvers/base_issues_resolver.rb +++ b/app/graphql/resolvers/base_issues_resolver.rb @@ -4,13 +4,13 @@ module Resolvers class BaseIssuesResolver < BaseResolver prepend IssueResolverArguments - argument :state, Types::IssuableStateEnum, - required: false, - description: 'Current state of this issue.' argument :sort, Types::IssueSortEnum, description: 'Sort issues by this criteria.', required: false, default_value: :created_desc + argument :state, Types::IssuableStateEnum, + required: false, + description: 'Current state of this issue.' type Types::IssueType.connection_type, null: true diff --git a/app/graphql/resolvers/ci/jobs_resolver.rb b/app/graphql/resolvers/ci/jobs_resolver.rb index 5ae9e721cc8..df138a15538 100644 --- a/app/graphql/resolvers/ci/jobs_resolver.rb +++ b/app/graphql/resolvers/ci/jobs_resolver.rb @@ -29,7 +29,7 @@ module Resolvers job_types: security_report_types ).execute else - pipeline.statuses + pipeline.statuses_order_id_desc end end end diff --git a/app/graphql/resolvers/ci/runner_status_resolver.rb b/app/graphql/resolvers/ci/runner_status_resolver.rb new file mode 100644 index 00000000000..d916a8a13f0 --- /dev/null +++ b/app/graphql/resolvers/ci/runner_status_resolver.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + # NOTE: This class was introduced to allow modifying the meaning of certain values in RunnerStatusEnum + # while preserving backward compatibility. It can be removed in 15.0 once the API has stabilized. + class RunnerStatusResolver < BaseResolver + type Types::Ci::RunnerStatusEnum, null: false + + alias_method :runner, :object + + argument :legacy_mode, + type: GraphQL::Types::String, + default_value: '14.5', + required: false, + description: 'Compatibility mode. A null value turns off compatibility mode.', + deprecated: { reason: 'Will be removed in 15.0. From that release onward, the field will behave as if legacyMode is null', milestone: '14.6' } + + def resolve(legacy_mode:, **args) + runner.status(legacy_mode) + end + end + end +end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index 07105701daa..9848b5a503f 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -7,6 +7,10 @@ module Resolvers type Types::Ci::RunnerType.connection_type, null: true + argument :active, ::GraphQL::Types::Boolean, + required: false, + description: 'Filter runners by active (true) or paused (false) status.' + argument :status, ::Types::Ci::RunnerStatusEnum, required: false, description: 'Filter runners by status.' @@ -38,6 +42,7 @@ module Resolvers def runners_finder_params(params) { + active: params[:active], status_status: params[:status]&.to_s, type_type: params[:type], tag_name: params[:tag_list], diff --git a/app/graphql/resolvers/clusters/agent_activity_events_resolver.rb b/app/graphql/resolvers/clusters/agent_activity_events_resolver.rb new file mode 100644 index 00000000000..b6fec3d3772 --- /dev/null +++ b/app/graphql/resolvers/clusters/agent_activity_events_resolver.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Resolvers + module Clusters + class AgentActivityEventsResolver < BaseResolver + type Types::Clusters::AgentActivityEventType, null: true + + alias_method :agent, :object + + delegate :project, to: :agent + + def resolve(**args) + return ::Clusters::Agents::ActivityEvent.none unless can_view_activity_events? + + agent.activity_events + end + + private + + def can_view_activity_events? + current_user.can?(:admin_cluster, project) + end + end + end +end diff --git a/app/graphql/resolvers/clusters/agents_resolver.rb b/app/graphql/resolvers/clusters/agents_resolver.rb index 9b8cea52e3b..5ad66ed7cdd 100644 --- a/app/graphql/resolvers/clusters/agents_resolver.rb +++ b/app/graphql/resolvers/clusters/agents_resolver.rb @@ -28,7 +28,10 @@ module Resolvers private def preloads - { tokens: :last_used_agent_tokens } + { + activity_events: { activity_events: [:user, agent_token: :agent] }, + tokens: :last_used_agent_tokens + } end end end diff --git a/app/graphql/resolvers/container_repository_tags_resolver.rb b/app/graphql/resolvers/container_repository_tags_resolver.rb new file mode 100644 index 00000000000..55a83dd49da --- /dev/null +++ b/app/graphql/resolvers/container_repository_tags_resolver.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Resolvers + class ContainerRepositoryTagsResolver < BaseResolver + type Types::ContainerRepositoryTagType.connection_type, null: true + + argument :sort, Types::ContainerRepositoryTagsSortEnum, + description: 'Sort tags by these criteria.', + required: false, + default_value: nil + + argument :name, GraphQL::Types::String, + description: 'Search by tag name.', + required: false, + default_value: nil + + def resolve(sort:, **filters) + result = tags + + if filters[:name] + result = tags.filter do |tag| + tag.name.include?(filters[:name]) + end + end + + result = sort_tags(result, sort) if sort + result + end + + private + + def sort_tags(to_be_sorted, sort) + raise StandardError unless Types::ContainerRepositoryTagsSortEnum.enum.include?(sort) + + sort_value, _, direction = sort.to_s.rpartition('_') + + sorted = to_be_sorted.sort_by(&sort_value.to_sym) + return sorted.reverse if direction == 'desc' + + sorted + end + + def tags + object.tags + rescue Faraday::Error + raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, "Can't connect to the Container Registry. If this error persists, please review the troubleshooting documentation." + end + end +end diff --git a/app/graphql/resolvers/design_management/designs_resolver.rb b/app/graphql/resolvers/design_management/designs_resolver.rb index dec778fac80..a62ef6d76e5 100644 --- a/app/graphql/resolvers/design_management/designs_resolver.rb +++ b/app/graphql/resolvers/design_management/designs_resolver.rb @@ -8,16 +8,16 @@ module Resolvers type ::Types::DesignManagement::DesignType.connection_type, null: true - argument :ids, [DesignID], + argument :at_version, VersionID, required: false, - description: 'Filters designs by their ID.' + description: 'Filters designs to only those that existed at the version. ' \ + 'If argument is omitted or nil then all designs will reflect the latest version' argument :filenames, [GraphQL::Types::String], required: false, description: 'Filters designs by their filename.' - argument :at_version, VersionID, + argument :ids, [DesignID], required: false, - description: 'Filters designs to only those that existed at the version. ' \ - 'If argument is omitted or nil then all designs will reflect the latest version' + description: 'Filters designs by their ID.' def self.single ::Resolvers::DesignManagement::DesignResolver diff --git a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb index d879c1434dc..76e365c40b1 100644 --- a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb +++ b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb @@ -16,16 +16,16 @@ module Resolvers authorize :read_design - argument :id, DesignAtVersionID, - required: false, - as: :design_at_version_id, - description: 'ID of the DesignAtVersion.' argument :design_id, DesignID, required: false, description: 'ID of a specific design.' argument :filename, GraphQL::Types::String, required: false, description: 'Filename of a specific design.' + argument :id, DesignAtVersionID, + required: false, + as: :design_at_version_id, + description: 'ID of the DesignAtVersion.' def self.single self diff --git a/app/graphql/resolvers/kas/agent_configurations_resolver.rb b/app/graphql/resolvers/kas/agent_configurations_resolver.rb index 238dae0bf12..a1b1d3bfe4c 100644 --- a/app/graphql/resolvers/kas/agent_configurations_resolver.rb +++ b/app/graphql/resolvers/kas/agent_configurations_resolver.rb @@ -14,7 +14,7 @@ module Resolvers return [] unless can_read_agent_configuration? kas_client.list_agent_config_files(project: project) - rescue GRPC::BadStatus => e + rescue GRPC::BadStatus, Gitlab::Kas::Client::ConfigurationError => e raise Gitlab::Graphql::Errors::ResourceNotAvailable, e.class.name end diff --git a/app/graphql/resolvers/package_pipelines_resolver.rb b/app/graphql/resolvers/package_pipelines_resolver.rb new file mode 100644 index 00000000000..59a1cd173a4 --- /dev/null +++ b/app/graphql/resolvers/package_pipelines_resolver.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Resolvers + class PackagePipelinesResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::Ci::PipelineType.connection_type, null: true + extension Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension + + authorizes_object! + authorize :read_pipeline + + alias_method :package, :object + + def resolve(first: nil, last: nil, after: nil, before: nil, lookahead:) + finder = ::Packages::BuildInfosFinder.new( + package, + first: first, + last: last, + after: decode_cursor(after), + before: decode_cursor(before), + max_page_size: context.schema.default_max_page_size, + support_next_page: lookahead.selects?(:page_info) + ) + + build_infos = finder.execute + + # this .pluck_pipeline_ids can load max 101 pipelines ids + ::Ci::Pipeline.id_in(build_infos.pluck_pipeline_ids) + end + + # we manage the pagination manually, so opt out of the connection field extension + def self.field_options + super.merge( + connection: false, + extras: [:lookahead] + ) + end + + private + + def decode_cursor(encoded) + return unless encoded + + decoded = Gitlab::Json.parse(context.schema.cursor_encoder.decode(encoded, nonce: true)) + id_from_cursor(decoded) + rescue JSON::ParserError + raise Gitlab::Graphql::Errors::ArgumentError, "Please provide a valid cursor" + end + + def id_from_cursor(cursor) + cursor&.fetch('id') + rescue KeyError + raise Gitlab::Graphql::Errors::ArgumentError, "Please provide a valid cursor" + end + end +end diff --git a/app/graphql/resolvers/project_jobs_resolver.rb b/app/graphql/resolvers/project_jobs_resolver.rb index 75068014242..8a2693ee46b 100644 --- a/app/graphql/resolvers/project_jobs_resolver.rb +++ b/app/graphql/resolvers/project_jobs_resolver.rb @@ -33,6 +33,7 @@ module Resolvers def preloads { + previous_stage_jobs_and_needs: [:needs, :pipeline], artifacts: [:job_artifacts], pipeline: [:user] } diff --git a/app/graphql/resolvers/project_pipeline_resolver.rb b/app/graphql/resolvers/project_pipeline_resolver.rb index 5acd7f95606..ea733ab08ad 100644 --- a/app/graphql/resolvers/project_pipeline_resolver.rb +++ b/app/graphql/resolvers/project_pipeline_resolver.rb @@ -24,7 +24,6 @@ module Resolvers super end - # the preloads are defined on ee/app/graphql/ee/resolvers/project_pipeline_resolver.rb def resolve(iid: nil, sha: nil, **args) self.lookahead = args.delete(:lookahead) @@ -42,5 +41,11 @@ module Resolvers end end end + + def unconditional_includes + [ + { statuses: [:needs] } + ] + end end end diff --git a/app/graphql/resolvers/project_pipelines_resolver.rb b/app/graphql/resolvers/project_pipelines_resolver.rb index 5a1e92efc96..47a8b028d4d 100644 --- a/app/graphql/resolvers/project_pipelines_resolver.rb +++ b/app/graphql/resolvers/project_pipelines_resolver.rb @@ -18,7 +18,7 @@ module Resolvers def preloads { - jobs: [:statuses], + jobs: { statuses_order_id_desc: [:needs] }, upstream: [:triggered_by_pipeline], downstream: [:triggered_pipelines] } diff --git a/app/graphql/resolvers/snippets/blobs_resolver.rb b/app/graphql/resolvers/snippets/blobs_resolver.rb index 00f41517422..cbbc65d7263 100644 --- a/app/graphql/resolvers/snippets/blobs_resolver.rb +++ b/app/graphql/resolvers/snippets/blobs_resolver.rb @@ -35,3 +35,5 @@ module Resolvers end end end + +Resolvers::Snippets::BlobsResolver.prepend_mod diff --git a/app/graphql/resolvers/users/participants_resolver.rb b/app/graphql/resolvers/users/participants_resolver.rb new file mode 100644 index 00000000000..9e87b60fa34 --- /dev/null +++ b/app/graphql/resolvers/users/participants_resolver.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Resolvers + module Users + class ParticipantsResolver < BaseResolver + type Types::UserType.connection_type, null: true + + def resolve(**args) + object.visible_participants(current_user) + end + end + end +end diff --git a/app/graphql/types/base_edge.rb b/app/graphql/types/base_edge.rb new file mode 100644 index 00000000000..f4409c983f8 --- /dev/null +++ b/app/graphql/types/base_edge.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Types + class BaseEdge < GraphQL::Types::Relay::BaseEdge + field_class Types::BaseField + end +end diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb index 93e17ea6dfc..75909592c6c 100644 --- a/app/graphql/types/base_field.rb +++ b/app/graphql/types/base_field.rb @@ -78,6 +78,8 @@ module Types attr_reader :feature_flag def field_authorized?(object, ctx) + object = object.node if object.is_a?(GraphQL::Pagination::Connection::Edge) + authorization.ok?(object, ctx[:current_user]) end diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb index cd677e50d28..b5797f07aa6 100644 --- a/app/graphql/types/base_object.rb +++ b/app/graphql/types/base_object.rb @@ -7,6 +7,7 @@ module Types prepend Gitlab::Graphql::MarkdownField field_class Types::BaseField + edge_type_class Types::BaseEdge def self.accepts(*types) @accepts ||= [] diff --git a/app/graphql/types/boards/board_issue_input_type.rb b/app/graphql/types/boards/board_issue_input_type.rb index b4dbe87e32d..afa66c1c510 100644 --- a/app/graphql/types/boards/board_issue_input_type.rb +++ b/app/graphql/types/boards/board_issue_input_type.rb @@ -17,6 +17,10 @@ module Types argument :assignee_wildcard_id, ::Types::Boards::AssigneeWildcardIdEnum, required: false, description: 'Filter by assignee wildcard. Incompatible with assigneeUsername.' + + argument :confidential, GraphQL::Types::Boolean, + required: false, + description: 'Filter by confidentiality.' end end end diff --git a/app/graphql/types/ci/build_need_type.rb b/app/graphql/types/ci/build_need_type.rb index 7bd12c99a08..b71d10c4c06 100644 --- a/app/graphql/types/ci/build_need_type.rb +++ b/app/graphql/types/ci/build_need_type.rb @@ -8,7 +8,7 @@ module Types graphql_name 'CiBuildNeed' field :id, GraphQL::Types::ID, null: false, - description: 'ID of the job we need to complete.' + description: 'ID of the BuildNeed.' field :name, GraphQL::Types::String, null: true, description: 'Name of the job we need to complete.' end diff --git a/app/graphql/types/ci/job_need_union.rb b/app/graphql/types/ci/job_need_union.rb new file mode 100644 index 00000000000..59608a6a312 --- /dev/null +++ b/app/graphql/types/ci/job_need_union.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + module Ci + class JobNeedUnion < GraphQL::Schema::Union + TypeNotSupportedError = Class.new(StandardError) + + possible_types Types::Ci::JobType, Types::Ci::BuildNeedType + + def self.resolve_type(object, context) + if object.is_a?(::Ci::BuildNeed) + Types::Ci::BuildNeedType + elsif object.is_a?(CommitStatus) + Types::Ci::JobType + else + raise TypeNotSupportedError + end + end + end + end +end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index 48bd91bfc5b..928ca2f597d 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -50,6 +50,8 @@ module Types null: true, description: 'How long the job was enqueued before starting.' + field :previous_stage_jobs_or_needs, Types::Ci::JobNeedUnion.connection_type, null: true, + description: 'Jobs that must complete before the job runs. Returns `BuildNeed`, which is the needed jobs if the job uses the `needs` keyword, or the previous stage jobs otherwise.' field :detailed_status, Types::Ci::DetailedStatusType, null: true, description: 'Detailed status of the job.' field :artifacts, Types::Ci::JobArtifactType.connection_type, null: true, @@ -74,7 +76,7 @@ module Types description: 'Indicates the job is active.' field :stuck, GraphQL::Types::Boolean, null: false, method: :stuck?, description: 'Indicates the job is stuck.' - field :coverage, GraphQL::FLOAT_TYPE, null: true, + field :coverage, GraphQL::Types::Float, null: true, description: 'Coverage level of the job.' field :created_by_tag, GraphQL::Types::Boolean, null: false, description: 'Whether the job was created by a tag.' @@ -101,6 +103,30 @@ module Types end end + def previous_stage_jobs_or_needs + if object.scheduling_type == 'stage' + Gitlab::Graphql::Lazy.with_value(previous_stage_jobs) do |jobs| + jobs + end + else + object.needs + end + end + + def previous_stage_jobs + BatchLoader::GraphQL.for([object.pipeline, object.stage_idx - 1]).batch(default_value: []) do |tuples, loader| + tuples.group_by(&:first).each do |pipeline, keys| + positions = keys.map(&:second) + + stages = pipeline.stages.by_position(positions) + + stages.each do |stage| + loader.call([pipeline, stage.position], stage.latest_statuses) + end + end + end + end + def stage ::Gitlab::Graphql::Lazy.with_value(pipeline) do |pl| BatchLoader::GraphQL.for([pl, object.stage]).batch do |ids, loader| diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index da2f11be9e2..c8ac31bce4d 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -45,7 +45,7 @@ module Types field :queued_duration, Types::DurationType, null: true, description: 'How long the pipeline was queued before starting.' - field :coverage, GraphQL::FLOAT_TYPE, null: true, + field :coverage, GraphQL::Types::Float, null: true, description: 'Coverage percentage.' field :created_at, Types::TimeType, null: false, @@ -66,7 +66,7 @@ module Types field :stages, type: Types::Ci::StageType.connection_type, null: true, - authorize: :read_commit_status, + authorize: :read_build, description: 'Stages of the pipeline.', extras: [:lookahead], resolver: Resolvers::Ci::PipelineStagesResolver @@ -89,14 +89,14 @@ module Types field :jobs, ::Types::Ci::JobType.connection_type, null: true, - authorize: :read_commit_status, + authorize: :read_build, description: 'Jobs belonging to the pipeline.', resolver: ::Resolvers::Ci::JobsResolver field :job, type: ::Types::Ci::JobType, null: true, - authorize: :read_commit_status, + authorize: :read_build, description: 'Specific job in this pipeline, either by name or ID.' do argument :id, type: ::Types::GlobalIDType[::CommitStatus], @@ -116,7 +116,7 @@ module Types field :source_job, type: Types::Ci::JobType, null: true, - authorize: :read_commit_status, + authorize: :read_build, description: 'Job where pipeline was triggered from.' field :downstream, Types::Ci::PipelineType.connection_type, null: true, diff --git a/app/graphql/types/ci/runner_status_enum.rb b/app/graphql/types/ci/runner_status_enum.rb index 8501ce20204..dd056191ceb 100644 --- a/app/graphql/types/ci/runner_status_enum.rb +++ b/app/graphql/types/ci/runner_status_enum.rb @@ -5,24 +5,37 @@ module Types class RunnerStatusEnum < BaseEnum graphql_name 'CiRunnerStatus' - ::Ci::Runner::AVAILABLE_STATUSES.each do |status| - description = case status - when 'active' - "A runner that is not paused." - when 'online' - "A runner that contacted this instance within the last #{::Ci::Runner::ONLINE_CONTACT_TIMEOUT.inspect}." - when 'offline' - "A runner that has not contacted this instance within the last #{::Ci::Runner::ONLINE_CONTACT_TIMEOUT.inspect}." - when 'not_connected' - "A runner that has never contacted this instance." - else - "A runner that is #{status.to_s.tr('_', ' ')}." - end - - value status.to_s.upcase, - description: description, - value: status.to_sym - end + value 'ACTIVE', + description: 'Runner that is not paused.', + deprecated: { reason: 'Use CiRunnerType.active instead', milestone: '14.6' }, + value: :active + + value 'PAUSED', + description: 'Runner that is paused.', + deprecated: { reason: 'Use CiRunnerType.active instead', milestone: '14.6' }, + value: :paused + + value 'ONLINE', + description: "Runner that contacted this instance within the last #{::Ci::Runner::ONLINE_CONTACT_TIMEOUT.inspect}.", + value: :online + + value 'OFFLINE', + description: "Runner that has not contacted this instance within the last #{::Ci::Runner::ONLINE_CONTACT_TIMEOUT.inspect}.", + deprecated: { reason: 'This field will have a slightly different scope starting in 15.0, with STALE being returned after a certain period offline', milestone: '14.6' }, + value: :offline + + value 'STALE', + description: "Runner that has not contacted this instance within the last #{::Ci::Runner::STALE_TIMEOUT.inspect}. Only available if legacyMode is null. Will be a possible return value starting in 15.0.", + value: :stale + + value 'NOT_CONNECTED', + description: 'Runner that has never contacted this instance.', + deprecated: { reason: "Use NEVER_CONTACTED instead. NEVER_CONTACTED will have a slightly different scope starting in 15.0, with STALE being returned instead after #{::Ci::Runner::STALE_TIMEOUT.inspect} of no contact", milestone: '14.6' }, + value: :not_connected + + value 'NEVER_CONTACTED', + description: 'Runner that has never contacted this instance. Set legacyMode to null to utilize this value. Will replace NOT_CONNECTED starting in 15.0.', + value: :never_contacted end end end diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index 9bf98aa7e86..d37cca0927f 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -27,8 +27,11 @@ module Types description: 'Access level of the runner.' field :active, GraphQL::Types::Boolean, null: false, description: 'Indicates the runner is allowed to receive jobs.' - field :status, ::Types::Ci::RunnerStatusEnum, null: false, - description: 'Status of the runner.' + field :status, + Types::Ci::RunnerStatusEnum, + null: false, + description: 'Status of the runner.', + resolver: ::Resolvers::Ci::RunnerStatusResolver field :version, GraphQL::Types::String, null: true, description: 'Version of the runner.' field :short_sha, GraphQL::Types::String, null: true, @@ -50,7 +53,7 @@ module Types field :job_count, GraphQL::Types::Int, null: true, description: "Number of jobs processed by the runner (limited to #{JOB_COUNT_LIMIT}, plus one to indicate that more items exist)." field :admin_url, GraphQL::Types::String, null: true, - description: 'Admin URL of the runner. Only available for adminstrators.' + description: 'Admin URL of the runner. Only available for administrators.' def job_count # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT diff --git a/app/graphql/types/ci/runner_web_url_edge.rb b/app/graphql/types/ci/runner_web_url_edge.rb index 3b9fdfd1571..368e16f972c 100644 --- a/app/graphql/types/ci/runner_web_url_edge.rb +++ b/app/graphql/types/ci/runner_web_url_edge.rb @@ -3,7 +3,7 @@ module Types module Ci # rubocop: disable Graphql/AuthorizeTypes - class RunnerWebUrlEdge < GraphQL::Types::Relay::BaseEdge + class RunnerWebUrlEdge < ::Types::BaseEdge include FindClosest field :web_url, GraphQL::Types::String, null: true, diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb index c0d931b3d31..70e78e391a7 100644 --- a/app/graphql/types/ci/stage_type.rb +++ b/app/graphql/types/ci/stage_type.rb @@ -4,7 +4,7 @@ module Types module Ci class StageType < BaseObject graphql_name 'CiStage' - authorize :read_commit_status + authorize :read_build field :id, GraphQL::Types::ID, null: false, description: 'ID of the stage.' @@ -31,7 +31,10 @@ module Types BatchLoader::GraphQL.for(key).batch(default_value: []) do |keys, loader| by_pipeline = keys.group_by(&:pipeline) - include_needs = keys.any? { |k| k.requires?(%i[nodes jobs nodes needs]) } + include_needs = keys.any? do |k| + k.requires?(%i[nodes jobs nodes needs]) || + k.requires?(%i[nodes jobs nodes previousStageJobsAndNeeds]) + end by_pipeline.each do |pl, key_group| project = pl.project diff --git a/app/graphql/types/ci/test_case_type.rb b/app/graphql/types/ci/test_case_type.rb index 9ec5daa44ea..6e5f55aa3ed 100644 --- a/app/graphql/types/ci/test_case_type.rb +++ b/app/graphql/types/ci/test_case_type.rb @@ -18,7 +18,7 @@ module Types field :classname, GraphQL::Types::String, null: true, description: 'Classname of the test case.' - field :execution_time, GraphQL::FLOAT_TYPE, null: true, + field :execution_time, GraphQL::Types::Float, null: true, description: 'Test case execution time in seconds.' field :file, GraphQL::Types::String, null: true, diff --git a/app/graphql/types/ci/test_report_total_type.rb b/app/graphql/types/ci/test_report_total_type.rb index aa07a391519..48aea1257c5 100644 --- a/app/graphql/types/ci/test_report_total_type.rb +++ b/app/graphql/types/ci/test_report_total_type.rb @@ -7,7 +7,7 @@ module Types graphql_name 'TestReportTotal' description 'Total test report statistics.' - field :time, GraphQL::FLOAT_TYPE, null: true, + field :time, GraphQL::Types::Float, null: true, description: 'Total duration of the tests.' field :count, GraphQL::Types::Int, null: true, diff --git a/app/graphql/types/ci/test_suite_summary_type.rb b/app/graphql/types/ci/test_suite_summary_type.rb index 3db2d80d591..ec7b852213b 100644 --- a/app/graphql/types/ci/test_suite_summary_type.rb +++ b/app/graphql/types/ci/test_suite_summary_type.rb @@ -12,7 +12,7 @@ module Types field :name, GraphQL::Types::String, null: true, description: 'Name of the test suite.' - field :total_time, GraphQL::FLOAT_TYPE, null: true, + field :total_time, GraphQL::Types::Float, null: true, description: 'Total duration of the tests in the test suite.' field :total_count, GraphQL::Types::Int, null: true, diff --git a/app/graphql/types/ci/test_suite_type.rb b/app/graphql/types/ci/test_suite_type.rb index f9f37d4045e..7ce479632cc 100644 --- a/app/graphql/types/ci/test_suite_type.rb +++ b/app/graphql/types/ci/test_suite_type.rb @@ -12,7 +12,7 @@ module Types field :name, GraphQL::Types::String, null: true, description: 'Name of the test suite.' - field :total_time, GraphQL::FLOAT_TYPE, null: true, + field :total_time, GraphQL::Types::Float, null: true, description: 'Total duration of the tests in the test suite.' field :total_count, GraphQL::Types::Int, null: true, diff --git a/app/graphql/types/clusters/agent_activity_event_type.rb b/app/graphql/types/clusters/agent_activity_event_type.rb new file mode 100644 index 00000000000..79a9fd70505 --- /dev/null +++ b/app/graphql/types/clusters/agent_activity_event_type.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Types + module Clusters + class AgentActivityEventType < BaseObject + graphql_name 'ClusterAgentActivityEvent' + + authorize :admin_cluster + + connection_type_class(Types::CountableConnectionType) + + field :recorded_at, + Types::TimeType, + null: true, + description: 'Timestamp the event was recorded.' + + field :kind, + GraphQL::Types::String, + null: true, + description: 'Type of event.' + + field :level, + GraphQL::Types::String, + null: true, + description: 'Severity of the event.' + + field :user, + Types::UserType, + null: true, + description: 'User associated with the event.' + + field :agent_token, + Types::Clusters::AgentTokenType, + null: true, + description: 'Agent token associated with the event.' + end + end +end diff --git a/app/graphql/types/clusters/agent_type.rb b/app/graphql/types/clusters/agent_type.rb index ce748f6e8ae..89316ed4728 100644 --- a/app/graphql/types/clusters/agent_type.rb +++ b/app/graphql/types/clusters/agent_type.rb @@ -55,6 +55,12 @@ module Types complexity: 5, resolver: ::Resolvers::Kas::AgentConnectionsResolver + field :activity_events, + Types::Clusters::AgentActivityEventType.connection_type, + null: true, + description: 'Recent activity for the cluster agent.', + resolver: Resolvers::Clusters::AgentActivityEventsResolver + def project Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find end diff --git a/app/graphql/types/container_repository_details_type.rb b/app/graphql/types/container_repository_details_type.rb index 8190cc9bc25..e713aaebe36 100644 --- a/app/graphql/types/container_repository_details_type.rb +++ b/app/graphql/types/container_repository_details_type.rb @@ -12,16 +12,11 @@ module Types Types::ContainerRepositoryTagType.connection_type, null: true, description: 'Tags of the container repository.', - max_page_size: 20 + max_page_size: 20, + resolver: Resolvers::ContainerRepositoryTagsResolver def can_delete Ability.allowed?(current_user, :destroy_container_image, object) end - - def tags - object.tags - rescue Faraday::Error - raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.' - end end end diff --git a/app/graphql/types/container_repository_tags_sort_enum.rb b/app/graphql/types/container_repository_tags_sort_enum.rb new file mode 100644 index 00000000000..253cffd9a8c --- /dev/null +++ b/app/graphql/types/container_repository_tags_sort_enum.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + class ContainerRepositoryTagsSortEnum < BaseEnum + graphql_name 'ContainerRepositoryTagSort' + description 'Values for sorting tags' + + value 'NAME_ASC', 'Ordered by name in ascending order.', value: :name_asc + value 'NAME_DESC', 'Ordered by name in descending order.', value: :name_desc + end +end diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb index 3b0f93d8dc1..498569f11ca 100644 --- a/app/graphql/types/issue_type.rb +++ b/app/graphql/types/issue_type.rb @@ -80,7 +80,8 @@ module Types description: 'Relative position of the issue (used for positioning in epic tree and issue boards).' field :participants, Types::UserType.connection_type, null: true, complexity: 5, - description: 'List of participants in the issue.' + description: 'List of participants in the issue.', + resolver: Resolvers::Users::ParticipantsResolver field :emails_disabled, GraphQL::Types::Boolean, null: false, method: :project_emails_disabled?, description: 'Indicates if a project has email notifications disabled: `true` if email notifications are disabled.' diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb index 6999ea270a2..0cfba6bbbd0 100644 --- a/app/graphql/types/issue_type_enum.rb +++ b/app/graphql/types/issue_type_enum.rb @@ -5,7 +5,7 @@ module Types graphql_name 'IssueType' description 'Issue type' - ::WorkItem::Type.base_types.keys.each do |issue_type| + ::WorkItem::Type.allowed_types_for_issues.each do |issue_type| value issue_type.upcase, value: issue_type, description: "#{issue_type.titleize} issue type" end end diff --git a/app/graphql/types/merge_request_connection_type.rb b/app/graphql/types/merge_request_connection_type.rb index d009b67bc0f..9596c812c69 100644 --- a/app/graphql/types/merge_request_connection_type.rb +++ b/app/graphql/types/merge_request_connection_type.rb @@ -3,7 +3,7 @@ module Types # rubocop: disable Graphql/AuthorizeTypes class MergeRequestConnectionType < Types::CountableConnectionType - field :total_time_to_merge, GraphQL::FLOAT_TYPE, null: true, + field :total_time_to_merge, GraphQL::Types::Float, null: true, description: 'Total sum of time to merge, in seconds, for the collection of merge requests.' # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index a0f00ddc3c6..0672ec6f0f8 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -21,10 +21,8 @@ module Types description: 'Internal ID of the merge request.' field :title, GraphQL::Types::String, null: false, description: 'Title of the merge request.' - markdown_field :title_html, null: true field :description, GraphQL::Types::String, null: true, description: 'Description of the merge request (Markdown rendered as HTML for caching).' - markdown_field :description_html, null: true field :state, MergeRequestStateEnum, null: false, description: 'State of the merge request.' field :created_at, Types::TimeType, null: false, @@ -96,7 +94,7 @@ module Types description: 'Rebase commit SHA of the merge request.' field :rebase_in_progress, GraphQL::Types::Boolean, method: :rebase_in_progress?, null: false, calls_gitaly: true, description: 'Indicates if there is a rebase currently in progress for the merge request.' - field :default_merge_commit_message, GraphQL::Types::String, null: true, + field :default_merge_commit_message, GraphQL::Types::String, null: true, calls_gitaly: true, description: 'Default merge commit message of the merge request.' field :default_merge_commit_message_with_description, GraphQL::Types::String, null: true, description: 'Default merge commit message of the merge request with description. Will have the same value as `defaultMergeCommitMessage` when project has `mergeCommitTemplate` set.', @@ -148,7 +146,8 @@ module Types field :author, Types::UserType, null: true, description: 'User who created this merge request.' field :participants, Types::UserType.connection_type, null: true, complexity: 15, - description: 'Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes.' + description: 'Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes.', + resolver: Resolvers::Users::ParticipantsResolver field :subscribed, GraphQL::Types::Boolean, method: :subscribed?, null: false, complexity: 5, description: 'Indicates if the currently logged in user is subscribed to this merge request.' field :labels, Types::LabelType.connection_type, null: true, complexity: 5, @@ -201,6 +200,9 @@ module Types field :timelogs, Types::TimelogType.connection_type, null: false, description: 'Timelogs on the merge request.' + markdown_field :title_html, null: true + markdown_field :description_html, null: true + def approved_by object.approved_by_users end diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb index 3c5994ac559..ba90fb06cb2 100644 --- a/app/graphql/types/namespace_type.rb +++ b/app/graphql/types/namespace_type.rb @@ -20,7 +20,6 @@ module Types field :description, GraphQL::Types::String, null: true, description: 'Description of the namespace.' - markdown_field :description_html, null: true field :visibility, GraphQL::Types::String, null: true, description: 'Visibility of the namespace.' @@ -47,6 +46,8 @@ module Types null: true, description: "Shared runners availability for the namespace and its descendants." + markdown_field :description_html, null: true + def root_storage_statistics Gitlab::Graphql::Loaders::BatchRootStorageStatisticsLoader.new(object.id).find end diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb index da6ea83401d..7314c137010 100644 --- a/app/graphql/types/notes/note_type.rb +++ b/app/graphql/types/notes/note_type.rb @@ -33,8 +33,6 @@ module Types method: :note, description: 'Content of the note.' - markdown_field :body_html, null: true, method: :note - field :created_at, Types::TimeType, null: false, description: 'Timestamp of the note creation.' field :updated_at, Types::TimeType, null: false, @@ -50,6 +48,8 @@ module Types null: true, description: 'URL to view this Note in the Web UI.' + markdown_field :body_html, null: true, method: :note + def url ::Gitlab::UrlBuilder.build(object) end diff --git a/app/graphql/types/packages/package_details_type.rb b/app/graphql/types/packages/package_details_type.rb index 59a4885e87e..5ac80860fe2 100644 --- a/app/graphql/types/packages/package_details_type.rb +++ b/app/graphql/types/packages/package_details_type.rb @@ -14,6 +14,13 @@ module Types field :dependency_links, Types::Packages::PackageDependencyLinkType.connection_type, null: true, description: 'Dependency link.' + # this is an override of Types::Packages::PackageType.pipelines + # in order to use a custom resolver: Resolvers::PackagePipelinesResolver + field :pipelines, + resolver: Resolvers::PackagePipelinesResolver, + description: 'Pipelines that built the package.', + deprecated: { reason: 'Due to scalability concerns, this field is going to be removed', milestone: '14.6' } + def versions object.versions end diff --git a/app/graphql/types/packages/package_type.rb b/app/graphql/types/packages/package_type.rb index 9851c6aec7e..d1312cb963d 100644 --- a/app/graphql/types/packages/package_type.rb +++ b/app/graphql/types/packages/package_type.rb @@ -21,7 +21,8 @@ module Types field :tags, Types::Packages::PackageTagType.connection_type, null: true, description: 'Package tags.' field :project, Types::ProjectType, null: false, description: 'Project where the package is stored.' field :pipelines, Types::Ci::PipelineType.connection_type, null: true, - description: 'Pipelines that built the package.' + description: 'Pipelines that built the package.', + deprecated: { reason: 'Due to scalability concerns, this field is going to be removed', milestone: '14.6' } field :metadata, Types::Packages::MetadataType, null: true, description: 'Package metadata.' field :versions, ::Types::Packages::PackageType.connection_type, null: true, diff --git a/app/graphql/types/project_statistics_type.rb b/app/graphql/types/project_statistics_type.rb index 60a3d5ce06b..ab2b9c2a3af 100644 --- a/app/graphql/types/project_statistics_type.rb +++ b/app/graphql/types/project_statistics_type.rb @@ -6,26 +6,26 @@ module Types authorize :read_statistics - field :commit_count, GraphQL::FLOAT_TYPE, null: false, + field :commit_count, GraphQL::Types::Float, null: false, description: 'Commit count of the project.' - field :storage_size, GraphQL::FLOAT_TYPE, null: false, + field :storage_size, GraphQL::Types::Float, null: false, description: 'Storage size of the project in bytes.' - field :repository_size, GraphQL::FLOAT_TYPE, null: false, + field :repository_size, GraphQL::Types::Float, null: false, description: 'Repository size of the project in bytes.' - field :lfs_objects_size, GraphQL::FLOAT_TYPE, null: false, + field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'Large File Storage (LFS) object size of the project in bytes.' - field :build_artifacts_size, GraphQL::FLOAT_TYPE, null: false, + field :build_artifacts_size, GraphQL::Types::Float, null: false, description: 'Build artifacts size of the project in bytes.' - field :packages_size, GraphQL::FLOAT_TYPE, null: false, + field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size of the project in bytes.' - field :wiki_size, GraphQL::FLOAT_TYPE, null: true, + field :wiki_size, GraphQL::Types::Float, null: true, description: 'Wiki size of the project in bytes.' - field :snippets_size, GraphQL::FLOAT_TYPE, null: true, + field :snippets_size, GraphQL::Types::Float, null: true, description: 'Snippets size of the project in bytes.' - field :pipeline_artifacts_size, GraphQL::FLOAT_TYPE, null: true, + field :pipeline_artifacts_size, GraphQL::Types::Float, null: true, description: 'CI Pipeline artifacts size in bytes.' - field :uploads_size, GraphQL::FLOAT_TYPE, null: true, + field :uploads_size, GraphQL::Types::Float, null: true, description: 'Uploads size of the project in bytes.' end end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index b6cb9cd3302..3d2ee47a499 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -194,7 +194,7 @@ module Types field :jobs, type: Types::Ci::JobType.connection_type, null: true, - authorize: :read_commit_status, + authorize: :read_build, description: 'Jobs of a project. This field can only be resolved for one project in any single request.', resolver: Resolvers::ProjectJobsResolver @@ -386,6 +386,11 @@ module Types null: true, description: 'Template used to create merge commit message in merge requests.' + field :squash_commit_template, + GraphQL::Types::String, + null: true, + description: 'Template used to create squash commit message in merge requests.' + def label(title:) BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args| LabelsFinder diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 104171e6772..3265c14bdca 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -71,6 +71,10 @@ module Types field :pipeline_editor_path, GraphQL::Types::String, null: true, description: 'Web path to edit .gitlab-ci.yml file.' + field :code_owners, [Types::UserType], null: true, + description: 'List of code owners for the blob.', + calls_gitaly: true + field :file_type, GraphQL::Types::String, null: true, description: 'Expected format of the blob based on the extension.' @@ -91,6 +95,9 @@ module Types calls_gitaly: true, description: 'Whether the current user can modify the blob.' + field :can_current_user_push_to_branch, GraphQL::Types::Boolean, null: true, method: :can_current_user_push_to_branch?, + description: 'Whether the current user can push to the branch.' + def raw_text_blob object.data unless object.binary? end @@ -101,3 +108,5 @@ module Types end end end + +Types::Repository::BlobType.prepend_mod_with('Types::Repository::BlobType') diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb index 47ca195cc4b..88dc6036bfd 100644 --- a/app/graphql/types/root_storage_statistics_type.rb +++ b/app/graphql/types/root_storage_statistics_type.rb @@ -6,14 +6,14 @@ module Types authorize :read_statistics - field :storage_size, GraphQL::FLOAT_TYPE, null: false, description: 'Total storage in bytes.' - field :repository_size, GraphQL::FLOAT_TYPE, null: false, description: 'Git repository size in bytes.' - field :lfs_objects_size, GraphQL::FLOAT_TYPE, null: false, description: 'LFS objects size in bytes.' - field :build_artifacts_size, GraphQL::FLOAT_TYPE, null: false, description: 'CI artifacts size in bytes.' - field :packages_size, GraphQL::FLOAT_TYPE, null: false, description: 'Packages size in bytes.' - field :wiki_size, GraphQL::FLOAT_TYPE, null: false, description: 'Wiki size in bytes.' - field :snippets_size, GraphQL::FLOAT_TYPE, null: false, description: 'Snippets size in bytes.' - field :pipeline_artifacts_size, GraphQL::FLOAT_TYPE, null: false, description: 'CI pipeline artifacts size in bytes.' - field :uploads_size, GraphQL::FLOAT_TYPE, null: false, description: 'Uploads size in bytes.' + field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.' + field :repository_size, GraphQL::Types::Float, null: false, description: 'Git repository size in bytes.' + field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'LFS objects size in bytes.' + field :build_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI artifacts size in bytes.' + field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size in bytes.' + field :wiki_size, GraphQL::Types::Float, null: false, description: 'Wiki size in bytes.' + field :snippets_size, GraphQL::Types::Float, null: false, description: 'Snippets size in bytes.' + field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI pipeline artifacts size in bytes.' + field :uploads_size, GraphQL::Types::Float, null: false, description: 'Uploads size in bytes.' end end diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb index 5356a998f0d..3629edb5b33 100644 --- a/app/graphql/types/subscription_type.rb +++ b/app/graphql/types/subscription_type.rb @@ -6,5 +6,8 @@ module Types field :issuable_assignees_updated, subscription: Subscriptions::IssuableUpdated, null: true, description: 'Triggered when the assignees of an issuable are updated.' + + field :issue_crm_contacts_updated, subscription: Subscriptions::IssuableUpdated, null: true, + description: 'Triggered when the crm contacts of an issuable are updated.' end end diff --git a/app/graphql/types/user_callout_feature_name_enum.rb b/app/graphql/types/user_callout_feature_name_enum.rb index 410ca5e1c95..bcb49a709ed 100644 --- a/app/graphql/types/user_callout_feature_name_enum.rb +++ b/app/graphql/types/user_callout_feature_name_enum.rb @@ -5,7 +5,7 @@ module Types graphql_name 'UserCalloutFeatureNameEnum' description 'Name of the feature that the callout is for.' - ::UserCallout.feature_names.keys.each do |feature_name| + ::Users::Callout.feature_names.keys.each do |feature_name| value feature_name.upcase, value: feature_name, description: "Callout feature name for #{feature_name}." end end |