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 {
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
"""
......@@ -18312,6 +18332,16 @@ type Project {
last: Int
): 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
requests of the project can also be merged with skipped jobs
......
......@@ -1909,6 +1909,69 @@
],
"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",
"name": "AlertManagementPayloadAlertFieldInput",
......@@ -53968,6 +54031,41 @@
"isDeprecated": false,
"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",
"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.
| `type` | AlertManagementIntegrationType! | Type of integration. |
| `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
An endpoint and credentials used to accept Prometheus alerts for a project.
......@@ -2767,6 +2777,7 @@ Autogenerated return type of PipelineRetry.
| `alertManagementAlertStatusCounts` | AlertManagementAlertStatusCountsType | Counts of alerts by status for the project |
| `alertManagementAlerts` | AlertManagementAlertConnection | Alert Management alerts of 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 |
| `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 |
......
......@@ -116,6 +116,12 @@ module EE
description: 'Code coverage summary associated with the project',
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,
::Types::IncidentManagement::OncallScheduleType.connection_type,
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