Commit c51b7fe5 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '230934-extract-incidents-create-service' into 'master'

Extract `Incidents::CreateService` from create incident issue services

Closes #230934

See merge request gitlab-org/gitlab!37590
parents 5eb91cf5 93f6b911
...@@ -15,10 +15,10 @@ module AlertManagement ...@@ -15,10 +15,10 @@ 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 result = create_incident
issue = result.payload[:issue] return result unless result.success?
return error(result.message, issue) if result.error? issue = result.payload[:issue]
return error(object_errors(alert), issue) unless associate_alert_with_issue(issue) return error(object_errors(alert), issue) unless associate_alert_with_issue(issue)
SystemNoteService.new_alert_issue(alert, issue, user) SystemNoteService.new_alert_issue(alert, issue, user)
...@@ -36,30 +36,19 @@ module AlertManagement ...@@ -36,30 +36,19 @@ module AlertManagement
user.can?(:create_issue, project) user.can?(:create_issue, project)
end end
def create_issue def create_incident
label_result = find_or_create_incident_label ::IncidentManagement::Incidents::CreateService.new(
issue = Issues::CreateService.new(
project, project,
user, user,
title: alert_presenter.title, title: alert_presenter.title,
description: alert_presenter.issue_description, description: alert_presenter.issue_description
label_ids: [label_result.payload[:label].id]
).execute ).execute
return error(object_errors(issue), issue) unless issue.valid?
success(issue)
end end
def associate_alert_with_issue(issue) def associate_alert_with_issue(issue)
alert.update(issue_id: issue.id) alert.update(issue_id: issue.id)
end end
def success(issue)
ServiceResponse.success(payload: { issue: issue })
end
def error(message, issue = nil) def error(message, issue = nil)
ServiceResponse.error(payload: { issue: issue }, message: message) ServiceResponse.error(payload: { issue: issue }, message: message)
end end
...@@ -78,10 +67,6 @@ module AlertManagement ...@@ -78,10 +67,6 @@ module AlertManagement
end end
end end
def find_or_create_incident_label
IncidentManagement::CreateIncidentLabelService.new(project, user).execute
end
def object_errors(object) def object_errors(object)
object.errors.full_messages.to_sentence object.errors.full_messages.to_sentence
end end
......
...@@ -3,32 +3,30 @@ ...@@ -3,32 +3,30 @@
module IncidentManagement module IncidentManagement
class CreateIssueService < BaseService class CreateIssueService < BaseService
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
include IncidentManagement::Settings
def initialize(project, params) def initialize(project, params)
super(project, User.alert_bot, params) super(project, User.alert_bot, params)
end end
def execute def execute
return error_with('setting disabled') unless incident_management_setting.create_issue? return error('setting disabled') unless incident_management_setting.create_issue?
return error_with('invalid alert') unless alert.valid? return error('invalid alert') unless alert.valid?
issue = create_issue result = create_incident
return error_with(issue_errors(issue)) unless issue.valid? return error(result.message, result.payload[:issue]) unless result.success?
success(issue: issue) result
end end
private private
def create_issue def create_incident
label_result = find_or_create_incident_label ::IncidentManagement::Incidents::CreateService.new(
Issues::CreateService.new(
project, project,
current_user, current_user,
title: issue_title, title: issue_title,
description: issue_description, description: issue_description
label_ids: [label_result.payload[:label].id]
).execute ).execute
end end
...@@ -46,10 +44,6 @@ module IncidentManagement ...@@ -46,10 +44,6 @@ module IncidentManagement
].compact.join(horizontal_line) ].compact.join(horizontal_line)
end end
def find_or_create_incident_label
IncidentManagement::CreateIncidentLabelService.new(project, current_user).execute
end
def alert_summary def alert_summary
alert.issue_summary_markdown alert.issue_summary_markdown
end end
...@@ -68,21 +62,10 @@ module IncidentManagement ...@@ -68,21 +62,10 @@ module IncidentManagement
incident_management_setting.issue_template_content incident_management_setting.issue_template_content
end end
def incident_management_setting def error(message, issue = nil)
strong_memoize(:incident_management_setting) do
project.incident_management_setting ||
project.build_incident_management_setting
end
end
def issue_errors(issue)
issue.errors.full_messages.to_sentence
end
def error_with(message)
log_error(%{Cannot create incident issue for "#{project.full_name}": #{message}}) log_error(%{Cannot create incident issue for "#{project.full_name}": #{message}})
error(message) ServiceResponse.error(payload: { issue: issue }, message: message)
end end
end end
end end
# frozen_string_literal: true
module IncidentManagement
module Incidents
class CreateService < BaseService
def initialize(project, current_user, title:, description:)
super(project, current_user)
@title = title
@description = description
end
def execute
issue = Issues::CreateService.new(
project,
current_user,
title: title,
description: description,
label_ids: [find_or_create_incident_label.id]
).execute
return error(issue.errors.full_messages.to_sentence, issue) unless issue.valid?
success(issue)
end
private
attr_reader :title, :description
def find_or_create_incident_label
IncidentManagement::CreateIncidentLabelService
.new(project, current_user)
.execute
.payload[:label]
end
def success(issue)
ServiceResponse.success(payload: { issue: issue })
end
def error(message, issue = nil)
ServiceResponse.error(payload: { issue: issue }, message: message)
end
end
end
end
...@@ -12,25 +12,19 @@ module IncidentManagement ...@@ -12,25 +12,19 @@ module IncidentManagement
def execute def execute
return forbidden unless webhook_available? return forbidden unless webhook_available?
issue = create_issue create_incident
return error(issue.errors.full_messages.to_sentence, issue) unless issue.valid?
success(issue)
end end
private private
alias_method :incident_payload, :params alias_method :incident_payload, :params
def create_issue def create_incident
label_result = find_or_create_incident_label ::IncidentManagement::Incidents::CreateService.new(
Issues::CreateService.new(
project, project,
current_user, current_user,
title: issue_title, title: issue_title,
description: issue_description, description: issue_description
label_ids: [label_result.payload[:label].id]
).execute ).execute
end end
...@@ -42,10 +36,6 @@ module IncidentManagement ...@@ -42,10 +36,6 @@ module IncidentManagement
ServiceResponse.error(message: 'Forbidden', http_status: :forbidden) ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
end end
def find_or_create_incident_label
::IncidentManagement::CreateIncidentLabelService.new(project, current_user).execute
end
def issue_title def issue_title
incident_payload['title'] incident_payload['title']
end end
...@@ -53,14 +43,6 @@ module IncidentManagement ...@@ -53,14 +43,6 @@ module IncidentManagement
def issue_description def issue_description
Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription.new(incident_payload).to_s Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription.new(incident_payload).to_s
end end
def success(issue)
ServiceResponse.success(payload: { issue: issue })
end
def error(message, issue = nil)
ServiceResponse.error(payload: { issue: issue }, message: message)
end
end end
end end
end end
...@@ -16,9 +16,10 @@ module IncidentManagement ...@@ -16,9 +16,10 @@ module IncidentManagement
alert = find_alert(alert_id) alert = find_alert(alert_id)
return unless alert return unless alert
new_issue = create_issue_for(alert) result = create_issue_for(alert)
return unless new_issue&.persisted? return unless result.success?
new_issue = result.payload[:issue]
link_issue_with_alert(alert, new_issue.id) link_issue_with_alert(alert, new_issue.id)
end end
...@@ -36,7 +37,6 @@ module IncidentManagement ...@@ -36,7 +37,6 @@ module IncidentManagement
IncidentManagement::CreateIssueService IncidentManagement::CreateIssueService
.new(alert.project, parsed_payload(alert)) .new(alert.project, parsed_payload(alert))
.execute .execute
.dig(:issue)
end end
def link_issue_with_alert(alert, issue_id) def link_issue_with_alert(alert, issue_id)
......
...@@ -88,7 +88,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -88,7 +88,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
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'
it_behaves_like 'create alert issue sets issue labels'
end end
context 'when the alert is generic' do context 'when the alert is generic' do
...@@ -97,7 +96,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do ...@@ -97,7 +96,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
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'
it_behaves_like 'create alert issue sets issue labels'
end end
context 'when issue cannot be created' do context 'when issue cannot be created' do
......
...@@ -25,10 +25,10 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -25,10 +25,10 @@ RSpec.describe IncidentManagement::CreateIssueService do
create(:project_incident_management_setting, project: project) create(:project_incident_management_setting, project: project)
end end
subject { service.execute } subject(:execute) { service.execute }
context 'when create_issue enabled' do context 'when create_issue enabled' do
let(:issue) { subject[:issue] } let(:issue) { execute.payload[:issue] }
before do before do
setting.update!(create_issue: true) setting.update!(create_issue: true)
...@@ -36,7 +36,7 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -36,7 +36,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
context 'without issue_template_content' do context 'without issue_template_content' do
it 'creates an issue with alert summary only' do it 'creates an issue with alert summary only' do
expect(subject).to include(status: :success) expect(execute).to be_success
expect(issue.author).to eq(user) expect(issue.author).to eq(user)
expect(issue.title).to eq(alert_title) expect(issue.title).to eq(alert_title)
...@@ -61,7 +61,8 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -61,7 +61,8 @@ RSpec.describe IncidentManagement::CreateIssueService do
.to receive(:log_error) .to receive(:log_error)
.with(error_message(issue_error)) .with(error_message(issue_error))
expect(subject).to include(status: :error, message: issue_error) expect(execute).to be_error
expect(execute.message).to eq(issue_error)
end end
end end
...@@ -70,7 +71,7 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -70,7 +71,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
let(:template_content) { 'some content' } let(:template_content) { 'some content' }
it 'creates an issue appending issue template' do it 'creates an issue appending issue template' do
expect(subject).to include(status: :success) expect(execute).to be_success
expect(issue.description).to include(alert_presenter.issue_summary_markdown) expect(issue.description).to include(alert_presenter.issue_summary_markdown)
expect(separator_count(issue.description)).to eq(1) expect(separator_count(issue.description)).to eq(1)
...@@ -95,7 +96,7 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -95,7 +96,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
end end
it 'creates an issue interpreting quick actions' do it 'creates an issue interpreting quick actions' do
expect(subject).to include(status: :success) expect(execute).to be_success
expect(issue.description).to include(plain_text) expect(issue.description).to include(plain_text)
expect(issue.due_date).to be_present expect(issue.due_date).to be_present
...@@ -128,7 +129,7 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -128,7 +129,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
end end
it 'includes both templates' do it 'includes both templates' do
expect(subject).to include(status: :success) expect(execute).to be_success
expect(issue.description).to include(alert_presenter.issue_summary_markdown) expect(issue.description).to include(alert_presenter.issue_summary_markdown)
expect(issue.description).to include(template_content) expect(issue.description).to include(template_content)
...@@ -162,7 +163,7 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -162,7 +163,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
it 'creates an issue' do it 'creates an issue' do
query_title = "#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold}" query_title = "#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold}"
expect(subject).to include(status: :success) expect(execute).to be_success
expect(issue.author).to eq(user) expect(issue.author).to eq(user)
expect(issue.title).to eq(alert_presenter.full_title) expect(issue.title).to eq(alert_presenter.full_title)
...@@ -181,7 +182,8 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -181,7 +182,8 @@ RSpec.describe IncidentManagement::CreateIssueService do
.to receive(:log_error) .to receive(:log_error)
.with(error_message('invalid alert')) .with(error_message('invalid alert'))
expect(subject).to eq(status: :error, message: 'invalid alert') expect(execute).to be_error
expect(execute.message).to eq('invalid alert')
end end
end end
...@@ -197,10 +199,6 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -197,10 +199,6 @@ RSpec.describe IncidentManagement::CreateIssueService do
it_behaves_like 'invalid alert' it_behaves_like 'invalid alert'
end end
end end
describe "label `incident`" do
it_behaves_like 'create alert issue sets issue labels'
end
end end
context 'when create_issue disabled' do context 'when create_issue disabled' do
...@@ -213,7 +211,8 @@ RSpec.describe IncidentManagement::CreateIssueService do ...@@ -213,7 +211,8 @@ RSpec.describe IncidentManagement::CreateIssueService do
.to receive(:log_error) .to receive(:log_error)
.with(error_message('setting disabled')) .with(error_message('setting disabled'))
expect(subject).to eq(status: :error, message: 'setting disabled') expect(execute).to be_error
expect(execute.message).to eq('setting disabled')
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IncidentManagement::Incidents::CreateService do
let_it_be(:project) { create(:project) }
let_it_be(:user) { User.alert_bot }
let(:description) { 'Incident description' }
describe '#execute' do
subject(:create_incident) { described_class.new(project, user, title: title, description: description).execute }
context 'when incident has title and description' do
let(:title) { 'Incident title' }
let(:new_issue) { Issue.last! }
let(:label_title) { IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title] }
it 'responds with success' do
expect(create_incident).to be_success
end
it 'creates an incident issue' do
expect { create_incident }.to change(Issue, :count).by(1)
end
it 'created issue has correct attributes' do
create_incident
expect(new_issue.title).to eq(title)
expect(new_issue.description).to eq(description)
expect(new_issue.author).to eq(user)
expect(new_issue.labels.map(&:title)).to eq([label_title])
end
context 'when incident label does not exists' do
it 'creates incident label' do
expect { create_incident }.to change { project.labels.where(title: label_title).count }.by(1)
end
end
context 'when incident label already exists' do
let!(:label) { create(:label, project: project, title: label_title) }
it 'does not create new labels' do
expect { create_incident }.not_to change(Label, :count)
end
end
end
context 'when incident has no title' do
let(:title) { '' }
it 'does not create an issue' do
expect { create_incident }.not_to change(Issue, :count)
end
it 'responds with errors' do
expect(create_incident).to be_error
expect(create_incident.message).to eq("Title can't be blank")
end
it 'result payload contains an Issue object' do
expect(create_incident.payload[:issue]).to be_kind_of(Issue)
end
end
end
end
# frozen_string_literal: true
RSpec.shared_examples 'create alert issue sets issue labels' do
let(:title) { IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title] }
let!(:label) { create(:label, project: project, title: title) }
let(:label_service) { instance_double(IncidentManagement::CreateIncidentLabelService, execute: label_service_response) }
before do
allow(IncidentManagement::CreateIncidentLabelService).to receive(:new).with(project, user).and_return(label_service)
end
context 'when create incident label responds with success' do
let(:label_service_response) { ServiceResponse.success(payload: { label: label }) }
it 'adds label to issue' do
expect(issue.labels).to eq([label])
end
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