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

Add mutation to remove project from Instance Security Dashboard

This change adds new mutation to GraphQL API that allows user to remove
project from Instance Security Dashboard.
parent f7d46e8a
......@@ -6221,6 +6221,7 @@ type Mutation {
mergeRequestSetSubscription(input: MergeRequestSetSubscriptionInput!): MergeRequestSetSubscriptionPayload
mergeRequestSetWip(input: MergeRequestSetWipInput!): MergeRequestSetWipPayload
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload
removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload
todoMarkDone(input: TodoMarkDoneInput!): TodoMarkDonePayload
todoRestore(input: TodoRestoreInput!): TodoRestorePayload
todoRestoreMany(input: TodoRestoreManyInput!): TodoRestoreManyPayload
......@@ -8366,6 +8367,36 @@ type RemoveAwardEmojiPayload {
errors: [String!]!
}
"""
Autogenerated input type of RemoveProjectFromSecurityDashboard
"""
input RemoveProjectFromSecurityDashboardInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
ID of the project to remove from the Instance Security Dashboard
"""
projectId: ID!
}
"""
Autogenerated return type of RemoveProjectFromSecurityDashboard
"""
type RemoveProjectFromSecurityDashboardPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Reasons why the mutation failed.
"""
errors: [String!]!
}
type Repository {
"""
Indicates repository has no visible content
......
......@@ -18271,6 +18271,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "removeProjectFromSecurityDashboard",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "RemoveProjectFromSecurityDashboardInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "RemoveProjectFromSecurityDashboardPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "todoMarkDone",
"description": null,
......@@ -24614,6 +24641,94 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "RemoveProjectFromSecurityDashboardInput",
"description": "Autogenerated input type of RemoveProjectFromSecurityDashboard",
"fields": null,
"inputFields": [
{
"name": "projectId",
"description": "ID of the project to remove from the Instance Security Dashboard",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"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": "RemoveProjectFromSecurityDashboardPayload",
"description": "Autogenerated return type of RemoveProjectFromSecurityDashboard",
"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": "Reasons why the mutation failed.",
"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": "OBJECT",
"name": "Repository",
......
......@@ -1180,6 +1180,15 @@ Autogenerated return type of RemoveAwardEmoji
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Reasons why the mutation failed. |
## RemoveProjectFromSecurityDashboardPayload
Autogenerated return type of RemoveProjectFromSecurityDashboard
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Reasons why the mutation failed. |
## Repository
| Name | Type | Description |
......
......@@ -19,6 +19,7 @@ module EE
mount_mutation ::Mutations::Vulnerabilities::Dismiss
mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics
mount_mutation ::Mutations::InstanceSecurityDashboard::AddProject
mount_mutation ::Mutations::InstanceSecurityDashboard::RemoveProject
end
end
end
......
# frozen_string_literal: true
module Mutations
module InstanceSecurityDashboard
class RemoveProject < BaseMutation
graphql_name 'RemoveProjectFromSecurityDashboard'
authorize :read_instance_security_dashboard
argument :project_id, GraphQL::ID_TYPE,
required: true,
description: 'ID of the project to remove from the Instance Security Dashboard'
def resolve(project_id:)
dashboard = authorized_find!
raise_resource_not_available_error! unless dashboard.feature_available?(:security_dashboard)
result = remove_project(extract_project_id(project_id))
{
errors: result.zero? ? ['The project does not belong to your dashboard or you don\'t have permission to perform this action'] : []
}
end
private
def find_object(*args)
::InstanceSecurityDashboard.new(current_user)
end
def extract_project_id(gid)
return unless gid.present?
GitlabSchema.parse_gid(gid, expected_type: ::Project).model_id
end
def remove_project(project_id)
current_user
.users_security_dashboard_projects
.delete_by_project_id(project_id)
end
end
end
end
---
title: Add GraphQL mutation for removing projects from Instance Security Dashboard
merge_request: 30100
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
describe Mutations::InstanceSecurityDashboard::RemoveProject do
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
describe '#resolve' do
let_it_be(:project) { create(:project) }
let_it_be(:already_added_project) { create(:project) }
let_it_be(:user) { create(:user, security_dashboard_projects: [already_added_project]) }
let(:project_id) { GitlabSchema.id_from_object(project) }
before_all do
already_added_project.add_developer(user)
end
subject { mutation.resolve(project_id: project_id) }
context 'when user is not logged_in' do
let(:current_user) { nil }
it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user is logged_in' do
let(:current_user) { user }
context 'when security_dashboard is not enabled' do
it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when security_dashboard is enabled' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'when project is not configured in security dashboard' do
it { is_expected.to eq(errors: ['The project does not belong to your dashboard or you don\'t have permission to perform this action']) }
end
context 'when project is configured in security dashboard' do
let(:project_id) { GitlabSchema.id_from_object(already_added_project) }
it { is_expected.to eq(errors: []) }
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