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
return error_no_permissions unless allowed?
return error_issue_already_exists if alert.issue
result = create_issue
issue = result.payload[:issue]
result = create_incident
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)
SystemNoteService.new_alert_issue(alert, issue, user)
......@@ -36,30 +36,19 @@ module AlertManagement
user.can?(:create_issue, project)
end
def create_issue
label_result = find_or_create_incident_label
issue = Issues::CreateService.new(
def create_incident
::IncidentManagement::Incidents::CreateService.new(
project,
user,
title: alert_presenter.title,
description: alert_presenter.issue_description,
label_ids: [label_result.payload[:label].id]
description: alert_presenter.issue_description
).execute
return error(object_errors(issue), issue) unless issue.valid?
success(issue)
end
def associate_alert_with_issue(issue)
alert.update(issue_id: issue.id)
end
def success(issue)
ServiceResponse.success(payload: { issue: issue })
end
def error(message, issue = nil)
ServiceResponse.error(payload: { issue: issue }, message: message)
end
......@@ -78,10 +67,6 @@ module AlertManagement
end
end
def find_or_create_incident_label
IncidentManagement::CreateIncidentLabelService.new(project, user).execute
end
def object_errors(object)
object.errors.full_messages.to_sentence
end
......
......@@ -3,32 +3,30 @@
module IncidentManagement
class CreateIssueService < BaseService
include Gitlab::Utils::StrongMemoize
include IncidentManagement::Settings
def initialize(project, params)
super(project, User.alert_bot, params)
end
def execute
return error_with('setting disabled') unless incident_management_setting.create_issue?
return error_with('invalid alert') unless alert.valid?
return error('setting disabled') unless incident_management_setting.create_issue?
return error('invalid alert') unless alert.valid?
issue = create_issue
return error_with(issue_errors(issue)) unless issue.valid?
result = create_incident
return error(result.message, result.payload[:issue]) unless result.success?
success(issue: issue)
result
end
private
def create_issue
label_result = find_or_create_incident_label
Issues::CreateService.new(
def create_incident
::IncidentManagement::Incidents::CreateService.new(
project,
current_user,
title: issue_title,
description: issue_description,
label_ids: [label_result.payload[:label].id]
description: issue_description
).execute
end
......@@ -46,10 +44,6 @@ module IncidentManagement
].compact.join(horizontal_line)
end
def find_or_create_incident_label
IncidentManagement::CreateIncidentLabelService.new(project, current_user).execute
end
def alert_summary
alert.issue_summary_markdown
end
......@@ -68,21 +62,10 @@ module IncidentManagement
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)
issue.errors.full_messages.to_sentence
end
def error_with(message)
def error(message, issue = nil)
log_error(%{Cannot create incident issue for "#{project.full_name}": #{message}})
error(message)
ServiceResponse.error(payload: { issue: issue }, message: message)
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
def execute
return forbidden unless webhook_available?
issue = create_issue
return error(issue.errors.full_messages.to_sentence, issue) unless issue.valid?
success(issue)
create_incident
end
private
alias_method :incident_payload, :params
def create_issue
label_result = find_or_create_incident_label
Issues::CreateService.new(
def create_incident
::IncidentManagement::Incidents::CreateService.new(
project,
current_user,
title: issue_title,
description: issue_description,
label_ids: [label_result.payload[:label].id]
description: issue_description
).execute
end
......@@ -42,10 +36,6 @@ module IncidentManagement
ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
end
def find_or_create_incident_label
::IncidentManagement::CreateIncidentLabelService.new(project, current_user).execute
end
def issue_title
incident_payload['title']
end
......@@ -53,14 +43,6 @@ module IncidentManagement
def issue_description
Gitlab::IncidentManagement::PagerDuty::IncidentIssueDescription.new(incident_payload).to_s
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
......@@ -16,9 +16,10 @@ module IncidentManagement
alert = find_alert(alert_id)
return unless alert
new_issue = create_issue_for(alert)
return unless new_issue&.persisted?
result = create_issue_for(alert)
return unless result.success?
new_issue = result.payload[:issue]
link_issue_with_alert(alert, new_issue.id)
end
......@@ -36,7 +37,6 @@ module IncidentManagement
IncidentManagement::CreateIssueService
.new(alert.project, parsed_payload(alert))
.execute
.dig(:issue)
end
def link_issue_with_alert(alert, issue_id)
......
......@@ -88,7 +88,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
it_behaves_like 'creating an alert issue'
it_behaves_like 'setting an issue attributes'
it_behaves_like 'create alert issue sets issue labels'
end
context 'when the alert is generic' do
......@@ -97,7 +96,6 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
it_behaves_like 'creating an alert issue'
it_behaves_like 'setting an issue attributes'
it_behaves_like 'create alert issue sets issue labels'
end
context 'when issue cannot be created' do
......
......@@ -25,10 +25,10 @@ RSpec.describe IncidentManagement::CreateIssueService do
create(:project_incident_management_setting, project: project)
end
subject { service.execute }
subject(:execute) { service.execute }
context 'when create_issue enabled' do
let(:issue) { subject[:issue] }
let(:issue) { execute.payload[:issue] }
before do
setting.update!(create_issue: true)
......@@ -36,7 +36,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
context 'without issue_template_content' 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.title).to eq(alert_title)
......@@ -61,7 +61,8 @@ RSpec.describe IncidentManagement::CreateIssueService do
.to receive(:log_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
......@@ -70,7 +71,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
let(:template_content) { 'some content' }
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(separator_count(issue.description)).to eq(1)
......@@ -95,7 +96,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
end
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.due_date).to be_present
......@@ -128,7 +129,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
end
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(template_content)
......@@ -162,7 +163,7 @@ RSpec.describe IncidentManagement::CreateIssueService do
it 'creates an issue' do
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.title).to eq(alert_presenter.full_title)
......@@ -181,7 +182,8 @@ RSpec.describe IncidentManagement::CreateIssueService do
.to receive(:log_error)
.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
......@@ -197,10 +199,6 @@ RSpec.describe IncidentManagement::CreateIssueService do
it_behaves_like 'invalid alert'
end
end
describe "label `incident`" do
it_behaves_like 'create alert issue sets issue labels'
end
end
context 'when create_issue disabled' do
......@@ -213,7 +211,8 @@ RSpec.describe IncidentManagement::CreateIssueService do
.to receive(:log_error)
.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
......
# 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