Commit 460f2512 authored by rossfuhrman's avatar rossfuhrman Committed by Shinya Maeda

Convert to actual GraphQL types

Convert Mutations::Security::CiConfiguration to use
actual GraphQL types instead of GraphQL::Types::JSON
parent c54c3f20
......@@ -2193,12 +2193,12 @@ input ConfigureSastInput {
clientMutationId: String
"""
Payload containing SAST variable values (https://docs.gitlab.com/ee/user/application_security/sast/#available-variables).
SAST CI configuration for the project
"""
configuration: JSON!
configuration: SastCiConfigurationInput!
"""
Full path of the project.
Full path of the project
"""
projectPath: ID!
}
......@@ -2218,9 +2218,14 @@ type ConfigureSastPayload {
errors: [String!]!
"""
JSON containing the status of MR creation.
Status of creating the commit for the supplied SAST CI configuration
"""
status: String!
"""
result: JSON
Redirect path to use when the response is successful
"""
successPath: String
}
"""
......@@ -14666,6 +14671,41 @@ type SastCiConfigurationEntityEdge {
node: SastCiConfigurationEntity
}
"""
Represents an entity in SAST CI configuration
"""
input SastCiConfigurationEntityInput {
"""
Default value that is used if value is empty
"""
defaultValue: String!
"""
CI keyword of entity
"""
field: String!
"""
Current value of the entity
"""
value: String!
}
"""
Represents a CI configuration of SAST
"""
input SastCiConfigurationInput {
"""
List of global entities related to SAST configuration
"""
global: [SastCiConfigurationEntityInput!]
"""
List of pipeline entities related to SAST configuration
"""
pipeline: [SastCiConfigurationEntityInput!]
}
"""
Represents an entity for options in SAST CI configuration
"""
......
......@@ -5971,7 +5971,7 @@
"inputFields": [
{
"name": "projectPath",
"description": "Full path of the project.",
"description": "Full path of the project",
"type": {
"kind": "NON_NULL",
"name": null,
......@@ -5985,13 +5985,13 @@
},
{
"name": "configuration",
"description": "Payload containing SAST variable values (https://docs.gitlab.com/ee/user/application_security/sast/#available-variables).",
"description": "SAST CI configuration for the project",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "JSON",
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationInput",
"ofType": null
}
},
......@@ -6058,14 +6058,32 @@
"deprecationReason": null
},
{
"name": "result",
"description": "JSON containing the status of MR creation.",
"name": "status",
"description": "Status of creating the commit for the supplied SAST CI configuration",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "successPath",
"description": "Redirect path to use when the response is successful",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "JSON",
"name": "String",
"ofType": null
},
"isDeprecated": false,
......@@ -42812,6 +42830,106 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationEntityInput",
"description": "Represents an entity in SAST CI configuration",
"fields": null,
"inputFields": [
{
"name": "field",
"description": "CI keyword of entity",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "defaultValue",
"description": "Default value that is used if value is empty",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "value",
"description": "Current value of the entity",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationInput",
"description": "Represents a CI configuration of SAST",
"fields": null,
"inputFields": [
{
"name": "global",
"description": "List of global entities related to SAST configuration",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationEntityInput",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "pipeline",
"description": "List of pipeline entities related to SAST configuration",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "SastCiConfigurationEntityInput",
"ofType": null
}
}
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "SastCiConfigurationOptionsEntity",
......@@ -369,7 +369,8 @@ Autogenerated return type of ConfigureSast
| --- | ---- | ---------- |
| `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. |
| `status` | String! | Status of creating the commit for the supplied SAST CI configuration |
| `successPath` | String | Redirect path to use when the response is successful |
## ContainerExpirationPolicy
......
......@@ -10,37 +10,55 @@ module Mutations
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'Full path of the project.'
description: 'Full path of the project'
argument :configuration, GraphQL::Types::JSON, # rubocop:disable Graphql/JSONType
argument :configuration, ::Types::CiConfiguration::Sast::InputType,
required: true,
description: 'Payload containing SAST variable values (https://docs.gitlab.com/ee/user/application_security/sast/#available-variables).'
description: 'SAST CI configuration for the project'
field :result, # rubocop:disable Graphql/JSONType
GraphQL::Types::JSON,
null: true,
description: 'JSON containing the status of MR creation.'
field :status, GraphQL::STRING_TYPE, null: false,
description: 'Status of creating the commit for the supplied SAST CI configuration'
field :success_path, GraphQL::STRING_TYPE, null: true,
description: 'Redirect path to use when the response is successful'
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)
validate_flag!(project)
sast_create_service_params = format_for_service(configuration)
result = ::Security::CiConfiguration::SastCreateService.new(project, current_user, sast_create_service_params).execute
prepare_response(result)
end
private
def validate_flag!(project)
return if ::Feature.enabled?(:security_sast_configuration, project)
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'security_sast_configuration flag is not enabled on this project'
end
def find_object(full_path:)
resolve_project(full_path: full_path)
end
def format_json(result)
# Temporary formatting necessary for supporting REST API
# Will be removed during the implementation of
# https://gitlab.com/gitlab-org/gitlab/-/issues/246737
def format_for_service(configuration)
global_defaults = configuration["global"]&.collect {|k| [k["field"], k["defaultValue"]]}.to_h
pipeline_defaults = configuration["pipeline"]&.collect {|k| [k["field"], k["defaultValue"]]}.to_h
global_defaults.merge!(pipeline_defaults)
end
def prepare_response(result)
{
result: {
status: result[:status],
success_path: result[:success_path],
errors: result[:errors]
}
status: result[:status],
success_path: result[:success_path],
errors: Array(result[:errors])
}
end
end
......
# frozen_string_literal: true
module Types
module CiConfiguration
module Sast
# rubocop: disable Graphql/AuthorizeTypes
class EntityInputType < BaseInputObject
graphql_name 'SastCiConfigurationEntityInput'
description 'Represents an entity in SAST CI configuration'
argument :field, GraphQL::STRING_TYPE, required: true,
description: 'CI keyword of entity'
argument :default_value, GraphQL::STRING_TYPE, required: true,
description: 'Default value that is used if value is empty'
argument :value, GraphQL::STRING_TYPE, required: true,
description: 'Current value of the entity'
end
end
end
end
# frozen_string_literal: true
module Types
module CiConfiguration
module Sast
class InputType < BaseInputObject # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'SastCiConfigurationInput'
description 'Represents a CI configuration of SAST'
argument :global, [::Types::CiConfiguration::Sast::EntityInputType],
description: 'List of global entities related to SAST configuration',
required: false
argument :pipeline, [::Types::CiConfiguration::Sast::EntityInputType],
description: 'List of pipeline entities related to SAST configuration',
required: false
end
end
end
end
......@@ -15,6 +15,8 @@ module Security
if result[:status] == :success
result[:success_path] = successful_change_path
else
result[:errors] = result[:message]
end
result
......
---
title: Change the configureSast mutation to use actual GraphQL types instead of JSON types
merge_request: 40637
author:
type: changed
---
name: security_sast_configuration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40637
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235929
group: group::static analysis
type: development
default_enabled: false
......@@ -10,16 +10,16 @@ RSpec.describe Mutations::Security::CiConfiguration::ConfigureSast do
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?"
success_path: "http://127.0.0.1:3000/root/demo-historic-secrets/-/merge_requests/new?",
errors: nil
}
end
let_it_be(:service_error_result_json) do
{
result: "3ec1b6e9a1f564da65f7c084e2497cf930dfa1c7",
status: "error",
success_path: nil,
errors: %w(error1 error2)
}
end
......@@ -32,12 +32,16 @@ RSpec.describe Mutations::Security::CiConfiguration::ConfigureSast do
)
end
before do
stub_feature_flags(security_sast_configuration: true)
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] }
let(:result) { subject }
it 'raises an error if the resource is not accessible to the user' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
......@@ -77,11 +81,19 @@ RSpec.describe Mutations::Security::CiConfiguration::ConfigureSast do
expect(result).to match(
status: 'success',
success_path: service_result_json[:success_path],
errors: be_nil
errors: []
)
end
end
context 'when sast configuration feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_sast_configuration: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
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|
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['SastCiConfigurationEntityInput'] do
it { expect(described_class.graphql_name).to eq('SastCiConfigurationEntityInput') }
it { expect(described_class.arguments.keys).to match_array(%w[field defaultValue value]) }
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['SastCiConfigurationInput'] do
it { expect(described_class.graphql_name).to eq('SastCiConfigurationInput') }
it { expect(described_class.arguments.keys).to match_array(%w[global pipeline]) }
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