Commit de35014f authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'create_mr_mutation' into 'master'

Implement a GraphQL mutation for MR creation flow in SAST config

See merge request gitlab-org/gitlab!38406
parents be7648e6 c7b00ec0
...@@ -1531,6 +1531,46 @@ type ComplianceFrameworkEdge { ...@@ -1531,6 +1531,46 @@ type ComplianceFrameworkEdge {
node: ComplianceFramework node: ComplianceFramework
} }
"""
Autogenerated input type of ConfigureSast
"""
input ConfigureSastInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Payload containing SAST variable values (https://docs.gitlab.com/ee/user/application_security/sast/#available-variables).
"""
configuration: JSON!
"""
Full path of the project.
"""
projectPath: ID!
}
"""
Autogenerated return type of ConfigureSast
"""
type ConfigureSastPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
JSON containing the status of MR creation.
"""
result: JSON
}
""" """
A tag expiration policy designed to keep only the images that matter most A tag expiration policy designed to keep only the images that matter most
""" """
...@@ -8776,6 +8816,7 @@ type Mutation { ...@@ -8776,6 +8816,7 @@ type Mutation {
awardEmojiToggle(input: AwardEmojiToggleInput!): AwardEmojiTogglePayload awardEmojiToggle(input: AwardEmojiToggleInput!): AwardEmojiTogglePayload
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
commitCreate(input: CommitCreateInput!): CommitCreatePayload commitCreate(input: CommitCreateInput!): CommitCreatePayload
configureSast(input: ConfigureSastInput!): ConfigureSastPayload
createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload
createAnnotation(input: CreateAnnotationInput!): CreateAnnotationPayload createAnnotation(input: CreateAnnotationInput!): CreateAnnotationPayload
createBranch(input: CreateBranchInput!): CreateBranchPayload createBranch(input: CreateBranchInput!): CreateBranchPayload
......
...@@ -4153,6 +4153,122 @@ ...@@ -4153,6 +4153,122 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "ConfigureSastInput",
"description": "Autogenerated input type of ConfigureSast",
"fields": null,
"inputFields": [
{
"name": "projectPath",
"description": "Full path of the project.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "configuration",
"description": "Payload containing SAST variable values (https://docs.gitlab.com/ee/user/application_security/sast/#available-variables).",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "JSON",
"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": "ConfigureSastPayload",
"description": "Autogenerated return type of ConfigureSast",
"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
},
{
"name": "result",
"description": "JSON containing the status of MR creation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "JSON",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "ContainerExpirationPolicy", "name": "ContainerExpirationPolicy",
...@@ -24893,6 +25009,33 @@ ...@@ -24893,6 +25009,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "configureSast",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ConfigureSastInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ConfigureSastPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "createAlertIssue", "name": "createAlertIssue",
"description": null, "description": null,
...@@ -264,6 +264,16 @@ Represents a ComplianceFramework associated with a Project ...@@ -264,6 +264,16 @@ Represents a ComplianceFramework associated with a Project
| --- | ---- | ---------- | | --- | ---- | ---------- |
| `name` | ProjectSettingEnum! | Name of the compliance framework | | `name` | ProjectSettingEnum! | Name of the compliance framework |
## ConfigureSastPayload
Autogenerated return type of ConfigureSast
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `result` | JSON | JSON containing the status of MR creation. |
## ContainerExpirationPolicy ## ContainerExpirationPolicy
A tag expiration policy designed to keep only the images that matter most A tag expiration policy designed to keep only the images that matter most
......
...@@ -26,6 +26,7 @@ module EE ...@@ -26,6 +26,7 @@ module EE
mount_mutation ::Mutations::DastSiteProfiles::Create mount_mutation ::Mutations::DastSiteProfiles::Create
mount_mutation ::Mutations::DastSiteProfiles::Delete mount_mutation ::Mutations::DastSiteProfiles::Delete
mount_mutation ::Mutations::DastScannerProfiles::Create mount_mutation ::Mutations::DastScannerProfiles::Create
mount_mutation ::Mutations::Security::CiConfiguration::ConfigureSast
mount_mutation ::Mutations::Namespaces::IncreaseStorageTemporarily mount_mutation ::Mutations::Namespaces::IncreaseStorageTemporarily
end end
end end
......
# frozen_string_literal: true
module Mutations
module Security
module CiConfiguration
class ConfigureSast < BaseMutation
include ResolvesProject
graphql_name 'ConfigureSast'
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'Full path of the project.'
argument :configuration, GraphQL::Types::JSON,
required: true,
description: 'Payload containing SAST variable values (https://docs.gitlab.com/ee/user/application_security/sast/#available-variables).'
field :result,
GraphQL::Types::JSON,
null: true,
description: 'JSON containing the status of MR creation.'
authorize :push_code
def resolve(project_path:, configuration:)
project = authorized_find!(full_path: project_path)
format_json(::Security::CiConfiguration::SastCreateService.new(project, current_user, configuration).execute)
end
private
def find_object(full_path:)
resolve_project(full_path: full_path)
end
def format_json(result)
{
result: {
status: result[:status],
success_path: result[:success_path],
errors: result[:errors]
}
}
end
end
end
end
end
---
title: Implement a GraphQL mutation for MR creation flow in SAST Config
merge_request: 38406
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Security::CiConfiguration::ConfigureSast do
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { create(:user) }
let_it_be(:service_result_json) do
{
result: "3ec1b6e9a1f564da65f7c084e2497cf930dfa1c7",
status: "success",
success_path: "http://127.0.0.1:3000/root/demo-historic-secrets/-/merge_requests/new?"
}
end
let_it_be(:service_error_result_json) do
{
result: "3ec1b6e9a1f564da65f7c084e2497cf930dfa1c7",
status: "error",
errors: %w(error1 error2)
}
end
let(:context) do
GraphQL::Query::Context.new(
query: OpenStruct.new(schema: nil),
values: { current_user: user },
object: nil
)
end
specify { expect(described_class).to require_graphql_authorizations(:push_code) }
describe '#resolve' do
subject { mutation.resolve(project_path: project.full_path, configuration: {}) }
let(:result) { subject[:result] }
it 'raises an error if the resource is not accessible to the user' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
context 'when user does not have enough permissions' do
before do
project.add_guest(user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user is a maintainer of a different project' do
before do
create(:project_empty_repo).add_maintainer(user)
end
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when the user can create a merge request' do
before_all do
project.add_developer(user)
end
context 'when service successfully generates a path to create a new merge request' do
it 'returns a success path' do
allow_next_instance_of(::Security::CiConfiguration::SastCreateService) do |service|
allow(service).to receive(:execute).and_return(service_result_json)
end
expect(result).to match(
status: 'success',
success_path: service_result_json[:success_path],
errors: be_nil
)
end
end
context 'when service can not generate any path to create a new merge request' do
it 'returns an array of errors' do
allow_next_instance_of(::Security::CiConfiguration::SastCreateService) do |service|
allow(service).to receive(:execute).and_return(service_error_result_json)
end
expect(result).to match(
status: 'error',
success_path: be_nil,
errors: match_array(service_error_result_json[: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