Commit d296d42c authored by Alan (Maciej) Paruszewski's avatar Alan (Maciej) Paruszewski Committed by Bob Van Landuyt

Add mutation to add projects to Instance Security Dashboard

This change will add new mutation to GraphQL API that will allow user to
add multiple projects to Instance Security Dashboard.
parent 4b975914
...@@ -38,6 +38,51 @@ type AddAwardEmojiPayload { ...@@ -38,6 +38,51 @@ type AddAwardEmojiPayload {
errors: [String!]! errors: [String!]!
} }
"""
Autogenerated input type of AddProjectsToSecurityDashboard
"""
input AddProjectsToSecurityDashboardInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
IDs of projects to be added to Instance Security Dashboard
"""
projectIds: [ID!]!
}
"""
Autogenerated return type of AddProjectsToSecurityDashboard
"""
type AddProjectsToSecurityDashboardPayload {
"""
IDs of projects that were added to the Instance Security Dashboard
"""
addedProjectIds: [ID!]
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
IDs of projects that are already added to the Instance Security Dashboard
"""
duplicatedProjectIds: [ID!]
"""
Reasons why the mutation failed.
"""
errors: [String!]!
"""
IDs of projects that were not added to the Instance Security Dashboard
"""
invalidProjectIds: [ID!]
}
""" """
Autogenerated input type of AdminSidekiqQueuesDeleteJobs Autogenerated input type of AdminSidekiqQueuesDeleteJobs
""" """
...@@ -5743,6 +5788,7 @@ enum MoveType { ...@@ -5743,6 +5788,7 @@ enum MoveType {
type Mutation { type Mutation {
addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload
addProjectsToSecurityDashboard(input: AddProjectsToSecurityDashboardInput!): AddProjectsToSecurityDashboardPayload
adminSidekiqQueuesDeleteJobs(input: AdminSidekiqQueuesDeleteJobsInput!): AdminSidekiqQueuesDeleteJobsPayload adminSidekiqQueuesDeleteJobs(input: AdminSidekiqQueuesDeleteJobsInput!): AdminSidekiqQueuesDeleteJobsPayload
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
......
...@@ -125,6 +125,168 @@ ...@@ -125,6 +125,168 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "AddProjectsToSecurityDashboardInput",
"description": "Autogenerated input type of AddProjectsToSecurityDashboard",
"fields": null,
"inputFields": [
{
"name": "projectIds",
"description": "IDs of projects to be added to Instance Security Dashboard",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"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": "AddProjectsToSecurityDashboardPayload",
"description": "Autogenerated return type of AddProjectsToSecurityDashboard",
"fields": [
{
"name": "addedProjectIds",
"description": "IDs of projects that were added to the Instance Security Dashboard",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"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": "duplicatedProjectIds",
"description": "IDs of projects that are already added to the Instance Security Dashboard",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"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
},
{
"name": "invalidProjectIds",
"description": "IDs of projects that were not added to the Instance Security Dashboard",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "INPUT_OBJECT", "kind": "INPUT_OBJECT",
"name": "AdminSidekiqQueuesDeleteJobsInput", "name": "AdminSidekiqQueuesDeleteJobsInput",
...@@ -16471,6 +16633,33 @@ ...@@ -16471,6 +16633,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "addProjectsToSecurityDashboard",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "AddProjectsToSecurityDashboardInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "AddProjectsToSecurityDashboardPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "adminSidekiqQueuesDeleteJobs", "name": "adminSidekiqQueuesDeleteJobs",
"description": null, "description": null,
......
...@@ -26,6 +26,18 @@ Autogenerated return type of AddAwardEmoji ...@@ -26,6 +26,18 @@ Autogenerated return type of AddAwardEmoji
| `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 | Reasons why the mutation failed. | | `errors` | String! => Array | Reasons why the mutation failed. |
## AddProjectsToSecurityDashboardPayload
Autogenerated return type of AddProjectsToSecurityDashboard
| Name | Type | Description |
| --- | ---- | ---------- |
| `addedProjectIds` | ID! => Array | IDs of projects that were added to the Instance Security Dashboard |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `duplicatedProjectIds` | ID! => Array | IDs of projects that are already added to the Instance Security Dashboard |
| `errors` | String! => Array | Reasons why the mutation failed. |
| `invalidProjectIds` | ID! => Array | IDs of projects that were not added to the Instance Security Dashboard |
## AdminSidekiqQueuesDeleteJobsPayload ## AdminSidekiqQueuesDeleteJobsPayload
Autogenerated return type of AdminSidekiqQueuesDeleteJobs Autogenerated return type of AdminSidekiqQueuesDeleteJobs
......
...@@ -18,6 +18,7 @@ module EE ...@@ -18,6 +18,7 @@ module EE
mount_mutation ::Mutations::Requirements::Update mount_mutation ::Mutations::Requirements::Update
mount_mutation ::Mutations::Vulnerabilities::Dismiss mount_mutation ::Mutations::Vulnerabilities::Dismiss
mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics
mount_mutation ::Mutations::SecurityDashboard::AddProjects
end end
end end
end end
......
# frozen_string_literal: true
module Mutations
module SecurityDashboard
class AddProjects < BaseMutation
graphql_name 'AddProjectsToSecurityDashboard'
authorize :read_instance_security_dashboard
field :invalid_project_ids, [GraphQL::ID_TYPE],
null: true,
description: 'IDs of projects that were not added to the Instance Security Dashboard'
field :added_project_ids, [GraphQL::ID_TYPE],
null: true,
description: 'IDs of projects that were added to the Instance Security Dashboard'
field :duplicated_project_ids, [GraphQL::ID_TYPE],
null: true,
description: 'IDs of projects that are already added to the Instance Security Dashboard'
argument :project_ids, [GraphQL::ID_TYPE],
required: true,
description: 'IDs of projects to be added to Instance Security Dashboard'
def resolve(project_ids:)
dashboard = authorized_find!
raise_resource_not_available_error! unless dashboard.feature_available?(:security_dashboard)
result = add_projects(project_ids.map(&method(:extract_project_id)))
{
invalid_project_ids: result.invalid_project_ids.map(&method(:to_global_id)),
added_project_ids: result.added_project_ids.map(&method(:to_global_id)),
duplicated_project_ids: result.duplicate_project_ids.map(&method(:to_global_id)),
errors: []
}
end
private
def find_object(*args)
InstanceSecurityDashboard.new(current_user)
end
def extract_project_id(global_id)
return unless global_id.present?
GitlabSchema.parse_gid(global_id, expected_type: ::Project).model_id
end
def add_projects(project_ids)
Dashboard::Projects::CreateService.new(
current_user,
current_user.security_dashboard_projects,
feature: :security_dashboard
).execute(project_ids.compact)
end
def to_global_id(project_id)
GitlabSchema.id_from_object(Project.new(id: project_id))
end
end
end
end
---
title: Add GraphQL mutation for adding projects to Instance Security Dashboard
merge_request: 30092
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
describe Mutations::SecurityDashboard::AddProjects 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(:my_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_ids) { [project, my_project, already_added_project].map(&GitlabSchema.method(:id_from_object)).map(&:to_s) }
before do
my_project.add_developer(user)
already_added_project.add_developer(user)
end
subject { mutation.resolve(project_ids: project_ids) }
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_ids is empty' do
let(:project_ids) { [] }
it { is_expected.to eq(added_project_ids: [], duplicated_project_ids: [], invalid_project_ids: [], errors: []) }
end
context 'when project_ids contains ids' do
it 'adds project that is available to the user to the security dashboard', :aggregate_failures do
expect(subject[:added_project_ids]).to eq([GitlabSchema.id_from_object(my_project)])
expect(user.security_dashboard_projects).to include(my_project)
end
it 'does not add project that already exist in the security dashboard', :aggregate_failures do
expect(subject[:duplicated_project_ids]).to eq([GitlabSchema.id_from_object(already_added_project)])
expect(user.security_dashboard_projects).to include(already_added_project)
end
it 'does not add project that is not available for the user' do
expect(subject[:invalid_project_ids]).to eq([GitlabSchema.id_from_object(project)])
end
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