Commit a53c0d24 authored by Peter Leitzen's avatar Peter Leitzen

Add incident label for manually created incident issues

Previously, we've created and applied the "incident" label
only when calling the `IncidentManagment::Incidents::CreateService`
explicitly. After we've added the issue type "incident" incidents
can be created/updated using the `Issues::Create/UpdateService`.
Those services did not apply this label.

This commit moves the code which applies the label close to the
`Issue` services to make sure we are labeling all incidents with
the "incident" label correctly.
parent 096ed8fa
...@@ -18,7 +18,6 @@ module IncidentManagement ...@@ -18,7 +18,6 @@ module IncidentManagement
current_user, current_user,
title: title, title: title,
description: description, description: description,
label_ids: [find_or_create_incident_label.id],
issue_type: ISSUE_TYPE issue_type: ISSUE_TYPE
).execute ).execute
...@@ -31,13 +30,6 @@ module IncidentManagement ...@@ -31,13 +30,6 @@ module IncidentManagement
attr_reader :title, :description attr_reader :title, :description
def find_or_create_incident_label
IncidentManagement::CreateIncidentLabelService
.new(project, current_user)
.execute
.payload[:label]
end
def success(issue) def success(issue)
ServiceResponse.success(payload: { issue: issue }) ServiceResponse.success(payload: { issue: issue })
end end
......
...@@ -61,6 +61,22 @@ module Issues ...@@ -61,6 +61,22 @@ module Issues
Milestones::IssuesCountService.new(milestone).delete_cache Milestones::IssuesCountService.new(milestone).delete_cache
end end
# Applies label "incident" (creates it if missing) to incident issues.
# Please use in "after" hooks only to ensure we are not appyling
# labels prematurely.
def add_incident_label(issue)
return unless issue.incident?
label = ::IncidentManagement::CreateIncidentLabelService
.new(project, current_user)
.execute
.payload[:label]
return if issue.label_ids.include?(label.id)
issue.labels << label
end
end end
end end
......
...@@ -25,12 +25,13 @@ module Issues ...@@ -25,12 +25,13 @@ module Issues
end end
end end
def after_create(issuable) def after_create(issue)
todo_service.new_issue(issuable, current_user) add_incident_label(issue)
todo_service.new_issue(issue, current_user)
user_agent_detail_service.create user_agent_detail_service.create
resolve_discussions_with_issue(issuable) resolve_discussions_with_issue(issue)
delete_milestone_total_issue_counter_cache(issuable.milestone) delete_milestone_total_issue_counter_cache(issue.milestone)
track_incident_action(current_user, issuable, :incident_created) track_incident_action(current_user, issue, :incident_created)
super super
end end
......
...@@ -22,6 +22,7 @@ module Issues ...@@ -22,6 +22,7 @@ module Issues
end end
def after_update(issue) def after_update(issue)
add_incident_label(issue)
IssuesChannel.broadcast_to(issue, event: 'updated') if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project) IssuesChannel.broadcast_to(issue, event: 'updated') if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project)
end end
......
---
title: Add incident label for manually created incident issues
merge_request: 41598
author:
type: fixed
...@@ -18,6 +18,13 @@ FactoryBot.define do ...@@ -18,6 +18,13 @@ FactoryBot.define do
title { "#{prefix}::#{generate(:label_title)}" } title { "#{prefix}::#{generate(:label_title)}" }
end end
trait :incident do
properties = IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES
title { properties.fetch(:title) }
description { properties.fetch(:description) }
color { properties.fetch(:color) }
end
factory :label, traits: [:base_label], class: 'ProjectLabel' do factory :label, traits: [:base_label], class: 'ProjectLabel' do
project project
......
...@@ -51,12 +51,11 @@ FactoryBot.define do ...@@ -51,12 +51,11 @@ FactoryBot.define do
create(:protected_branch, name: 'main', project: projects[0]) create(:protected_branch, name: 'main', project: projects[0])
# Incident Labeled Issues # Incident Labeled Issues
incident_label_attrs = IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES incident_label = create(:label, :incident, project: projects[0])
incident_label = create(:label, project: projects[0], **incident_label_attrs)
create(:labeled_issue, project: projects[0], labels: [incident_label]) create(:labeled_issue, project: projects[0], labels: [incident_label])
incident_group = create(:group) incident_group = create(:group)
incident_label_scoped_to_project = create(:label, project: projects[1], **incident_label_attrs) incident_label_scoped_to_project = create(:label, :incident, project: projects[1])
incident_label_scoped_to_group = create(:group_label, group: incident_group, **incident_label_attrs) incident_label_scoped_to_group = create(:group_label, :incident, group: incident_group)
create(:labeled_issue, project: projects[1], labels: [incident_label_scoped_to_project]) create(:labeled_issue, project: projects[1], labels: [incident_label_scoped_to_project])
create(:labeled_issue, project: projects[1], labels: [incident_label_scoped_to_group]) create(:labeled_issue, project: projects[1], labels: [incident_label_scoped_to_group])
......
...@@ -10,9 +10,10 @@ RSpec.describe IncidentManagement::CreateIncidentLabelService do ...@@ -10,9 +10,10 @@ RSpec.describe IncidentManagement::CreateIncidentLabelService do
subject(:execute) { service.execute } subject(:execute) { service.execute }
describe 'execute' do describe 'execute' do
let(:title) { described_class::LABEL_PROPERTIES[:title] } let(:incident_label_attributes) { attributes_for(:label, :incident) }
let(:color) { described_class::LABEL_PROPERTIES[:color] } let(:title) { incident_label_attributes[:title] }
let(:description) { described_class::LABEL_PROPERTIES[:description] } let(:color) { incident_label_attributes[:color] }
let(:description) { incident_label_attributes[:description] }
shared_examples 'existing label' do shared_examples 'existing label' do
it 'returns the existing label' do it 'returns the existing label' do
......
...@@ -13,7 +13,7 @@ RSpec.describe IncidentManagement::Incidents::CreateService do ...@@ -13,7 +13,7 @@ RSpec.describe IncidentManagement::Incidents::CreateService do
context 'when incident has title and description' do context 'when incident has title and description' do
let(:title) { 'Incident title' } let(:title) { 'Incident title' }
let(:new_issue) { Issue.last! } let(:new_issue) { Issue.last! }
let(:label_title) { IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title] } let(:label_title) { attributes_for(:label, :incident)[:title] }
it 'responds with success' do it 'responds with success' do
expect(create_incident).to be_success expect(create_incident).to be_success
...@@ -23,15 +23,20 @@ RSpec.describe IncidentManagement::Incidents::CreateService do ...@@ -23,15 +23,20 @@ RSpec.describe IncidentManagement::Incidents::CreateService do
expect { create_incident }.to change(Issue, :count).by(1) expect { create_incident }.to change(Issue, :count).by(1)
end end
it 'created issue has correct attributes' do it 'created issue has correct attributes', :aggregate_failures do
create_incident create_incident
aggregate_failures do
expect(new_issue.title).to eq(title) expect(new_issue.title).to eq(title)
expect(new_issue.description).to eq(description) expect(new_issue.description).to eq(description)
expect(new_issue.author).to eq(user) expect(new_issue.author).to eq(user)
expect(new_issue.issue_type).to eq('incident') end
expect(new_issue.labels.map(&:title)).to eq([label_title])
it_behaves_like 'incident issue' do
before do
create_incident
end end
let(:issue) { new_issue }
end end
context 'when incident label does not exists' do context 'when incident label does not exists' do
......
...@@ -49,16 +49,35 @@ RSpec.describe Issues::CreateService do ...@@ -49,16 +49,35 @@ RSpec.describe Issues::CreateService do
end end
end end
it_behaves_like 'not an incident issue'
context 'issue is incident type' do context 'issue is incident type' do
before do before do
opts[:issue_type] = 'incident' opts.merge!(issue_type: 'incident')
end end
let(:current_user) { user } let(:current_user) { user }
let(:incident_label_attributes) { attributes_for(:label, :incident) }
subject { issue } subject { issue }
it_behaves_like 'incident issue'
it_behaves_like 'an incident management tracked event', :incident_management_incident_created it_behaves_like 'an incident management tracked event', :incident_management_incident_created
it 'does create an incident label' do
expect { subject }
.to change { Label.where(incident_label_attributes).count }.by(1)
end
context 'when invalid' do
before do
opts.merge!(title: '')
end
it 'does not create an incident label prematurely' do
expect { subject }.not_to change(Label, :count)
end
end
end end
it 'refreshes the number of open issues', :use_clean_rails_memory_store_caching do it 'refreshes the number of open issues', :use_clean_rails_memory_store_caching do
......
...@@ -78,6 +78,12 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -78,6 +78,12 @@ RSpec.describe Issues::UpdateService, :mailer do
expect(issue.severity).to eq(IssuableSeverity::DEFAULT) expect(issue.severity).to eq(IssuableSeverity::DEFAULT)
end end
it_behaves_like 'not an incident issue' do
before do
update_issue(opts)
end
end
end end
context 'when issue type is incident' do context 'when issue type is incident' do
...@@ -88,6 +94,27 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -88,6 +94,27 @@ RSpec.describe Issues::UpdateService, :mailer do
expect(issue.severity).to eq('low') expect(issue.severity).to eq('low')
end end
it_behaves_like 'incident issue' do
before do
update_issue(opts)
end
end
context 'with existing incident label' do
let_it_be(:incident_label) { create(:label, :incident, project: project) }
before do
opts.delete(:label_ids) # don't override but retain existing labels
issue.labels << incident_label
end
it_behaves_like 'incident issue' do
before do
update_issue(opts)
end
end
end
end end
it 'refreshes the number of open issues when the issue is made confidential', :use_clean_rails_memory_store_caching do it 'refreshes the number of open issues when the issue is made confidential', :use_clean_rails_memory_store_caching do
...@@ -113,15 +140,37 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -113,15 +140,37 @@ RSpec.describe Issues::UpdateService, :mailer do
end end
context 'issue in incident type' do context 'issue in incident type' do
let(:current_user) { user }
let(:incident_label_attributes) { attributes_for(:label, :incident) }
before do before do
opts[:issue_type] = 'incident' opts.merge!(issue_type: 'incident', confidential: true)
end end
let(:current_user) { user } subject { update_issue(opts) }
subject { update_issue(confidential: true) }
it_behaves_like 'an incident management tracked event', :incident_management_incident_change_confidential it_behaves_like 'an incident management tracked event', :incident_management_incident_change_confidential
it_behaves_like 'incident issue' do
before do
subject
end
end
it 'does create an incident label' do
expect { subject }
.to change { Label.where(incident_label_attributes).count }.by(1)
end
context 'when invalid' do
before do
opts.merge!(title: '')
end
it 'does not create an incident label prematurely' do
expect { subject }.not_to change(Label, :count)
end
end
end end
it 'updates open issue counter for assignees when issue is reassigned' do it 'updates open issue counter for assignees when issue is reassigned' do
......
# frozen_string_literal: true
# This shared_example requires the following variables:
# - issue (required)
#
# Usage:
#
# it_behaves_like 'incident issue' do
# let(:issue) { ... }
# end
#
# include_examples 'incident issue'
RSpec.shared_examples 'incident issue' do
let(:label_properties) { attributes_for(:label, :incident) }
it 'has incident as issue type' do
expect(issue.issue_type).to eq('incident')
end
it 'has exactly one incident label' do
expect(issue.labels).to be_one do |label|
label.slice(*label_properties.keys).symbolize_keys == label_properties
end
end
end
# This shared_example requires the following variables:
# - issue (required)
#
# Usage:
#
# it_behaves_like 'not an incident issue' do
# let(:issue) { ... }
# end
#
# include_examples 'not an incident issue'
RSpec.shared_examples 'not an incident issue' do
let(:label_properties) { attributes_for(:label, :incident) }
it 'has not incident as issue type' do
expect(issue.issue_type).not_to eq('incident')
end
it 'has not an incident label' do
expect(issue.labels).not_to include(have_attributes(label_properties))
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