Commit 0df5ceb0 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '214583-add-mutation-to-add-projects-to-security-dashboard' into 'master'

Add mutation to add projects to Instance Security Dashboard

See merge request gitlab-org/gitlab!30092
parents f7773b61 d296d42c
...@@ -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
""" """
...@@ -5898,6 +5943,7 @@ enum MoveType { ...@@ -5898,6 +5943,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",
...@@ -16820,6 +16982,33 @@ ...@@ -16820,6 +16982,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