Commit d4d98166 authored by Alan (Maciej) Paruszewski's avatar Alan (Maciej) Paruszewski Committed by Heinrich Lee Yu

Add mutation to Resolve Vulnerability in GraphQL

This change adds new mutation that allows user to resolve vulnerability
using GraphQL API.
parent 682ec79a
......@@ -10875,6 +10875,7 @@ type Mutation {
updateNote(input: UpdateNoteInput!): UpdateNotePayload
updateRequirement(input: UpdateRequirementInput!): UpdateRequirementPayload
updateSnippet(input: UpdateSnippetInput!): UpdateSnippetPayload
vulnerabilityResolve(input: VulnerabilityResolveInput!): VulnerabilityResolvePayload
}
"""
......@@ -18825,6 +18826,11 @@ enum VulnerabilityGrade {
F
}
"""
Identifier of Vulnerability
"""
scalar VulnerabilityID
"""
Represents a vulnerability identifier
"""
......@@ -19125,6 +19131,41 @@ enum VulnerabilityReportType {
SECRET_DETECTION
}
"""
Autogenerated input type of VulnerabilityResolve
"""
input VulnerabilityResolveInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
ID of the vulnerability to be resolveed
"""
id: VulnerabilityID!
}
"""
Autogenerated return type of VulnerabilityResolve
"""
type VulnerabilityResolvePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
The vulnerability after state change
"""
vulnerability: Vulnerability
}
"""
Represents a vulnerability scanner
"""
......
......@@ -32406,6 +32406,33 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "vulnerabilityResolve",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "VulnerabilityResolveInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityResolvePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
......@@ -55030,6 +55057,16 @@
],
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "VulnerabilityID",
"description": "Identifier of Vulnerability",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityIdentifier",
......@@ -55960,6 +55997,108 @@
],
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "VulnerabilityResolveInput",
"description": "Autogenerated input type of VulnerabilityResolve",
"fields": null,
"inputFields": [
{
"name": "id",
"description": "ID of the vulnerability to be resolveed",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "VulnerabilityID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityResolvePayload",
"description": "Autogenerated return type of VulnerabilityResolve",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "vulnerability",
"description": "The vulnerability after state change",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Vulnerability",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityScanner",
......@@ -2802,6 +2802,16 @@ Check permissions for the current user on a vulnerability.
| `readVulnerabilityFeedback` | Boolean! | Indicates the user can perform `read_vulnerability_feedback` on this resource |
| `updateVulnerabilityFeedback` | Boolean! | Indicates the user can perform `update_vulnerability_feedback` on this resource |
### VulnerabilityResolvePayload
Autogenerated return type of VulnerabilityResolve.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `vulnerability` | Vulnerability | The vulnerability after state change |
### VulnerabilityScanner
Represents a vulnerability scanner.
......
......@@ -23,6 +23,7 @@ module EE
mount_mutation ::Mutations::RequirementsManagement::CreateRequirement
mount_mutation ::Mutations::RequirementsManagement::UpdateRequirement
mount_mutation ::Mutations::Vulnerabilities::Dismiss
mount_mutation ::Mutations::Vulnerabilities::Resolve
mount_mutation ::Mutations::Boards::Update
mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics
mount_mutation ::Mutations::InstanceSecurityDashboard::AddProject
......
# frozen_string_literal: true
module Mutations
module Vulnerabilities
class Resolve < BaseMutation
graphql_name 'VulnerabilityResolve'
authorize :admin_vulnerability
field :vulnerability, Types::VulnerabilityType,
null: true,
description: 'The vulnerability after state change'
argument :id,
::Types::GlobalIDType[::Vulnerability],
required: true,
description: 'ID of the vulnerability to be resolveed'
def resolve(id:)
vulnerability = authorized_find!(id: id)
result = resolve_vulnerability(vulnerability)
{
vulnerability: result,
errors: result.errors.full_messages || []
}
end
private
def resolve_vulnerability(vulnerability)
::Vulnerabilities::ResolveService.new(current_user, vulnerability).execute
end
def find_object(id:)
GitlabSchema.find_by_gid(id)
end
end
end
end
---
title: Add mutation to Resolve Vulnerability in GraphQL
merge_request: 42500
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Vulnerabilities::Resolve do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
describe '#resolve' do
let_it_be(:vulnerability) { create(:vulnerability, :with_findings) }
let_it_be(:user) { create(:user) }
let(:mutated_vulnerability) { subject[:vulnerability] }
subject { mutation.resolve(id: GitlabSchema.id_from_object(vulnerability)) }
context 'when the user can resolve the vulnerability' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'when user doe not have access to the project' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user has access to the project' do
before do
vulnerability.project.add_developer(user)
end
it 'returns the resolveed vulnerability', :aggregate_failures do
expect(mutated_vulnerability).to eq(vulnerability)
expect(mutated_vulnerability).to be_resolved
expect(subject[:errors]).to be_empty
end
end
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