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 0000000000000000000000000000000000000000..742ac9221988a75b1743859333259e62749fb250 --- /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/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 6aa1c50ded192603bc29d881fdf8af70667ed9ed..d37f045a95f9c8f61e5d0f32a80d91c67d6100aa 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -4688,6 +4688,30 @@ The edge type for [`CodeCoverageActivity`](#codecoverageactivity). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`CodeCoverageActivity`](#codecoverageactivity) | The item at the end of the edge. | +#### `CodeQualityDegradationConnection` + +The connection type for [`CodeQualityDegradation`](#codequalitydegradation). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `count` | [`Int!`](#int) | Total count of collection. | +| `edges` | [`[CodeQualityDegradationEdge]`](#codequalitydegradationedge) | A list of edges. | +| `nodes` | [`[CodeQualityDegradation]`](#codequalitydegradation) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `CodeQualityDegradationEdge` + +The edge type for [`CodeQualityDegradation`](#codequalitydegradation). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`CodeQualityDegradation`](#codequalitydegradation) | The item at the end of the edge. | + #### `CommitConnection` The connection type for [`Commit`](#commit). @@ -7455,6 +7479,20 @@ Represents the code coverage summary for a project. | `coverageCount` | [`Int`](#int) | Number of different code coverage results available. | | `lastUpdatedOn` | [`Date`](#date) | Latest date when the code coverage was created for the project. | +### `CodeQualityDegradation` + +Represents a code quality degradation on the pipeline. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `description` | [`String!`](#string) | A description of the code quality degradation. | +| `fingerprint` | [`String!`](#string) | A unique fingerprint to identify the code quality degradation. For example, an MD5 hash. | +| `line` | [`Int!`](#int) | The line on which the code quality degradation occurred. | +| `path` | [`String!`](#string) | The relative path to the file containing the code quality degradation. | +| `severity` | [`CodeQualityDegradationSeverity!`](#codequalitydegradationseverity) | Status of the degradation (BLOCKER, CRITICAL, MAJOR, MINOR, INFO). | + ### `Commit` #### Fields @@ -10577,6 +10615,7 @@ Information about pagination in a connection. | `active` | [`Boolean!`](#boolean) | Indicates if the pipeline is active. | | `beforeSha` | [`String`](#string) | Base SHA of the source branch. | | `cancelable` | [`Boolean!`](#boolean) | Specifies if a pipeline can be canceled. | +| `codeQualityReports` | [`CodeQualityDegradationConnection`](#codequalitydegradationconnection) | Code Quality degradations reported on the pipeline. (see [Connections](#connections)) | | `commitPath` | [`String`](#string) | Path to the commit that triggered the pipeline. | | `committedAt` | [`Time`](#time) | Timestamp of the pipeline's commit. | | `configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). | @@ -13551,6 +13590,16 @@ Values for YAML processor result. | `INSTANCE_TYPE` | A runner that is instance type. | | `PROJECT_TYPE` | A runner that is project type. | +### `CodeQualityDegradationSeverity` + +| Value | Description | +| ----- | ----------- | +| `BLOCKER` | Code Quality degradation has a status of blocker. | +| `CRITICAL` | Code Quality degradation has a status of critical. | +| `INFO` | Code Quality degradation has a status of info. | +| `MAJOR` | Code Quality degradation has a status of major. | +| `MINOR` | Code Quality degradation has a status of minor. | + ### `CommitActionMode` Mode of a commit action. diff --git a/ee/app/graphql/ee/types/ci/pipeline_type.rb b/ee/app/graphql/ee/types/ci/pipeline_type.rb index f8bbfe1296eed9506d316e392074e2c288cb085b..2f489bc6e536e3874ca0947eb5813c18a1d9cdef 100644 --- a/ee/app/graphql/ee/types/ci/pipeline_type.rb +++ b/ee/app/graphql/ee/types/ci/pipeline_type.rb @@ -19,6 +19,15 @@ module PipelineType null: true, description: 'Vulnerability findings reported on the pipeline.', resolver: ::Resolvers::PipelineSecurityReportFindingsResolver + + field :code_quality_reports, + ::Types::Ci::CodeQualityDegradationType.connection_type, + null: true, + description: 'Code Quality degradations reported on the pipeline.' + + def code_quality_reports + pipeline.codequality_reports.all_degradations.presence + end end end end diff --git a/ee/app/graphql/types/ci/code_quality_degradation_type.rb b/ee/app/graphql/types/ci/code_quality_degradation_type.rb new file mode 100644 index 0000000000000000000000000000000000000000..519c38e53acaeb0fe6d42c9b2faf49817391e4de --- /dev/null +++ b/ee/app/graphql/types/ci/code_quality_degradation_type.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Types + module Ci + # rubocop: disable Graphql/AuthorizeTypes + class CodeQualityDegradationType < BaseObject + graphql_name 'CodeQualityDegradation' + description 'Represents a code quality degradation on the pipeline.' + + connection_type_class(Types::CountableConnectionType) + + alias_method :degradation, :object + + field :description, GraphQL::STRING_TYPE, null: false, + description: "A description of the code quality degradation." + + field :fingerprint, GraphQL::STRING_TYPE, null: false, + description: 'A unique fingerprint to identify the code quality degradation. For example, an MD5 hash.' + + field :severity, Types::Ci::CodeQualityDegradationSeverityEnum, null: false, + description: "Status of the degradation (#{::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.keys.map(&:upcase).join(', ')})." + + field :path, GraphQL::STRING_TYPE, null: false, + description: 'The relative path to the file containing the code quality degradation.' + + def path + degradation.dig(:location, :path) + end + + field :line, GraphQL::INT_TYPE, null: false, + description: 'The line on which the code quality degradation occurred.' + + def line + degradation.dig(:location, :lines, :begin) || degradation.dig(:location, :positions, :begin, :line) + end + end + end +end diff --git a/ee/changelogs/unreleased/mo-add-codequality-to-graphql.yml b/ee/changelogs/unreleased/mo-add-codequality-to-graphql.yml new file mode 100644 index 0000000000000000000000000000000000000000..48d4931350f7028d0517384de0effa760d192d44 --- /dev/null +++ b/ee/changelogs/unreleased/mo-add-codequality-to-graphql.yml @@ -0,0 +1,5 @@ +--- +title: Add codequality reports endpoint to graphql +merge_request: 61383 +author: +type: added diff --git a/ee/spec/graphql/types/ci/code_quality_degradation_severity_enum_spec.rb b/ee/spec/graphql/types/ci/code_quality_degradation_severity_enum_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..875f0b8af1e4a0152a1f14b63ca2af998ebb17e3 --- /dev/null +++ b/ee/spec/graphql/types/ci/code_quality_degradation_severity_enum_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CodeQualityDegradationSeverity'] do + it 'exposes all code quality degradation severity types' do + expect(described_class.values.keys).to eq( + ::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.keys.map(&:upcase) + ) + end +end diff --git a/ee/spec/graphql/types/ci/code_quality_degradation_type_spec.rb b/ee/spec/graphql/types/ci/code_quality_degradation_type_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..fbc809ac25ff7f94698eafc3ecbb82f58e2948f8 --- /dev/null +++ b/ee/spec/graphql/types/ci/code_quality_degradation_type_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['CodeQualityDegradation'] do + it do + expect(described_class).to have_graphql_fields( + :description, + :fingerprint, + :severity, + :path, + :line + ) + end +end diff --git a/ee/spec/graphql/types/ci/pipeline_type_spec.rb b/ee/spec/graphql/types/ci/pipeline_type_spec.rb index d29037ad1ee73ff715c5c634a5c99e4fdff99e0a..abd372733badcdad51429990ff988524f3acdbf0 100644 --- a/ee/spec/graphql/types/ci/pipeline_type_spec.rb +++ b/ee/spec/graphql/types/ci/pipeline_type_spec.rb @@ -9,6 +9,7 @@ expected_fields = %w[ security_report_summary security_report_findings + code_quality_reports ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/ee/spec/requests/api/graphql/project/pipeline/code_quality_reports_spec.rb b/ee/spec/requests/api/graphql/project/pipeline/code_quality_reports_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..c09bec5d118f2ebf28ab88bfa189c72ff760df08 --- /dev/null +++ b/ee/spec/requests/api/graphql/project/pipeline/code_quality_reports_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Query.project(fullPath).pipeline(iid).codeQualityReports' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:current_user) { create(:user) } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + pipeline(iid: "#{pipeline.iid}") { + codeQualityReports { + nodes { + #{all_graphql_fields_for('CodeQualityDegradation')} + } + } + } + } + } + ) + end + + let(:codequality_degradations) { graphql_data_at(:project, :pipeline, :codeQualityReports, :nodes) } + + context 'when pipeline has a code quality report' do + let_it_be(:pipeline) { create(:ci_pipeline, :success, :with_codequality_reports, project: project) } + + context 'when user is member of the project' do + before do + project.add_developer(current_user) + end + + it 'returns all the code quality degradations' do + post_graphql(query, current_user: current_user) + + expect(codequality_degradations.size).to eq(3) + end + + it 'returns all the queried fields', :aggregate_failures do + post_graphql(query, current_user: current_user) + + codequality_degradations.each do |degradation| + expect(degradation['description']).not_to be_nil + expect(degradation['fingerprint']).not_to be_nil + expect(degradation['severity']).not_to be_nil + expect(degradation['path']).not_to be_nil + expect(degradation['line']).not_to be_nil + end + end + end + + context 'when user is not a member of the project' do + it 'returns no code quality degradations' do + post_graphql(query, current_user: current_user) + + expect(codequality_degradations).to be_nil + end + end + end + + context 'when pipeline does not have a code quality report' do + let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project) } + + before do + project.add_developer(current_user) + end + + it 'returns an empty result' do + post_graphql(query, current_user: current_user) + + expect(codequality_degradations).to be_nil + end + end +end diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb index 45d2748a23513b27206205916b3248c67c003261..c67e86a7ee111008000c870c6012b774668b05d1 100644 --- a/spec/graphql/types/ci/pipeline_type_spec.rb +++ b/spec/graphql/types/ci/pipeline_type_spec.rb @@ -18,7 +18,7 @@ ] if Gitlab.ee? - expected_fields += %w[security_report_summary security_report_findings] + expected_fields += %w[security_report_summary security_report_findings code_quality_reports] end expect(described_class).to have_graphql_fields(*expected_fields)