Commit 7f696037 authored by Stan Hu's avatar Stan Hu

Merge branch '213742_return_related_issue_information_for_vulnerabilities' into 'master'

Add issueLinks field to Vulnerability type

See merge request gitlab-org/gitlab!33173
parents 6fc0a489 64355323
......@@ -12613,6 +12613,36 @@ type Vulnerability {
"""
id: ID!
"""
List of issue links related to the vulnerability
"""
issueLinks(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
Filter issue links by link type
"""
linkType: [VulnerabilityIssueLinkType!]
): VulnerabilityIssueLinkConnection!
"""
Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability
"""
......@@ -12695,6 +12725,69 @@ type VulnerabilityEdge {
node: Vulnerability
}
"""
Represents an issue link of a vulnerability.
"""
type VulnerabilityIssueLink {
"""
GraphQL ID of the vulnerability
"""
id: ID!
"""
The issue attached to issue link
"""
issue: Issue!
"""
Type of the issue link
"""
linkType: VulnerabilityIssueLinkType!
}
"""
The connection type for VulnerabilityIssueLink.
"""
type VulnerabilityIssueLinkConnection {
"""
A list of edges.
"""
edges: [VulnerabilityIssueLinkEdge]
"""
A list of nodes.
"""
nodes: [VulnerabilityIssueLink]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type VulnerabilityIssueLinkEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: VulnerabilityIssueLink
}
"""
The type of the issue link related to a vulnerability.
"""
enum VulnerabilityIssueLinkType {
CREATED
RELATED
}
"""
Represents a vulnerability location. The fields with data will depend on the vulnerability report type
"""
......
......@@ -37150,6 +37150,81 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "issueLinks",
"description": "List of issue links related to the vulnerability",
"args": [
{
"name": "linkType",
"description": "Filter issue links by link type",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "VulnerabilityIssueLinkType",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "VulnerabilityIssueLinkConnection",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "location",
"description": "Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability",
......@@ -37404,6 +37479,208 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityIssueLink",
"description": "Represents an issue link of a vulnerability.",
"fields": [
{
"name": "id",
"description": "GraphQL ID of the vulnerability",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "issue",
"description": "The issue attached to issue link",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Issue",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "linkType",
"description": "Type of the issue link",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "VulnerabilityIssueLinkType",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityIssueLinkConnection",
"description": "The connection type for VulnerabilityIssueLink.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "VulnerabilityIssueLinkEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "VulnerabilityIssueLink",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityIssueLinkEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityIssueLink",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "VulnerabilityIssueLinkType",
"description": "The type of the issue link related to a vulnerability.",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "RELATED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CREATED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "UNION",
"name": "VulnerabilityLocation",
......@@ -1871,6 +1871,16 @@ Represents a vulnerability.
| `userPermissions` | VulnerabilityPermissions! | Permissions for the current user on the resource |
| `vulnerabilityPath` | String | URL to the vulnerability's details page |
## VulnerabilityIssueLink
Represents an issue link of a vulnerability.
| Name | Type | Description |
| --- | ---- | ---------- |
| `id` | ID! | GraphQL ID of the vulnerability |
| `issue` | Issue! | The issue attached to issue link |
| `linkType` | VulnerabilityIssueLinkType! | Type of the issue link |
## VulnerabilityLocationContainerScanning
Represents the location of a vulnerability found by a container security scan
......
# frozen_string_literal: true
module Resolvers
module Vulnerabilities
class IssueLinksResolver < BaseResolver
type Types::Vulnerability::IssueLinkType, null: true
argument :link_type, [Types::Vulnerability::IssueLinkTypeEnum],
required: false,
description: 'Filter issue links by link type'
delegate :issue_links, to: :object, private: true
def resolve(link_type: nil, **)
issue_links.by_link_type(link_type)
end
end
end
end
# frozen_string_literal: true
module Types
module Vulnerability
# rubocop: disable Graphql/AuthorizeTypes
class IssueLinkType < BaseObject
graphql_name 'VulnerabilityIssueLink'
description 'Represents an issue link of a vulnerability.'
field :id, GraphQL::ID_TYPE, null: false,
description: 'GraphQL ID of the vulnerability'
field :link_type, ::Types::Vulnerability::IssueLinkTypeEnum, null: false,
description: "Type of the issue link"
field :issue, ::Types::IssueType, null: false,
description: 'The issue attached to issue link'
end
# rubocop: enable Graphql/AuthorizeTypes
end
end
# frozen_string_literal: true
module Types
module Vulnerability
class IssueLinkTypeEnum < BaseEnum
graphql_name 'VulnerabilityIssueLinkType'
description 'The type of the issue link related to a vulnerability.'
::Vulnerabilities::IssueLink.link_types.keys.each do |link_type|
value link_type.to_s.upcase, value: link_type.to_s
end
end
end
end
......@@ -34,6 +34,10 @@ module Types
description: "URL to the vulnerability's details page",
resolve: -> (obj, _args, _ctx) { ::Gitlab::Routing.url_helpers.project_security_vulnerability_path(obj.project, obj) }
field :issue_links, ::Types::Vulnerability::IssueLinkType.connection_type, null: false,
description: "List of issue links related to the vulnerability",
resolver: Resolvers::Vulnerabilities::IssueLinksResolver
field :location, VulnerabilityLocationType, null: true,
description: 'Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability',
resolve: -> (obj, _args, _ctx) { obj.finding&.location&.merge(report_type: obj.report_type) }
......
......@@ -17,5 +17,7 @@ module Vulnerabilities
message: N_('already has a "created" issue link')
},
if: :created?
scope :by_link_type, -> (link_type) { link_type ? where(link_type: link_type.downcase) : all }
end
end
---
title: Introduce `issueLinks` field for VulnerabilityType on GraphQL API
merge_request: 33173
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
describe Resolvers::Vulnerabilities::IssueLinksResolver do
include GraphqlHelpers
describe '#resolve' do
let_it_be(:user) { create(:user) }
let_it_be(:vulnerability) { create(:vulnerability) }
let_it_be(:related_issue) { create(:vulnerabilities_issue_link, :related, vulnerability: vulnerability) }
let_it_be(:created_issue) { create(:vulnerabilities_issue_link, :created, vulnerability: vulnerability) }
subject { resolve(described_class, obj: vulnerability, args: filters, ctx: { current_user: user }) }
context 'when there is no filter given' do
let(:filters) { {} }
it { is_expected.to match_array([related_issue, created_issue]) }
end
context 'when the link_type filter is given' do
context 'when the filter is `CREATED`' do
let(:filters) { { link_type: 'CREATED' } }
it { is_expected.to match_array([created_issue]) }
end
context 'when the filter is `RELATED`' do
let(:filters) { { link_type: 'RELATED' } }
it { is_expected.to match_array([related_issue]) }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['VulnerabilityIssueLinkType'] do
let(:expected_values) { %w[RELATED CREATED] }
subject { described_class.values.keys }
it { is_expected.to contain_exactly(*expected_values) }
end
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['VulnerabilityIssueLink'] do
let(:expected_fields) { %i[id link_type issue] }
subject { described_class }
it { is_expected.to have_graphql_fields(expected_fields) }
end
......@@ -8,7 +8,7 @@ describe GitlabSchema.types['Vulnerability'] do
let_it_be(:vulnerability) { create(:vulnerability, project: project) }
let(:fields) do
%i[userPermissions id title description user_notes_count state severity report_type vulnerability_path location project]
%i[userPermissions id title description user_notes_count state severity report_type vulnerability_path location project issueLinks]
end
before do
......
......@@ -78,4 +78,29 @@ describe Vulnerabilities::IssueLink do
end
end
end
describe '.by_link_type' do
let_it_be(:created_issue_link) { create(:vulnerabilities_issue_link, :created) }
let_it_be(:related_issue_link) { create(:vulnerabilities_issue_link, :related) }
subject { described_class.by_link_type(link_type).to_a }
context 'when the given argument is `nil`' do
let(:link_type) { nil }
it { is_expected.to match_array([created_issue_link, related_issue_link]) }
end
context 'when the given argument is an uppercase string enum value' do
let(:link_type) { 'CREATED' }
it { is_expected.to match_array([created_issue_link]) }
end
context 'when the given argument is an uppercase symbol enum value' do
let(:link_type) { :RELATED }
it { is_expected.to match_array([related_issue_link]) }
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment