Commit 9a2a930a authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '294235-add-custom-mapping-to-create-http-integration' into 'master'

Extend creating HTTP Integration mutation with custom mapping attributes

See merge request gitlab-org/gitlab!50860
parents 29713000 179b0e96
...@@ -21,21 +21,30 @@ module Mutations ...@@ -21,21 +21,30 @@ module Mutations
description: 'Whether the integration is receiving alerts.' description: 'Whether the integration is receiving alerts.'
def resolve(args) def resolve(args)
project = authorized_find!(full_path: args[:project_path]) @project = authorized_find!(full_path: args[:project_path])
response ::AlertManagement::HttpIntegrations::CreateService.new( response ::AlertManagement::HttpIntegrations::CreateService.new(
project, project,
current_user, current_user,
args.slice(:name, :active) http_integration_params(args)
).execute ).execute
end end
private private
attr_reader :project
def find_object(full_path:) def find_object(full_path:)
resolve_project(full_path: full_path) resolve_project(full_path: full_path)
end end
# overriden in EE
def http_integration_params(args)
args.slice(:name, :active)
end
end end
end end
end end
end end
Mutations::AlertManagement::HttpIntegration::Create.prepend_if_ee('::EE::Mutations::AlertManagement::HttpIntegration::Create')
...@@ -9,14 +9,14 @@ module AlertManagement ...@@ -9,14 +9,14 @@ module AlertManagement
def initialize(project, current_user, params) def initialize(project, current_user, params)
@project = project @project = project
@current_user = current_user @current_user = current_user
@params = params @params = params.with_indifferent_access
end end
def execute def execute
return error_no_permissions unless allowed? return error_no_permissions unless allowed?
return error_multiple_integrations unless creation_allowed? return error_multiple_integrations unless creation_allowed?
integration = project.alert_management_http_integrations.create(params) integration = project.alert_management_http_integrations.create(permitted_params)
return error_in_create(integration) unless integration.valid? return error_in_create(integration) unless integration.valid?
success(integration) success(integration)
...@@ -34,6 +34,15 @@ module AlertManagement ...@@ -34,6 +34,15 @@ module AlertManagement
project.alert_management_http_integrations.empty? project.alert_management_http_integrations.empty?
end end
def permitted_params
params.slice(*permitted_params_keys)
end
# overriden in EE
def permitted_params_keys
%i[name active]
end
def error(message) def error(message)
ServiceResponse.error(message: message) ServiceResponse.error(message: message)
end end
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
"required": ["path", "type"], "required": ["path", "type"],
"properties": { "properties": {
"path": { "type": "array" }, "path": { "type": "array" },
"type": { "type": "string" } "type": { "type": "string" },
"label": { "type": ["string", "null"] }
}, },
"additionalProperties": false "additionalProperties": false
} }
......
...@@ -742,6 +742,106 @@ enum AlertManagementIntegrationType { ...@@ -742,6 +742,106 @@ enum AlertManagementIntegrationType {
PROMETHEUS PROMETHEUS
} }
"""
Field that are available while modifying the custom mapping attributes for an HTTP integration
"""
input AlertManagementPayloadAlertFieldInput {
"""
A GitLab alert field name.
"""
fieldName: AlertManagementPayloadAlertFieldName!
"""
Human-readable label of the payload path.
"""
label: String
"""
Path to value inside payload JSON.
"""
path: [String!]!
"""
Type of the parsed value.
"""
type: AlertManagementPayloadAlertFieldType!
}
"""
Values for alert field names used in the custom mapping
"""
enum AlertManagementPayloadAlertFieldName {
"""
A high-level summary of the problem.
"""
DESCRIPTION
"""
The resolved time of the incident.
"""
END_TIME
"""
The unique identifier of the alert. This can be used to group occurrences of the same alert.
"""
FINGERPRINT
"""
The name of the associated GitLab environment.
"""
GITLAB_ENVIRONMENT_NAME
"""
One or more hosts, as to where this incident occurred.
"""
HOSTS
"""
The name of the associated monitoring tool.
"""
MONITORING_TOOL
"""
The affected service.
"""
SERVICE
"""
The severity of the alert.
"""
SEVERITY
"""
The time of the incident.
"""
START_TIME
"""
The title of the incident.
"""
TITLE
}
"""
Values for alert field types used in the custom mapping
"""
enum AlertManagementPayloadAlertFieldType {
"""
Array field type
"""
ARRAY
"""
DateTime field type
"""
DATETIME
"""
String field type
"""
STRING
}
""" """
An endpoint and credentials used to accept Prometheus alerts for a project An endpoint and credentials used to accept Prometheus alerts for a project
""" """
...@@ -11316,6 +11416,16 @@ input HttpIntegrationCreateInput { ...@@ -11316,6 +11416,16 @@ input HttpIntegrationCreateInput {
""" """
name: String! name: String!
"""
The custom mapping of GitLab alert attributes to fields from the payload_example.
"""
payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!]
"""
The example of an alert payload.
"""
payloadExample: JsonString
""" """
The project to create the integration in. The project to create the integration in.
""" """
...@@ -13532,6 +13642,11 @@ enum JobArtifactFileType { ...@@ -13532,6 +13642,11 @@ enum JobArtifactFileType {
TRACE TRACE
} }
"""
JSON object as raw string
"""
scalar JsonString
type Label { type Label {
""" """
Background color of the label Background color of the label
......
...@@ -1909,6 +1909,177 @@ ...@@ -1909,6 +1909,177 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "AlertManagementPayloadAlertFieldInput",
"description": "Field that are available while modifying the custom mapping attributes for an HTTP integration",
"fields": null,
"inputFields": [
{
"name": "fieldName",
"description": "A GitLab alert field name.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "AlertManagementPayloadAlertFieldName",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "path",
"description": "Path to value inside payload JSON.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"defaultValue": null
},
{
"name": "label",
"description": "Human-readable label of the payload path.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "type",
"description": "Type of the parsed value.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "AlertManagementPayloadAlertFieldType",
"ofType": null
}
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "AlertManagementPayloadAlertFieldName",
"description": "Values for alert field names used in the custom mapping",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "TITLE",
"description": "The title of the incident.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "DESCRIPTION",
"description": "A high-level summary of the problem.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "START_TIME",
"description": "The time of the incident.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "END_TIME",
"description": "The resolved time of the incident.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "SERVICE",
"description": "The affected service.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "MONITORING_TOOL",
"description": "The name of the associated monitoring tool.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "HOSTS",
"description": "One or more hosts, as to where this incident occurred.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "SEVERITY",
"description": "The severity of the alert.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "FINGERPRINT",
"description": "The unique identifier of the alert. This can be used to group occurrences of the same alert.",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "GITLAB_ENVIRONMENT_NAME",
"description": "The name of the associated GitLab environment.",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "AlertManagementPayloadAlertFieldType",
"description": "Values for alert field types used in the custom mapping",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "ARRAY",
"description": "Array field type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "DATETIME",
"description": "DateTime field type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "STRING",
"description": "String field type",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "AlertManagementPrometheusIntegration", "name": "AlertManagementPrometheusIntegration",
...@@ -30957,6 +31128,34 @@ ...@@ -30957,6 +31128,34 @@
}, },
"defaultValue": null "defaultValue": null
}, },
{
"name": "payloadExample",
"description": "The example of an alert payload.",
"type": {
"kind": "SCALAR",
"name": "JsonString",
"ofType": null
},
"defaultValue": null
},
{
"name": "payloadAttributeMappings",
"description": "The custom mapping of GitLab alert attributes to fields from the payload_example.",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "AlertManagementPayloadAlertFieldInput",
"ofType": null
}
}
},
"defaultValue": null
},
{ {
"name": "clientMutationId", "name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.", "description": "A unique identifier for the client performing the mutation.",
...@@ -37235,6 +37434,16 @@ ...@@ -37235,6 +37434,16 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "SCALAR",
"name": "JsonString",
"description": "JSON object as raw string",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "Label", "name": "Label",
...@@ -4254,6 +4254,33 @@ Values of types of integrations. ...@@ -4254,6 +4254,33 @@ Values of types of integrations.
| `HTTP` | Integration with any monitoring tool | | `HTTP` | Integration with any monitoring tool |
| `PROMETHEUS` | Prometheus integration | | `PROMETHEUS` | Prometheus integration |
### AlertManagementPayloadAlertFieldName
Values for alert field names used in the custom mapping.
| Value | Description |
| ----- | ----------- |
| `DESCRIPTION` | A high-level summary of the problem. |
| `END_TIME` | The resolved time of the incident. |
| `FINGERPRINT` | The unique identifier of the alert. This can be used to group occurrences of the same alert. |
| `GITLAB_ENVIRONMENT_NAME` | The name of the associated GitLab environment. |
| `HOSTS` | One or more hosts, as to where this incident occurred. |
| `MONITORING_TOOL` | The name of the associated monitoring tool. |
| `SERVICE` | The affected service. |
| `SEVERITY` | The severity of the alert. |
| `START_TIME` | The time of the incident. |
| `TITLE` | The title of the incident. |
### AlertManagementPayloadAlertFieldType
Values for alert field types used in the custom mapping.
| Value | Description |
| ----- | ----------- |
| `ARRAY` | Array field type |
| `DATETIME` | DateTime field type |
| `STRING` | String field type |
### AlertManagementSeverity ### AlertManagementSeverity
Alert severity values. Alert severity values.
......
# frozen_string_literal: true
module EE
module Mutations
module AlertManagement
module HttpIntegration
module Create
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
argument :payload_example, ::Types::JsonStringType,
required: false,
description: 'The example of an alert payload.'
argument :payload_attribute_mappings, [::Types::AlertManagement::PayloadAlertFieldInputType],
required: false,
description: 'The custom mapping of GitLab alert attributes to fields from the payload_example.'
end
private
override :http_integration_params
def http_integration_params(args)
base_args = super(args)
return base_args unless ::Gitlab::AlertManagement.custom_mapping_available?(project)
validate_payload_example!(args[:payload_example])
args.slice(*base_args.keys, :payload_example).merge(
payload_attribute_mapping: payload_attribute_mapping(args[:payload_attribute_mappings])
)
end
def payload_attribute_mapping(mappings)
mappings.each_with_object({}) do |m, h|
h[m.field_name] = { path: m.path, type: m.type, label: m.label }
end
end
def validate_payload_example!(payload_example)
return if ::Gitlab::Utils::DeepSize.new(payload_example).valid?
raise ::Gitlab::Graphql::Errors::ArgumentError, 'payloadExample JSON is too big'
end
end
end
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
# rubocop: disable Graphql/AuthorizeTypes
class PayloadAlertFieldInputType < BaseInputObject
graphql_name 'AlertManagementPayloadAlertFieldInput'
description 'Field that are available while modifying the custom mapping attributes for an HTTP integration'
argument :field_name,
::Types::AlertManagement::PayloadAlertFieldNameEnum,
required: true,
description: 'A GitLab alert field name.'
argument :path,
[GraphQL::STRING_TYPE],
required: true,
description: 'Path to value inside payload JSON.'
argument :label,
GraphQL::STRING_TYPE,
required: false,
description: 'Human-readable label of the payload path.'
argument :type,
::Types::AlertManagement::PayloadAlertFieldTypeEnum,
required: true,
description: 'Type of the parsed value.'
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class PayloadAlertFieldNameEnum < BaseEnum
graphql_name 'AlertManagementPayloadAlertFieldName'
description 'Values for alert field names used in the custom mapping'
# The complete list of fields can be found in:
# https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html#customize-the-alert-payload-outside-of-gitlab
value 'TITLE', 'The title of the incident.', value: 'title'
value 'DESCRIPTION', 'A high-level summary of the problem.', value: 'description'
value 'START_TIME', 'The time of the incident.', value: 'start_time'
value 'END_TIME', 'The resolved time of the incident.', value: 'end_time'
value 'SERVICE', 'The affected service.', value: 'service'
value 'MONITORING_TOOL', 'The name of the associated monitoring tool.', value: 'monitoring_tool'
value 'HOSTS', 'One or more hosts, as to where this incident occurred.', value: 'hosts'
value 'SEVERITY', 'The severity of the alert.', value: 'severity'
value 'FINGERPRINT', 'The unique identifier of the alert. This can be used to group occurrences of the same alert.', value: 'fingerprint'
value 'GITLAB_ENVIRONMENT_NAME', 'The name of the associated GitLab environment.', value: 'gitlab_environment_name'
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class PayloadAlertFieldTypeEnum < BaseEnum
graphql_name 'AlertManagementPayloadAlertFieldType'
description 'Values for alert field types used in the custom mapping'
value 'ARRAY', 'Array field type', value: 'array'
value 'DATETIME', 'DateTime field type', value: 'datetime'
value 'STRING', 'String field type', value: 'string'
end
end
end
# frozen_string_literal: true
module Types
# Keep in mind when using this type, that it is not recommended to use it
# when the structure of the JSON data is known beforehand.
# More info here:
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json
class JsonStringType < BaseScalar
graphql_name 'JsonString'
description 'JSON object as raw string'
def self.coerce_input(value, _ctx)
::Gitlab::Json.parse!(value)
rescue JSON::ParserError => e
raise GraphQL::CoercionError, "Invalid JSON string: #{e.message}"
end
def self.coerce_result(value, _ctx)
value.to_json
end
end
end
...@@ -12,6 +12,13 @@ module EE ...@@ -12,6 +12,13 @@ module EE
def creation_allowed? def creation_allowed?
project.feature_available?(:multiple_alert_http_integrations) || super project.feature_available?(:multiple_alert_http_integrations) || super
end end
override :permitted_params_keys
def permitted_params_keys
return super unless ::Gitlab::AlertManagement.custom_mapping_available?(project)
super + %i[payload_example payload_attribute_mapping]
end
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module AlertManagement
def self.custom_mapping_available?(project)
::Feature.enabled?(:multiple_http_integrations_custom_mapping, project) &&
project.feature_available?(:multiple_alert_http_integrations)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementPayloadAlertFieldName'] do
it 'exposes all alert field names' do
expect(described_class.values.keys).to match_array(
%w[TITLE DESCRIPTION START_TIME END_TIME SERVICE MONITORING_TOOL HOSTS SEVERITY FINGERPRINT GITLAB_ENVIRONMENT_NAME]
)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementPayloadAlertFieldType'] do
it 'exposes all alert field types' do
expect(described_class.values.keys).to match_array(%w[ARRAY DATETIME STRING])
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['JsonString'] do
let(:hash) do
{
one: { key: 'value one' },
two: 'value two'
}
end
specify { expect(described_class.graphql_name).to eq('JsonString') }
describe '.coerce_input' do
subject(:input) { described_class.coerce_isolated_input(json_string) }
context 'when a JSON string is a valid JSON' do
let(:json_string) { hash.to_json }
it 'coerces JSON string into a Hash' do
expect(input).to eq(Gitlab::Json.parse!(json_string))
end
end
context 'when a JSON string is not a JSON' do
let(:json_string) { 'not a JSON' }
it 'raises an exception' do
expect { input }.to raise_error(GraphQL::CoercionError).with_message(%r{Invalid JSON string})
end
end
end
describe '.coerce_result' do
subject(:result) { described_class.coerce_isolated_result(hash) }
it 'coerces a hash to a JSON string' do
expect(result).to eq(hash.to_json)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Creating a new HTTP Integration' do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let(:payload_example) do
{
'alert' => { 'name' => 'Test alert' },
'started_at' => Time.current.strftime('%d %B %Y, %-l:%M%p (%Z)')
}.to_json
end
let(:payload_attribute_mappings) do
[
{ fieldName: 'TITLE', path: %w[alert name], type: 'STRING' },
{ fieldName: 'START_TIME', path: %w[started_at], type: 'DATETIME', label: 'Start time' }
]
end
let(:variables) do
{
project_path: project.full_path,
active: false,
name: 'New HTTP Integration',
payload_example: payload_example,
payload_attribute_mappings: payload_attribute_mappings
}
end
let(:mutation) do
graphql_mutation(:http_integration_create, variables) do
<<~QL
clientMutationId
errors
integration {
id
type
name
active
token
url
apiUrl
}
QL
end
end
let(:mutation_response) { graphql_mutation_response(:http_integration_create) }
shared_examples 'ignoring the custom mapping' do
it 'creates integration without the custom mapping params' do
post_graphql_mutation(mutation, current_user: current_user)
new_integration = ::AlertManagement::HttpIntegration.last!
integration_response = mutation_response['integration']
expect(response).to have_gitlab_http_status(:success)
expect(integration_response['id']).to eq(GitlabSchema.id_from_object(new_integration).to_s)
expect(new_integration.payload_example).to eq({})
expect(new_integration.payload_attribute_mapping).to eq({})
end
end
before do
project.add_maintainer(current_user)
stub_licensed_features(multiple_alert_http_integrations: true)
stub_feature_flags(multiple_http_integrations_custom_mapping: project)
end
it_behaves_like 'creating a new HTTP integration'
it 'stores the custom mapping params' do
post_graphql_mutation(mutation, current_user: current_user)
new_integration = ::AlertManagement::HttpIntegration.last!
expect(new_integration.payload_example).to eq(Gitlab::Json.parse(payload_example))
expect(new_integration.payload_attribute_mapping).to eq(
{
'title' => { 'path' => %w[alert name], 'type' => 'string', 'label' => nil },
'start_time' => { 'path' => %w[started_at], 'type' => 'datetime', 'label' => 'Start time' }
}
)
end
[:project_path, :active, :name].each do |argument|
context "without required argument #{argument}" do
before do
variables.delete(argument)
end
it_behaves_like 'an invalid argument to the mutation', argument_name: argument
end
end
context 'with the custom mappings feature unavailable' do
before do
stub_licensed_features(multiple_alert_http_integrations: false)
end
it_behaves_like 'ignoring the custom mapping'
end
context 'with multiple_http_integrations_custom_mapping feature flag disabled' do
before do
stub_feature_flags(multiple_http_integrations_custom_mapping: false)
end
it_behaves_like 'ignoring the custom mapping'
end
context 'with invalid payloadExample attribute' do
let(:payload_example) { 'not a JSON' }
it 'responds with errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect_graphql_errors_to_include(/was provided invalid value for payloadExample \(Invalid JSON string/)
end
end
context 'with invalid payloadAttributeMapping attribute does not contain fieldName' do
let(:payload_attribute_mappings) do
[{ path: %w[alert name], type: 'STRING' }]
end
it 'responds with errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect_graphql_errors_to_include(/was provided invalid value for payloadAttributeMappings\.0\.fieldName \(Expected value to not be null/)
end
end
context 'with invalid payloadAttributeMapping attribute does not contain path' do
let(:payload_attribute_mappings) do
[{ fieldName: 'TITLE', type: 'STRING' }]
end
it 'responds with errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect_graphql_errors_to_include(/was provided invalid value for payloadAttributeMappings\.0\.path \(Expected value to not be null/)
end
end
context 'with invalid payloadAttributeMapping attribute does not contain type' do
let(:payload_attribute_mappings) do
[{ fieldName: 'TITLE', path: %w[alert name] }]
end
it 'responds with errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect_graphql_errors_to_include(/was provided invalid value for payloadAttributeMappings\.0\.type \(Expected value to not be null/)
end
end
it 'validates the payload_example size' do
allow(::Gitlab::Utils::DeepSize)
.to receive(:new)
.with(Gitlab::Json.parse(payload_example))
.and_return(double(valid?: false))
post_graphql_mutation(mutation, current_user: current_user)
expect_graphql_errors_to_include(/payloadExample JSON is too big/)
end
end
...@@ -5,19 +5,54 @@ require 'spec_helper' ...@@ -5,19 +5,54 @@ require 'spec_helper'
RSpec.describe AlertManagement::HttpIntegrations::CreateService do RSpec.describe AlertManagement::HttpIntegrations::CreateService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project) } let_it_be_with_reload(:project) { create(:project) }
let(:params) { { name: 'New HTTP Integration' } }
let(:payload_example) do
{
'alert' => { 'name' => 'Test alert' },
'started_at' => Time.current.strftime('%d %B %Y, %-l:%M%p (%Z)')
}
end
let(:payload_attribute_mapping) do
{
'title' => { 'path' => %w[alert name], 'type' => 'string' },
'start_time' => { 'path' => %w[started_at], 'type' => 'datetime' }
}
end
let(:params) do
{
name: 'New HTTP Integration',
payload_example: payload_example,
payload_attribute_mapping: payload_attribute_mapping
}
end
let(:service) { described_class.new(project, user, params) } let(:service) { described_class.new(project, user, params) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
stub_licensed_features(multiple_alert_http_integrations: true)
end end
describe '#execute' do describe '#execute' do
shared_examples 'ignoring the custom mapping' do
it 'creates integration without the custom mapping params' do
expect(response).to be_success
integration = response.payload[:integration]
expect(integration).to be_a(::AlertManagement::HttpIntegration)
expect(integration.payload_example).to eq({})
expect(integration.payload_attribute_mapping).to eq({})
end
end
subject(:response) { service.execute } subject(:response) { service.execute }
context 'with multiple HTTP integrations feature available' do
before do
stub_licensed_features(multiple_alert_http_integrations: true)
end
context 'when an integration already exists' do context 'when an integration already exists' do
let_it_be(:existing_integration) { create(:alert_management_http_integration, project: project) } let_it_be(:existing_integration) { create(:alert_management_http_integration, project: project) }
...@@ -32,5 +67,30 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService do ...@@ -32,5 +67,30 @@ RSpec.describe AlertManagement::HttpIntegrations::CreateService do
expect(integration.endpoint_identifier).to be_present expect(integration.endpoint_identifier).to be_present
end end
end end
context 'with multiple_http_integrations_custom_mapping feature flag enabled' do
before do
stub_feature_flags(multiple_http_integrations_custom_mapping: project)
end
it 'successfully creates a new integration with the custom mappings' do
expect(response).to be_success
integration = response.payload[:integration]
expect(integration).to be_a(::AlertManagement::HttpIntegration)
expect(integration.name).to eq('New HTTP Integration')
expect(integration.payload_example).to eq(payload_example)
expect(integration.payload_attribute_mapping).to eq(payload_attribute_mapping)
end
end
context 'with multiple_http_integrations_custom_mapping feature flag disabled' do
before do
stub_feature_flags(multiple_http_integrations_custom_mapping: false)
end
it_behaves_like 'ignoring the custom mapping'
end
end
end end
end end
...@@ -38,7 +38,7 @@ RSpec.describe AlertManagement::HttpIntegration do ...@@ -38,7 +38,7 @@ RSpec.describe AlertManagement::HttpIntegration do
context 'with valid JSON schema' do context 'with valid JSON schema' do
let(:attribute_mapping) do let(:attribute_mapping) do
{ {
title: { path: %w(a b c), type: 'string' }, title: { path: %w(a b c), type: 'string', label: 'Title' },
description: { path: %w(a), type: 'string' } description: { path: %w(a), type: 'string' }
} }
end end
......
...@@ -39,21 +39,7 @@ RSpec.describe 'Creating a new HTTP Integration' do ...@@ -39,21 +39,7 @@ RSpec.describe 'Creating a new HTTP Integration' do
project.add_maintainer(current_user) project.add_maintainer(current_user)
end end
it 'creates a new integration' do it_behaves_like 'creating a new HTTP integration'
post_graphql_mutation(mutation, current_user: current_user)
new_integration = ::AlertManagement::HttpIntegration.last!
integration_response = mutation_response['integration']
expect(response).to have_gitlab_http_status(:success)
expect(integration_response['id']).to eq(GitlabSchema.id_from_object(new_integration).to_s)
expect(integration_response['type']).to eq('HTTP')
expect(integration_response['name']).to eq(new_integration.name)
expect(integration_response['active']).to eq(new_integration.active)
expect(integration_response['token']).to eq(new_integration.token)
expect(integration_response['url']).to eq(new_integration.url)
expect(integration_response['apiUrl']).to eq(nil)
end
[:project_path, :active, :name].each do |argument| [:project_path, :active, :name].each do |argument|
context "without required argument #{argument}" do context "without required argument #{argument}" do
......
# frozen_string_literal: true
RSpec.shared_examples 'creating a new HTTP integration' do
it 'creates a new integration' do
post_graphql_mutation(mutation, current_user: current_user)
new_integration = ::AlertManagement::HttpIntegration.last!
integration_response = mutation_response['integration']
expect(response).to have_gitlab_http_status(:success)
expect(integration_response['id']).to eq(GitlabSchema.id_from_object(new_integration).to_s)
expect(integration_response['type']).to eq('HTTP')
expect(integration_response['name']).to eq(new_integration.name)
expect(integration_response['active']).to eq(new_integration.active)
expect(integration_response['token']).to eq(new_integration.token)
expect(integration_response['url']).to eq(new_integration.url)
expect(integration_response['apiUrl']).to eq(nil)
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