Commit 126e0158 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch 'ck3g-graphql-query-for-persisted-custom-mapping' into 'master'

Return custom mapping fields for HttpIntegration

See merge request gitlab-org/gitlab!53629
parents 8f47c9a2 44e2a3a4
# frozen_string_literal: true
module Resolvers
module AlertManagement
class HttpIntegrationsResolver < BaseResolver
alias_method :project, :synchronized_object
type Types::AlertManagement::HttpIntegrationType.connection_type, null: true
def resolve(**args)
http_integrations
end
private
def http_integrations
return [] unless Ability.allowed?(current_user, :admin_operations, project)
::AlertManagement::HttpIntegrationsFinder.new(project, {}).execute
end
end
end
end
......@@ -20,3 +20,5 @@ module Types
end
end
end
Types::AlertManagement::HttpIntegrationType.prepend_ee_mod
......@@ -273,6 +273,12 @@ module Types
description: 'Integrations which can receive alerts for the project.',
resolver: Resolvers::AlertManagement::IntegrationsResolver
field :alert_management_http_integrations,
Types::AlertManagement::HttpIntegrationType.connection_type,
null: true,
description: 'HTTP Integrations which can receive alerts for the project.',
resolver: Resolvers::AlertManagement::HttpIntegrationsResolver
field :releases,
Types::ReleaseType.connection_type,
null: true,
......
......@@ -634,6 +634,21 @@ type AlertManagementHttpIntegration implements AlertManagementIntegration {
"""
name: String
"""
Extract alert fields from payload example for custom mapping.
"""
payloadAlertFields: [AlertManagementPayloadAlertField!]
"""
The custom mapping of GitLab alert attributes to fields from the payload_example.
"""
payloadAttributeMappings: [AlertManagementPayloadAlertMappingField!]
"""
The example of an alert payload.
"""
payloadExample: JsonString
"""
Token used to authenticate alert notification requests.
"""
......@@ -650,6 +665,41 @@ type AlertManagementHttpIntegration implements AlertManagementIntegration {
url: String
}
"""
The connection type for AlertManagementHttpIntegration.
"""
type AlertManagementHttpIntegrationConnection {
"""
A list of edges.
"""
edges: [AlertManagementHttpIntegrationEdge]
"""
A list of nodes.
"""
nodes: [AlertManagementHttpIntegration]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type AlertManagementHttpIntegrationEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: AlertManagementHttpIntegration
}
"""
Identifier of AlertManagement::HttpIntegration.
"""
......@@ -862,6 +912,31 @@ enum AlertManagementPayloadAlertFieldType {
STRING
}
"""
Parsed field (with its name) from an alert used for custom mappings
"""
type AlertManagementPayloadAlertMappingField {
"""
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
}
"""
An endpoint and credentials used to accept Prometheus alerts for a project
"""
......@@ -19031,6 +19106,31 @@ type Project {
statuses: [AlertManagementStatus!]
): AlertManagementAlertConnection
"""
HTTP Integrations which can receive alerts for the project.
"""
alertManagementHttpIntegrations(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): AlertManagementHttpIntegrationConnection
"""
Integrations which can receive alerts for the project.
"""
......
......@@ -1578,6 +1578,64 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "payloadAlertFields",
"description": "Extract alert fields from payload example for custom mapping.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AlertManagementPayloadAlertField",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "payloadAttributeMappings",
"description": "The custom mapping of GitLab alert attributes to fields from the payload_example.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AlertManagementPayloadAlertMappingField",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "payloadExample",
"description": "The example of an alert payload.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "JsonString",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "token",
"description": "Token used to authenticate alert notification requests.",
......@@ -1636,6 +1694,118 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AlertManagementHttpIntegrationConnection",
"description": "The connection type for AlertManagementHttpIntegration.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AlertManagementHttpIntegrationEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AlertManagementHttpIntegration",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AlertManagementHttpIntegrationEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "AlertManagementHttpIntegration",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "AlertManagementHttpIntegrationID",
......@@ -2143,6 +2313,83 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AlertManagementPayloadAlertMappingField",
"description": "Parsed field (with its name) from an alert used for custom mappings",
"fields": [
{
"name": "fieldName",
"description": "A GitLab alert field name.",
"args": [
],
"type": {
"kind": "ENUM",
"name": "AlertManagementPayloadAlertFieldName",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "label",
"description": "Human-readable label of the payload path.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "path",
"description": "Path to value inside payload JSON.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "type",
"description": "Type of the parsed value.",
"args": [
],
"type": {
"kind": "ENUM",
"name": "AlertManagementPayloadAlertFieldType",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AlertManagementPrometheusIntegration",
......@@ -55864,6 +56111,59 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "alertManagementHttpIntegrations",
"description": "HTTP Integrations which can receive alerts for the project.",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "AlertManagementHttpIntegrationConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "alertManagementIntegrations",
"description": "Integrations which can receive alerts for the project.",
......@@ -274,6 +274,9 @@ An endpoint and credentials used to accept alerts for a project.
| `apiUrl` | String | URL at which Prometheus metrics can be queried to populate the metrics dashboard. |
| `id` | ID! | ID of the integration. |
| `name` | String | Name of the integration. |
| `payloadAlertFields` | AlertManagementPayloadAlertField! => Array | Extract alert fields from payload example for custom mapping. |
| `payloadAttributeMappings` | AlertManagementPayloadAlertMappingField! => Array | The custom mapping of GitLab alert attributes to fields from the payload_example. |
| `payloadExample` | JsonString | The example of an alert payload. |
| `token` | String | Token used to authenticate alert notification requests. |
| `type` | AlertManagementIntegrationType! | Type of integration. |
| `url` | String | Endpoint which accepts alert notifications. |
......@@ -288,6 +291,17 @@ Parsed field from an alert used for custom mappings.
| `path` | String! => Array | Path to value inside payload JSON. |
| `type` | AlertManagementPayloadAlertFieldType | Type of the parsed value. |
### AlertManagementPayloadAlertMappingField
Parsed field (with its name) from an alert used for custom mappings.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `fieldName` | AlertManagementPayloadAlertFieldName | A GitLab alert field name. |
| `label` | String | Human-readable label of the payload path. |
| `path` | String! => Array | Path to value inside payload JSON. |
| `type` | AlertManagementPayloadAlertFieldType | Type of the parsed value. |
### AlertManagementPrometheusIntegration
An endpoint and credentials used to accept Prometheus alerts for a project.
......@@ -3028,6 +3042,7 @@ Autogenerated return type of PipelineRetry.
| `alertManagementAlert` | AlertManagementAlert | A single Alert Management alert of the project. |
| `alertManagementAlertStatusCounts` | AlertManagementAlertStatusCountsType | Counts of alerts by status for the project. |
| `alertManagementAlerts` | AlertManagementAlertConnection | Alert Management alerts of the project. |
| `alertManagementHttpIntegrations` | AlertManagementHttpIntegrationConnection | HTTP Integrations which can receive alerts for the project. |
| `alertManagementIntegrations` | AlertManagementIntegrationConnection | Integrations which can receive alerts for the project. |
| `alertManagementPayloadFields` | AlertManagementPayloadAlertField! => Array | Extract alert fields from payload for custom mapping. |
| `allowMergeOnSkippedPipeline` | Boolean | If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs. |
......
# frozen_string_literal: true
module EE
module Types
module AlertManagement
module HttpIntegrationType
extend ActiveSupport::Concern
prepended do
field :payload_example, ::Types::JsonStringType,
null: true,
description: 'The example of an alert payload.'
field :payload_attribute_mappings, [::Types::AlertManagement::PayloadAlertMappingFieldType],
null: true,
description: 'The custom mapping of GitLab alert attributes to fields from the payload_example.',
resolver: ::Resolvers::AlertManagement::PayloadAlertMappingFieldResolver
field :payload_alert_fields, [::Types::AlertManagement::PayloadAlertFieldType],
null: true,
description: 'Extract alert fields from payload example for custom mapping.',
resolver: ::Resolvers::AlertManagement::PersistedPayloadAlertFieldResolver
end
end
end
end
end
# frozen_string_literal: true
module Resolvers
module AlertManagement
class PayloadAlertMappingFieldResolver < BaseResolver
type ::Types::AlertManagement::PayloadAlertMappingFieldType, null: true
alias_method :integration, :object
def resolve
integration.payload_attribute_mapping.map do |field_name, mapping|
::AlertManagement::AlertPayloadField.new(
project: integration.project,
field_name: field_name,
path: mapping['path'],
label: mapping['label'],
type: mapping['type']
)
end
end
end
end
end
# frozen_string_literal: true
module Resolvers
module AlertManagement
class PersistedPayloadAlertFieldResolver < BaseResolver
type ::Types::AlertManagement::PayloadAlertFieldType, null: true
alias_method :integration, :object
def resolve
Gitlab::AlertManagement::AlertPayloadFieldExtractor
.new(integration.project)
.extract(integration.payload_example)
end
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class PayloadAlertMappingFieldType < BaseObject
graphql_name 'AlertManagementPayloadAlertMappingField'
description 'Parsed field (with its name) from an alert used for custom mappings'
authorize :read_alert_management_alert
field :field_name,
::Types::AlertManagement::PayloadAlertFieldNameEnum,
null: true,
description: 'A GitLab alert field name.'
field :path,
[GraphQL::STRING_TYPE],
null: true,
description: 'Path to value inside payload JSON.'
field :label,
GraphQL::STRING_TYPE,
null: true,
description: 'Human-readable label of the payload path.'
field :type,
::Types::AlertManagement::PayloadAlertFieldTypeEnum,
null: true,
description: 'Type of the parsed value.'
end
end
end
......@@ -5,7 +5,7 @@ module AlertManagement
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :project, :path, :label, :type
attr_accessor :project, :path, :label, :type, :field_name
ARRAY_TYPE = 'array'
DATETIME_TYPE = 'datetime'
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementHttpIntegration'] do
specify { expect(described_class.graphql_name).to eq('AlertManagementHttpIntegration') }
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
it 'exposes the expected fields' do
expected_fields = %i[
id
type
name
active
token
url
api_url
payload_example
payload_attribute_mappings
payload_alert_fields
]
expect(described_class).to have_graphql_fields(*expected_fields)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting Alert Management HTTP Integrations' do
include ::Gitlab::Routing.url_helpers
include GraphqlHelpers
let_it_be(:payload_example) do
{
alert: {
desc: 'Alert description',
name: 'Alert name'
}
}
end
let_it_be(:payload_attribute_mapping) do
{
title: { path: %w(alert name), type: 'string' },
description: { path: %w(alert desc), type: 'string', label: 'Description' }
}
end
let_it_be(:project) { create(:project, :repository) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:prometheus_service) { create(:prometheus_service, project: project) }
let_it_be(:project_alerting_setting) { create(:project_alerting_setting, project: project) }
let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) }
let_it_be(:other_project_http_integration) { create(:alert_management_http_integration) }
let_it_be(:active_http_integration) do
create(
:alert_management_http_integration,
project: project,
payload_example: payload_example,
payload_attribute_mapping: payload_attribute_mapping
)
end
let(:fields) do
<<~QUERY
nodes {
#{all_graphql_fields_for('AlertManagementHttpIntegration')}
}
QUERY
end
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('alertManagementHttpIntegrations', {}, fields)
)
end
before do
stub_licensed_features(multiple_alert_http_integrations: true)
stub_feature_flags(multiple_http_integrations_custom_mapping: project)
end
before_all do
project.add_developer(developer)
project.add_maintainer(maintainer)
end
context 'with integrations' do
let(:integrations) { graphql_data.dig('project', 'alertManagementHttpIntegrations', 'nodes') }
context 'without project permissions' do
let(:current_user) { guest }
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
specify { expect(integrations).to be_nil }
end
context 'with developer permissions' do
let(:current_user) { developer }
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
specify { expect(integrations).to eq([]) }
end
context 'with maintainer permissions' do
let(:current_user) { maintainer }
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
specify { expect(integrations.size).to eq(2) }
it 'returns the correct properties of the integrations' do
expect(integrations).to include(
{
'id' => GitlabSchema.id_from_object(active_http_integration).to_s,
'type' => 'HTTP',
'name' => active_http_integration.name,
'active' => active_http_integration.active,
'token' => active_http_integration.token,
'url' => active_http_integration.url,
'apiUrl' => nil,
'payloadExample' => payload_example.to_json,
'payloadAttributeMappings' => [
{
'fieldName' => 'TITLE',
'label' => nil,
'path' => %w(alert name),
'type' => 'STRING'
},
{
'fieldName' => 'DESCRIPTION',
'label' => 'Description',
'path' => %w(alert desc),
'type' => 'STRING'
}
],
'payloadAlertFields' => [
{
'label' => 'Name',
'path' => %w(alert name),
'type' => 'STRING'
},
{
'label' => 'Desc',
'path' => %w(alert desc),
'type' => 'STRING'
}
]
},
{
'id' => GitlabSchema.id_from_object(inactive_http_integration).to_s,
'type' => 'HTTP',
'name' => inactive_http_integration.name,
'active' => inactive_http_integration.active,
'token' => inactive_http_integration.token,
'url' => inactive_http_integration.url,
'apiUrl' => nil,
'payloadExample' => "{}",
'payloadAttributeMappings' => [],
'payloadAlertFields' => []
}
)
end
end
end
end
......@@ -8,9 +8,9 @@ RSpec.describe 'getting Alert Management Integrations' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
let_it_be(:prometheus_service) { create(:prometheus_service, project: project) }
let_it_be(:project_alerting_setting) { create(:project_alerting_setting, project: project) }
let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) }
let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) }
let_it_be(:project_alerting_setting) { create(:project_alerting_setting, project: project) }
let_it_be(:other_project_http_integration) { create(:alert_management_http_integration) }
let(:fields) do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::AlertManagement::HttpIntegrationsResolver do
include GraphqlHelpers
let_it_be(:guest) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:prometheus_integration) { create(:prometheus_service, project: project) }
let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) }
let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) }
let_it_be(:other_proj_integration) { create(:alert_management_http_integration) }
subject { sync(resolve_http_integrations) }
before do
project.add_developer(developer)
project.add_maintainer(maintainer)
end
specify do
expect(described_class).to have_nullable_graphql_type(Types::AlertManagement::HttpIntegrationType.connection_type)
end
context 'user does not have permission' do
let(:current_user) { guest }
it { is_expected.to be_empty }
end
context 'user has developer permission' do
let(:current_user) { developer }
it { is_expected.to be_empty }
end
context 'user has maintainer permission' do
let(:current_user) { maintainer }
it { is_expected.to contain_exactly(active_http_integration) }
end
private
def resolve_http_integrations(args = {}, context = { current_user: current_user })
resolve(described_class, obj: project, ctx: context)
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