Commit ff387090 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch 'extract-alert-management-alert-presenters' into 'master'

Extract Alert Management alert presenters

See merge request gitlab-org/gitlab!34529
parents 9025ebb0 5631edfc
...@@ -10,6 +10,7 @@ module AlertManagement ...@@ -10,6 +10,7 @@ module AlertManagement
include Sortable include Sortable
include Noteable include Noteable
include Gitlab::SQL::Pattern include Gitlab::SQL::Pattern
include Presentable
STATUSES = { STATUSES = {
triggered: 0, triggered: 0,
...@@ -25,6 +26,8 @@ module AlertManagement ...@@ -25,6 +26,8 @@ module AlertManagement
ignored: :ignore ignored: :ignore
}.freeze }.freeze
DETAILS_IGNORED_PARAMS = %w(start_time).freeze
belongs_to :project belongs_to :project
belongs_to :issue, optional: true belongs_to :issue, optional: true
...@@ -136,7 +139,7 @@ module AlertManagement ...@@ -136,7 +139,7 @@ module AlertManagement
end end
def details def details
details_payload = payload.except(*attributes.keys) details_payload = payload.except(*attributes.keys, *DETAILS_IGNORED_PARAMS)
Gitlab::Utils::InlineHash.merge_keys(details_payload) Gitlab::Utils::InlineHash.merge_keys(details_payload)
end end
...@@ -161,6 +164,12 @@ module AlertManagement ...@@ -161,6 +164,12 @@ module AlertManagement
project.execute_services(hook_data, :alert_hooks) project.execute_services(hook_data, :alert_hooks)
end end
def present
return super(presenter_class: AlertManagement::PrometheusAlertPresenter) if prometheus?
super
end
private private
def hook_data def hook_data
......
# frozen_string_literal: true
module AlertManagement
class AlertPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
include IncidentManagement::Settings
MARKDOWN_LINE_BREAK = " \n".freeze
def initialize(alert, _attributes = {})
super
@alert = alert
@project = alert.project
end
def issue_description
horizontal_line = "\n\n---\n\n"
[
issue_summary_markdown,
alert_markdown,
incident_management_setting.issue_template_content
].compact.join(horizontal_line)
end
def start_time
started_at&.strftime('%d %B %Y, %-l:%M%p (%Z)')
end
def issue_summary_markdown
<<~MARKDOWN.chomp
#### Summary
#{metadata_list}
#{alert_details}#{metric_embed_for_alert}
MARKDOWN
end
private
attr_reader :alert, :project
def alerting_alert
strong_memoize(:alerting_alert) do
Gitlab::Alerting::Alert.new(project: project, payload: alert.payload).present
end
end
def alert_markdown; end
def metadata_list
metadata = []
metadata << list_item('Start time', start_time)
metadata << list_item('Severity', severity)
metadata << list_item('full_query', backtick(full_query)) if full_query
metadata << list_item('Service', service) if service
metadata << list_item('Monitoring tool', monitoring_tool) if monitoring_tool
metadata << list_item('Hosts', host_links) if hosts.any?
metadata.join(MARKDOWN_LINE_BREAK)
end
def alert_details
if details.present?
<<~MARKDOWN.chomp
#### Alert Details
#{details_list}
MARKDOWN
end
end
def details_list
alert.details
.map { |label, value| list_item(label, value) }
.join(MARKDOWN_LINE_BREAK)
end
def metric_embed_for_alert; end
def full_query; end
def list_item(key, value)
"**#{key}:** #{value}".strip
end
def backtick(value)
"`#{value}`"
end
def host_links
hosts.join(' ')
end
end
end
# frozen_string_literal: true
module AlertManagement
class PrometheusAlertPresenter < AlertManagement::AlertPresenter
private
def alert_markdown
alerting_alert.alert_markdown
end
def details_list
alerting_alert.annotation_list
end
def metric_embed_for_alert
alerting_alert.metric_embed_for_alert
end
def full_query
alerting_alert.full_query
end
end
end
...@@ -58,6 +58,21 @@ module Projects ...@@ -58,6 +58,21 @@ module Projects
MARKDOWN MARKDOWN
end end
def annotation_list
strong_memoize(:annotation_list) do
annotations
.reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) }
.map { |annotation| list_item(annotation.label, annotation.value) }
.join(MARKDOWN_LINE_BREAK)
end
end
def metric_embed_for_alert
url = embed_url_for_gitlab_alert || embed_url_for_self_managed_alert
"\n[](#{url})" if url
end
private private
def alert_title def alert_title
...@@ -93,15 +108,6 @@ module Projects ...@@ -93,15 +108,6 @@ module Projects
end end
end end
def annotation_list
strong_memoize(:annotation_list) do
annotations
.reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) }
.map { |annotation| list_item(annotation.label, annotation.value) }
.join(MARKDOWN_LINE_BREAK)
end
end
def list_item(key, value) def list_item(key, value)
"**#{key}:** #{value}".strip "**#{key}:** #{value}".strip
end end
...@@ -120,12 +126,6 @@ module Projects ...@@ -120,12 +126,6 @@ module Projects
Array(hosts.value).join(' ') Array(hosts.value).join(' ')
end end
def metric_embed_for_alert
url = embed_url_for_gitlab_alert || embed_url_for_self_managed_alert
"\n[](#{url})" if url
end
def embed_url_for_gitlab_alert def embed_url_for_gitlab_alert
return unless gitlab_alert return unless gitlab_alert
......
...@@ -17,7 +17,7 @@ module AlertManagement ...@@ -17,7 +17,7 @@ module AlertManagement
return error_no_permissions unless allowed? return error_no_permissions unless allowed?
return error_issue_already_exists if alert.issue return error_issue_already_exists if alert.issue
result = create_issue(user, alert_payload) result = create_issue
@issue = result.payload[:issue] @issue = result.payload[:issue]
return error(result.message) if result.error? return error(result.message) if result.error?
...@@ -36,7 +36,7 @@ module AlertManagement ...@@ -36,7 +36,7 @@ module AlertManagement
user.can?(:create_issue, project) user.can?(:create_issue, project)
end end
def create_issue(user, alert_payload) def create_issue
issue = do_create_issue(label_ids: issue_label_ids) issue = do_create_issue(label_ids: issue_label_ids)
# Create an unlabelled issue if we couldn't create the issue # Create an unlabelled issue if we couldn't create the issue
...@@ -53,14 +53,6 @@ module AlertManagement ...@@ -53,14 +53,6 @@ module AlertManagement
success success
end end
def alert_payload
if alert.prometheus?
alert.payload
else
Gitlab::Alerting::NotificationPayloadParser.call(alert.payload.to_h)
end
end
def update_alert_issue_id def update_alert_issue_id
alert.update(issue_id: issue.id) alert.update(issue_id: issue.id)
end end
...@@ -85,24 +77,16 @@ module AlertManagement ...@@ -85,24 +77,16 @@ module AlertManagement
Issues::CreateService.new( Issues::CreateService.new(
project, project,
user, user,
title: issue_title, title: alert_presenter.title,
description: issue_description, description: alert_presenter.issue_description,
**params **params
).execute ).execute
end end
def issue_title def alert_presenter
alert_presenter.full_title strong_memoize(:alert_presenter) do
end alert.present
end
def issue_description
horizontal_line = "\n\n---\n\n"
[
alert_summary,
alert_markdown,
issue_template_content
].compact.join(horizontal_line)
end end
def issue_label_ids def issue_label_ids
...@@ -117,31 +101,6 @@ module AlertManagement ...@@ -117,31 +101,6 @@ module AlertManagement
.execute .execute
end end
def alert_summary
alert_presenter.issue_summary_markdown
end
def alert_markdown
alert_presenter.alert_markdown
end
def alert_presenter
strong_memoize(:alert_presenter) do
Gitlab::Alerting::Alert.new(project: project, payload: alert_payload).present
end
end
def issue_template_content
incident_management_setting.issue_template_content
end
def incident_management_setting
strong_memoize(:incident_management_setting) do
project.incident_management_setting ||
project.build_incident_management_setting
end
end
def issue_errors(issue) def issue_errors(issue)
issue.errors.full_messages.to_sentence issue.errors.full_messages.to_sentence
end end
......
...@@ -337,4 +337,22 @@ describe AlertManagement::Alert do ...@@ -337,4 +337,22 @@ describe AlertManagement::Alert do
expect { subject }.to change { alert.events }.by(1) expect { subject }.to change { alert.events }.by(1)
end end
end end
describe '#present' do
context 'when alert is generic' do
let(:alert) { build(:alert_management_alert) }
it 'uses generic alert presenter' do
expect(alert.present).to be_kind_of(AlertManagement::AlertPresenter)
end
end
context 'when alert is Prometheus specific' do
let(:alert) { build(:alert_management_alert, :prometheus) }
it 'uses Prometheus Alert presenter' do
expect(alert.present).to be_kind_of(AlertManagement::PrometheusAlertPresenter)
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AlertManagement::AlertPresenter do
let_it_be(:project) { create(:project) }
let_it_be(:generic_payload) do
{
'title' => 'Alert title',
'start_time' => '2020-04-27T10:10:22.265949279Z',
'custom' => { 'param' => 73 }
}
end
let_it_be(:alert) do
create(:alert_management_alert, :with_host, :with_service, :with_monitoring_tool, project: project, payload: generic_payload)
end
subject(:presenter) { described_class.new(alert) }
describe '#issue_description' do
let(:markdown_line_break) { ' ' }
it 'returns an alert issue description' do
expect(presenter.issue_description).to eq(
<<~MARKDOWN.chomp
#### Summary
**Start time:** #{presenter.start_time}#{markdown_line_break}
**Severity:** #{presenter.severity}#{markdown_line_break}
**Service:** #{alert.service}#{markdown_line_break}
**Monitoring tool:** #{alert.monitoring_tool}#{markdown_line_break}
**Hosts:** #{alert.hosts.join(' ')}
#### Alert Details
**custom.param:** 73
MARKDOWN
)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AlertManagement::PrometheusAlertPresenter do
let_it_be(:project) { create(:project) }
let_it_be(:prometheus_payload) do
{
'annotations' => {
'title' => 'Alert title',
'gitlab_incident_markdown' => '**`markdown example`**',
'custom annotation' => 'custom annotation value'
},
'startsAt' => '2020-04-27T10:10:22.265949279Z',
'generatorURL' => 'http://8d467bd4607a:9090/graph?g0.expr=vector%281%29&g0.tab=1'
}
end
let_it_be(:alert) do
create(:alert_management_alert, :prometheus, project: project, payload: prometheus_payload)
end
subject(:presenter) { described_class.new(alert) }
describe '#issue_description' do
let(:markdown_line_break) { ' ' }
it 'returns an alert issue description' do
expect(presenter.issue_description).to eq(
<<~MARKDOWN.chomp
#### Summary
**Start time:** #{presenter.start_time}#{markdown_line_break}
**Severity:** #{presenter.severity}#{markdown_line_break}
**full_query:** `vector(1)`#{markdown_line_break}
**Monitoring tool:** Prometheus
#### Alert Details
**custom annotation:** custom annotation value
---
**`markdown example`**
MARKDOWN
)
end
end
end
...@@ -8,10 +8,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -8,10 +8,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
let_it_be(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
let_it_be(:payload) do let_it_be(:payload) do
{ {
'title' => 'Alert title',
'annotations' => {
'title' => 'Alert title'
},
'startsAt' => '2020-04-27T10:10:22.265949279Z', 'startsAt' => '2020-04-27T10:10:22.265949279Z',
'generatorURL' => 'http://8d467bd4607a:9090/graph?g0.expr=vector%281%29&g0.tab=1' 'generatorURL' => 'http://8d467bd4607a:9090/graph?g0.expr=vector%281%29&g0.tab=1'
} }
...@@ -19,6 +15,7 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -19,6 +15,7 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
let_it_be(:generic_alert, reload: true) { create(:alert_management_alert, :triggered, project: project, payload: payload) } let_it_be(:generic_alert, reload: true) { create(:alert_management_alert, :triggered, project: project, payload: payload) }
let_it_be(:prometheus_alert, reload: true) { create(:alert_management_alert, :triggered, :prometheus, project: project, payload: payload) } let_it_be(:prometheus_alert, reload: true) { create(:alert_management_alert, :triggered, :prometheus, project: project, payload: payload) }
let(:alert) { generic_alert } let(:alert) { generic_alert }
let(:alert_presenter) { alert.present }
let(:created_issue) { Issue.last! } let(:created_issue) { Issue.last! }
describe '#execute' do describe '#execute' do
...@@ -61,7 +58,7 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -61,7 +58,7 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
end end
it 'sets the issue title' do it 'sets the issue title' do
expect(created_issue.title).to eq(alert_presenter.title) expect(created_issue.title).to eq(alert.title)
end end
it 'sets the issue description' do it 'sets the issue description' do
...@@ -165,9 +162,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -165,9 +162,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
context 'when the alert is prometheus alert' do context 'when the alert is prometheus alert' do
let(:alert) { prometheus_alert } let(:alert) { prometheus_alert }
let(:alert_presenter) do
Gitlab::Alerting::Alert.new(project: project, payload: alert.payload).present
end
it_behaves_like 'creating an alert issue' it_behaves_like 'creating an alert issue'
it_behaves_like 'setting an issue attributes' it_behaves_like 'setting an issue attributes'
...@@ -176,10 +170,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -176,10 +170,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
context 'when the alert is generic' do context 'when the alert is generic' do
let(:alert) { generic_alert } let(:alert) { generic_alert }
let(:alert_presenter) do
alert_payload = Gitlab::Alerting::NotificationPayloadParser.call(alert.payload.to_h)
Gitlab::Alerting::Alert.new(project: project, payload: alert_payload).present
end
it_behaves_like 'creating an alert issue' it_behaves_like 'creating an alert issue'
it_behaves_like 'setting an issue attributes' it_behaves_like 'setting an issue attributes'
...@@ -187,11 +177,11 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -187,11 +177,11 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
end end
context 'when issue cannot be created' do context 'when issue cannot be created' do
let(:alert) { prometheus_alert } let(:alert) { generic_alert }
before do before do
# set invalid payload for Prometheus alert # Invalid alert
alert.update!(payload: {}) alert.update_columns(title: '')
end end
it 'has an unsuccessful status' do it 'has an unsuccessful status' do
......
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