Commit b94daeb4 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '255340-graphql-delete-compliance-frameworks' into 'master'

Add ability to delete compliance framework via graphql

See merge request gitlab-org/gitlab!48912
parents 64787967 c7032f8f
...@@ -3290,6 +3290,11 @@ type ComplianceFrameworkEdge { ...@@ -3290,6 +3290,11 @@ type ComplianceFrameworkEdge {
node: ComplianceFramework node: ComplianceFramework
} }
"""
Identifier of ComplianceManagement::Framework
"""
scalar ComplianceManagementFrameworkID
""" """
Autogenerated input type of ConfigureSast Autogenerated input type of ConfigureSast
""" """
...@@ -6545,6 +6550,36 @@ type DestroyBoardPayload { ...@@ -6545,6 +6550,36 @@ type DestroyBoardPayload {
errors: [String!]! errors: [String!]!
} }
"""
Autogenerated input type of DestroyComplianceFramework
"""
input DestroyComplianceFrameworkInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
The global ID of the compliance framework to destroy
"""
id: ComplianceManagementFrameworkID!
}
"""
Autogenerated return type of DestroyComplianceFramework
"""
type DestroyComplianceFrameworkPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
}
""" """
Autogenerated input type of DestroyContainerRepository Autogenerated input type of DestroyContainerRepository
""" """
...@@ -14247,6 +14282,7 @@ type Mutation { ...@@ -14247,6 +14282,7 @@ type Mutation {
designManagementUpload(input: DesignManagementUploadInput!): DesignManagementUploadPayload designManagementUpload(input: DesignManagementUploadInput!): DesignManagementUploadPayload
destroyBoard(input: DestroyBoardInput!): DestroyBoardPayload destroyBoard(input: DestroyBoardInput!): DestroyBoardPayload
destroyBoardList(input: DestroyBoardListInput!): DestroyBoardListPayload destroyBoardList(input: DestroyBoardListInput!): DestroyBoardListPayload
destroyComplianceFramework(input: DestroyComplianceFrameworkInput!): DestroyComplianceFrameworkPayload
destroyContainerRepository(input: DestroyContainerRepositoryInput!): DestroyContainerRepositoryPayload destroyContainerRepository(input: DestroyContainerRepositoryInput!): DestroyContainerRepositoryPayload
destroyNote(input: DestroyNoteInput!): DestroyNotePayload destroyNote(input: DestroyNoteInput!): DestroyNotePayload
destroySnippet(input: DestroySnippetInput!): DestroySnippetPayload destroySnippet(input: DestroySnippetInput!): DestroySnippetPayload
......
...@@ -9045,6 +9045,16 @@ ...@@ -9045,6 +9045,16 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "SCALAR",
"name": "ComplianceManagementFrameworkID",
"description": "Identifier of ComplianceManagement::Framework",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "INPUT_OBJECT", "kind": "INPUT_OBJECT",
"name": "ConfigureSastInput", "name": "ConfigureSastInput",
...@@ -18130,6 +18140,94 @@ ...@@ -18130,6 +18140,94 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "DestroyComplianceFrameworkInput",
"description": "Autogenerated input type of DestroyComplianceFramework",
"fields": null,
"inputFields": [
{
"name": "id",
"description": "The global ID of the compliance framework to destroy",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ComplianceManagementFrameworkID",
"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": "DestroyComplianceFrameworkPayload",
"description": "Autogenerated return type of DestroyComplianceFramework",
"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
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "INPUT_OBJECT", "kind": "INPUT_OBJECT",
"name": "DestroyContainerRepositoryInput", "name": "DestroyContainerRepositoryInput",
...@@ -40589,6 +40687,33 @@ ...@@ -40589,6 +40687,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "destroyComplianceFramework",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "DestroyComplianceFrameworkInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "DestroyComplianceFrameworkPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "destroyContainerRepository", "name": "destroyContainerRepository",
"description": null, "description": null,
...@@ -1099,6 +1099,15 @@ Autogenerated return type of DestroyBoard. ...@@ -1099,6 +1099,15 @@ Autogenerated return type of DestroyBoard.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. | | `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. | | `errors` | String! => Array | Errors encountered during execution of the mutation. |
### DestroyComplianceFrameworkPayload
Autogenerated return type of DestroyComplianceFramework.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
### DestroyContainerRepositoryPayload ### DestroyContainerRepositoryPayload
Autogenerated return type of DestroyContainerRepository. Autogenerated return type of DestroyContainerRepository.
......
...@@ -10,6 +10,7 @@ module EE ...@@ -10,6 +10,7 @@ module EE
mount_mutation ::Mutations::Clusters::Agents::Delete mount_mutation ::Mutations::Clusters::Agents::Delete
mount_mutation ::Mutations::Clusters::AgentTokens::Create mount_mutation ::Mutations::Clusters::AgentTokens::Create
mount_mutation ::Mutations::Clusters::AgentTokens::Delete mount_mutation ::Mutations::Clusters::AgentTokens::Delete
mount_mutation ::Mutations::ComplianceManagement::Frameworks::Destroy
mount_mutation ::Mutations::Issues::SetIteration mount_mutation ::Mutations::Issues::SetIteration
mount_mutation ::Mutations::Issues::SetWeight mount_mutation ::Mutations::Issues::SetWeight
mount_mutation ::Mutations::Issues::SetEpic mount_mutation ::Mutations::Issues::SetEpic
......
# frozen_string_literal: true
module Mutations
module ComplianceManagement
module Frameworks
class Destroy < ::Mutations::BaseMutation
graphql_name 'DestroyComplianceFramework'
authorize :manage_compliance_framework
argument :id,
::Types::GlobalIDType[::ComplianceManagement::Framework],
required: true,
description: 'The global ID of the compliance framework to destroy'
def resolve(id:)
framework = authorized_find!(id: id)
result = ::ComplianceManagement::Frameworks::DestroyService.new(framework: framework, current_user: current_user).execute
{ errors: result.success? ? [] : Array.wrap(result.message) }
end
private
def find_object(id:)
GitlabSchema.object_from_id(id, expected_type: ::ComplianceManagement::Framework)
end
end
end
end
end
...@@ -5,7 +5,7 @@ module ComplianceManagement ...@@ -5,7 +5,7 @@ module ComplianceManagement
delegate { @subject.namespace } delegate { @subject.namespace }
condition(:custom_compliance_frameworks_enabled) do condition(:custom_compliance_frameworks_enabled) do
License.feature_available?(:custom_compliance_frameworks) License.feature_available?(:custom_compliance_frameworks) && Feature.enabled?(:ff_custom_compliance_frameworks)
end end
rule { can?(:owner_access) & custom_compliance_frameworks_enabled }.policy do rule { can?(:owner_access) & custom_compliance_frameworks_enabled }.policy do
......
---
title: Add GraphQL mutation to destroy compliance framework
merge_request: 48912
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::ComplianceManagement::Frameworks::Destroy do
include GraphqlHelpers
let_it_be(:framework) { create(:compliance_framework) }
let(:user) { framework.namespace.owner }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject { mutation.resolve(id: global_id_of(framework)) }
shared_examples 'a compliance framework that cannot be found' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
shared_examples 'one compliance framework was destroyed' do
it 'destroys a compliance framework' do
expect { subject }.to change { ComplianceManagement::Framework.count }.by(-1)
end
it 'expects zero errors in the response' do
expect(subject[:errors]).to be_empty
end
end
context 'feature is unlicensed' do
before do
stub_licensed_features(custom_compliance_frameworks: false)
end
it_behaves_like 'a compliance framework that cannot be found'
end
context 'feature is disabled but is licensed' do
before do
stub_feature_flags(ff_custom_compliance_frameworks: false)
stub_licensed_features(custom_compliance_frameworks: true)
end
it_behaves_like 'a compliance framework that cannot be found'
end
context 'feature is enabled and licensed' do
before do
stub_licensed_features(custom_compliance_frameworks: true)
stub_feature_flags(ff_custom_compliance_frameworks: true)
end
context 'current_user is namespace owner' do
it_behaves_like 'one compliance framework was destroyed'
end
context 'current_user is group owner' do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:framework) { create(:compliance_framework, namespace: group)}
before do
group.add_owner(user)
end
it_behaves_like 'one compliance framework was destroyed'
end
end
end
...@@ -49,4 +49,12 @@ RSpec.describe ComplianceManagement::FrameworkPolicy do ...@@ -49,4 +49,12 @@ RSpec.describe ComplianceManagement::FrameworkPolicy do
it { is_expected.to be_disallowed(:manage_compliance_framework) } it { is_expected.to be_disallowed(:manage_compliance_framework) }
end end
context 'feature is disabled' do
before do
stub_feature_flags(ff_custom_compliance_framework: false)
end
it { is_expected.to be_disallowed(:manage_compliance_framework) }
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Delete a compliance framework' do
include GraphqlHelpers
let_it_be(:framework) { create(:compliance_framework) }
let(:mutation) { graphql_mutation(:destroy_compliance_framework, { id: global_id_of(framework) }) }
subject { post_graphql_mutation(mutation, current_user: current_user) }
def mutation_response
graphql_mutation_response(:destroy_compliance_framework)
end
context 'feature is unlicensed' do
let_it_be(:current_user) { framework.namespace.owner }
before do
stub_licensed_features(custom_compliance_frameworks: false)
stub_feature_flags(ff_custom_compliance_frameworks: true)
end
it 'does not destroy a compliance framework' do
expect { subject }.not_to change { ComplianceManagement::Framework.count }
end
it_behaves_like 'a mutation that returns top-level errors',
errors: ["The resource that you are attempting to access does not exist or you don't have permission to perform this action"]
end
context 'when licensed and enabled' do
before do
stub_licensed_features(custom_compliance_frameworks: true)
stub_feature_flags(ff_custom_compliance_frameworks: true)
end
context 'current_user is namespace owner' do
let_it_be(:current_user) { framework.namespace.owner }
it 'has no errors' do
subject
expect(mutation_response['errors']).to be_empty
end
it 'destroys a compliance framework' do
expect { subject }.to change { ComplianceManagement::Framework.count }.by(-1)
end
end
context 'current_user is not namespace owner' do
let_it_be(:current_user) { create(:user) }
it 'does not destroy a compliance framework' do
expect { subject }.not_to change { ComplianceManagement::Framework.count }
end
it_behaves_like 'a mutation that returns top-level errors',
errors: ["The resource that you are attempting to access does not exist or you don't have permission to perform this action"]
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