Commit 4b76d7e0 authored by Sean McGivern's avatar Sean McGivern

Merge branch...

Merge branch '294266-parse-example-alert-payload-to-return-list-of-payload-alert-fields' into 'master'

Resolve "Parse example alert payload to return list of payload alert fields"

See merge request gitlab-org/gitlab!50823
parents d6a9e35c 996bd788
...@@ -742,6 +742,26 @@ enum AlertManagementIntegrationType { ...@@ -742,6 +742,26 @@ enum AlertManagementIntegrationType {
PROMETHEUS PROMETHEUS
} }
"""
Parsed field from an alert used for custom mappings
"""
type AlertManagementPayloadAlertField {
"""
Human-readable label of the payload path.
"""
label: String
"""
Path to value inside payload JSON.
"""
path: [String!]
"""
Type of the parsed value.
"""
type: AlertManagementPayloadAlertFieldType
}
""" """
Field that are available while modifying the custom mapping attributes for an HTTP integration Field that are available while modifying the custom mapping attributes for an HTTP integration
""" """
...@@ -18312,6 +18332,16 @@ type Project { ...@@ -18312,6 +18332,16 @@ type Project {
last: Int last: Int
): AlertManagementIntegrationConnection ): AlertManagementIntegrationConnection
"""
Extract alert fields from payload for custom mapping
"""
alertManagementPayloadFields(
"""
Sample payload for extracting alert fields for custom mappings.
"""
payloadExample: String!
): [AlertManagementPayloadAlertField!]
""" """
If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge
requests of the project can also be merged with skipped jobs requests of the project can also be merged with skipped jobs
......
...@@ -1909,6 +1909,69 @@ ...@@ -1909,6 +1909,69 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "AlertManagementPayloadAlertField",
"description": "Parsed field from an alert used for custom mappings",
"fields": [
{
"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": "INPUT_OBJECT", "kind": "INPUT_OBJECT",
"name": "AlertManagementPayloadAlertFieldInput", "name": "AlertManagementPayloadAlertFieldInput",
...@@ -53968,6 +54031,41 @@ ...@@ -53968,6 +54031,41 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "alertManagementPayloadFields",
"description": "Extract alert fields from payload for custom mapping",
"args": [
{
"name": "payloadExample",
"description": "Sample payload for extracting alert fields for custom mappings.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AlertManagementPayloadAlertField",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "allowMergeOnSkippedPipeline", "name": "allowMergeOnSkippedPipeline",
"description": "If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs", "description": "If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs",
...@@ -132,6 +132,16 @@ An endpoint and credentials used to accept alerts for a project. ...@@ -132,6 +132,16 @@ An endpoint and credentials used to accept alerts for a project.
| `type` | AlertManagementIntegrationType! | Type of integration. | | `type` | AlertManagementIntegrationType! | Type of integration. |
| `url` | String | Endpoint which accepts alert notifications. | | `url` | String | Endpoint which accepts alert notifications. |
### AlertManagementPayloadAlertField
Parsed field from an alert used for custom mappings.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `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 ### AlertManagementPrometheusIntegration
An endpoint and credentials used to accept Prometheus alerts for a project. An endpoint and credentials used to accept Prometheus alerts for a project.
...@@ -2767,6 +2777,7 @@ Autogenerated return type of PipelineRetry. ...@@ -2767,6 +2777,7 @@ Autogenerated return type of PipelineRetry.
| `alertManagementAlertStatusCounts` | AlertManagementAlertStatusCountsType | Counts of alerts by status for the project | | `alertManagementAlertStatusCounts` | AlertManagementAlertStatusCountsType | Counts of alerts by status for the project |
| `alertManagementAlerts` | AlertManagementAlertConnection | Alert Management alerts of the project | | `alertManagementAlerts` | AlertManagementAlertConnection | Alert Management alerts of the project |
| `alertManagementIntegrations` | AlertManagementIntegrationConnection | 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 | | `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 |
| `archived` | Boolean | Indicates the archived status of the project | | `archived` | Boolean | Indicates the archived status of the project |
| `autocloseReferencedIssues` | Boolean | Indicates if issues referenced by merge requests and commits within the default branch are closed automatically | | `autocloseReferencedIssues` | Boolean | Indicates if issues referenced by merge requests and commits within the default branch are closed automatically |
......
...@@ -116,6 +116,12 @@ module EE ...@@ -116,6 +116,12 @@ module EE
description: 'Code coverage summary associated with the project', description: 'Code coverage summary associated with the project',
resolver: ::Resolvers::Ci::CodeCoverageSummaryResolver resolver: ::Resolvers::Ci::CodeCoverageSummaryResolver
field :alert_management_payload_fields,
[::Types::AlertManagement::PayloadAlertFieldType],
null: true,
description: 'Extract alert fields from payload for custom mapping',
resolver: ::Resolvers::AlertManagement::PayloadAlertFieldResolver
field :incident_management_oncall_schedules, field :incident_management_oncall_schedules,
::Types::IncidentManagement::OncallScheduleType.connection_type, ::Types::IncidentManagement::OncallScheduleType.connection_type,
null: true, null: true,
......
# frozen_string_literal: true
module Resolvers
module AlertManagement
class PayloadAlertFieldResolver < BaseResolver
argument :payload_example, GraphQL::STRING_TYPE,
required: true,
description: 'Sample payload for extracting alert fields for custom mappings.'
type ::Types::AlertManagement::PayloadAlertFieldType, null: true
alias_method :project, :object
def resolve(payload_example:)
params = { payload: payload_example }
result = ::AlertManagement::ExtractAlertPayloadFieldsService
.new(container: project, current_user: current_user, params: params)
.execute
raise GraphQL::ExecutionError, result.message if result.error?
result.payload[:payload_alert_fields]
end
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class PayloadAlertFieldType < BaseObject
graphql_name 'AlertManagementPayloadAlertField'
description 'Parsed field from an alert used for custom mappings'
authorize :read_alert_management_alert
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
# frozen_string_literal: true
module AlertManagement
class AlertPayloadFieldPolicy < ::BasePolicy
delegate { @subject.project }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'parse alert payload fields' do
include GraphqlHelpers
let_it_be_with_refind(:project) { create(:project) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) }
let(:current_user) { maintainer }
let(:license) { true }
let(:feature_flag) { true }
let(:payload) do
{
'title' => 'value',
'started_at' => '2020-01-02 04:05:06',
'nested' => {
'key' => 'string'
},
'arr' => %w[one two]
}
end
let(:payload_json) { Gitlab::Json.generate(payload) }
let(:arguments) { { payloadExample: payload_json } }
let(:fields) { all_graphql_fields_for('AlertManagementPayloadAlertField') }
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('alertManagementPayloadFields', arguments, fields)
)
end
let(:parsed_fields) do
graphql_data.dig('project', 'alertManagementPayloadFields')
end
before_all do
project.add_developer(developer)
project.add_maintainer(maintainer)
end
before do
stub_licensed_features(multiple_alert_http_integrations: license)
stub_feature_flags(multiple_http_integrations_custom_mapping: feature_flag)
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
specify do
expect(parsed_fields).to eq([
{ 'path' => %w[title], 'label' => 'Title', 'type' => 'STRING' },
{ 'path' => %w[started_at], 'label' => 'Started at', 'type' => 'DATETIME' },
{ 'path' => %w[nested key], 'label' => 'Key', 'type' => 'STRING' },
{ 'path' => %w[arr], 'label' => 'Arr', 'type' => 'ARRAY' }
])
end
shared_examples 'query with error' do |error_message|
it 'returns an error', :aggregate_failures do
expect(parsed_fields).to be_nil
expect(graphql_errors).to include(a_hash_including('message' => error_message))
end
end
context 'without user permission' do
let(:current_user) { developer }
it_behaves_like 'query with error', 'Insufficient permissions'
end
context 'without license' do
let(:license) { false }
it_behaves_like 'query with error', 'Feature not available'
end
context 'with invalid payload JSON' do
let(:payload_json) { 'invalid json' }
it_behaves_like 'query with error', 'Failed to parse payload'
end
context 'with non-Hash JSON' do
let(:payload_json) { '1' }
it_behaves_like 'query with error', 'Failed to parse payload'
end
context 'without feature flag' do
let(:feature_flag) { false }
it_behaves_like 'query with error', 'Feature not available'
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