Commit a55e3de1 authored by Max Woolf's avatar Max Woolf Committed by Alex Kalderimis

Adds ProjectSetComplianceFramework GraphQL Mutation

Adds the ability to set a project's compliance framework
using the GraphQL API.

Changelog: added
EE: true
parent 54e345fb
......@@ -3375,6 +3375,28 @@ Input type: `PipelineRetryInput`
| <a id="mutationpipelineretryerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationpipelineretrypipeline"></a>`pipeline` | [`Pipeline`](#pipeline) | The pipeline after mutation. |
### `Mutation.projectSetComplianceFramework`
Assign (or unset) a compliance framework to a project.
Input type: `ProjectSetComplianceFrameworkInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationprojectsetcomplianceframeworkclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationprojectsetcomplianceframeworkcomplianceframeworkid"></a>`complianceFrameworkId` | [`ComplianceManagementFrameworkID`](#compliancemanagementframeworkid) | ID of the compliance framework to assign to the project. |
| <a id="mutationprojectsetcomplianceframeworkprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | ID of the project to change the compliance framework of. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationprojectsetcomplianceframeworkclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationprojectsetcomplianceframeworkerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationprojectsetcomplianceframeworkproject"></a>`project` | [`Project`](#project) | Project after mutation. |
### `Mutation.prometheusIntegrationCreate`
Input type: `PrometheusIntegrationCreateInput`
......
......@@ -80,6 +80,7 @@ module EE
mount_mutation ::Mutations::IncidentManagement::EscalationPolicy::Update
mount_mutation ::Mutations::IncidentManagement::EscalationPolicy::Destroy
mount_mutation ::Mutations::AppSec::Fuzzing::API::CiConfiguration::Create
mount_mutation ::Mutations::Projects::SetComplianceFramework
mount_mutation ::Mutations::SecurityPolicy::CommitScanExecutionPolicy
mount_mutation ::Mutations::SecurityPolicy::AssignSecurityPolicyProject
mount_mutation ::Mutations::SecurityPolicy::CreateSecurityPolicyProject
......
# frozen_string_literal: true
module Mutations
module Projects
class SetComplianceFramework < BaseMutation
graphql_name 'ProjectSetComplianceFramework'
description 'Assign (or unset) a compliance framework to a project.'
authorize :admin_compliance_framework
argument :project_id, Types::GlobalIDType[::Project],
required: true,
description: 'ID of the project to change the compliance framework of.'
argument :compliance_framework_id, Types::GlobalIDType[::ComplianceManagement::Framework],
required: false,
description: 'ID of the compliance framework to assign to the project.'
field :project,
Types::ProjectType,
null: true,
description: "Project after mutation."
def resolve(project_id:, compliance_framework_id:)
project = GitlabSchema.find_by_gid(project_id).sync
authorize!(project)
::Projects::UpdateService.new(project, current_user, compliance_framework_setting_attributes: {
framework: compliance_framework_id&.model_id
}).execute
{ project: project, errors: errors_on_object(project) }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Projects::SetComplianceFramework do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:framework) { create(:compliance_framework, namespace: namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
let_it_be(:current_user) { create(:user) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
subject { mutation.resolve(project_id: GitlabSchema.id_from_object(project), compliance_framework_id: GitlabSchema.id_from_object(framework)) }
shared_examples "the user cannot change a project's compliance framework" do
it 'raises an exception' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
shared_examples "the user can change a project's compliance framework" do
it 'changes the compliance framework of the project' do
expect { subject }.to change { project.reload.compliance_management_framework }.from(nil).to(framework)
end
context 'when a project had a compliance framework' do
before do
project.update!(compliance_management_framework: framework)
end
it 'can remove the compliance framework of the project' do
expect { mutation.resolve(project_id: GitlabSchema.id_from_object(project), compliance_framework_id: nil) }.to change { project.reload.compliance_management_framework }.to(nil)
end
end
it 'returns the project that was updated' do
expect(subject).to include(project: project)
end
end
describe '#resolve' do
context 'feature is licensed' do
before do
stub_licensed_features(compliance_framework: true)
end
context 'current_user is a guest' do
let(:current_user) { nil }
it_behaves_like "the user cannot change a project's compliance framework"
end
context 'current_user is a project developer' do
before do
project.add_developer(current_user)
end
it_behaves_like "the user cannot change a project's compliance framework"
end
context 'current_user is a project maintainer' do
before do
project.add_maintainer(current_user)
end
it_behaves_like "the user cannot change a project's compliance framework"
end
context 'current_user is a project owner' do
let(:current_user) { project.owner }
it_behaves_like "the user can change a project's compliance framework"
end
end
context 'feature is unlicensed' do
before do
stub_licensed_features(compliance_framework: false)
end
it_behaves_like "the user cannot change a project's compliance framework"
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Set project compliance framework' do
include GraphqlHelpers
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
let_it_be(:framework) { create(:compliance_framework, namespace: namespace) }
let_it_be(:current_user) { project.owner }
let(:variables) { { project_id: GitlabSchema.id_from_object(project).to_s, compliance_framework_id: GitlabSchema.id_from_object(framework).to_s } }
let(:mutation) do
graphql_mutation(:project_set_compliance_framework, variables) do
<<~QL
project {
complianceFrameworks {
nodes {
name
}
}
}
QL
end
end
def mutation_response
graphql_mutation_response(:project_set_compliance_framework)
end
describe '#resolve' do
context 'when feature is not available' do
before do
stub_licensed_features(compliance_framework: false)
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 feature is available' do
before do
stub_licensed_features(compliance_framework: true)
end
it_behaves_like 'a working GraphQL mutation'
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