Commit 2b95ed39 authored by Sean Arnold's avatar Sean Arnold

Add removed_at to oncall participants table

- Add migrations
- Add specs for new methods
- Update code to use new scopes
parent 50f16077
# frozen_string_literal: true
class AddRemovedAtToOncallParticipant < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :incident_management_oncall_participants, :removed_at, :datetime_with_timezone
end
end
def down
remove_column :incident_management_oncall_participants, :removed_at
end
end
# frozen_string_literal: true
class AddRemovedAtToOncallParticipantIndex < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :incident_management_oncall_participants, :removed_at
end
def down
remove_concurrent_index :incident_management_oncall_participants, :removed_at
end
end
44b0e2b3e32e45fb557a5c9c41f69de94a5d41bad43267f00090b9c527b2b1e1
\ No newline at end of file
88bbd8cbc4398c3c05a834c4d8d36ee55aca9d698da3cf3c1df0afe916bc051f
\ No newline at end of file
...@@ -13233,7 +13233,8 @@ CREATE TABLE incident_management_oncall_participants ( ...@@ -13233,7 +13233,8 @@ CREATE TABLE incident_management_oncall_participants (
oncall_rotation_id bigint NOT NULL, oncall_rotation_id bigint NOT NULL,
user_id bigint NOT NULL, user_id bigint NOT NULL,
color_palette smallint NOT NULL, color_palette smallint NOT NULL,
color_weight smallint NOT NULL color_weight smallint NOT NULL,
removed_at timestamp with time zone
); );
CREATE SEQUENCE incident_management_oncall_participants_id_seq CREATE SEQUENCE incident_management_oncall_participants_id_seq
...@@ -22460,6 +22461,7 @@ CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_n ...@@ -22460,6 +22461,7 @@ CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_n
CREATE INDEX index_incident_management_oncall_schedules_on_project_id ON incident_management_oncall_schedules USING btree (project_id); CREATE INDEX index_incident_management_oncall_schedules_on_project_id ON incident_management_oncall_schedules USING btree (project_id);
CREATE INDEX index_incident_management_oncall_shifts_on_participant_id ON incident_management_oncall_shifts USING btree (participant_id); CREATE INDEX index_incident_management_oncall_shifts_on_participant_id ON incident_management_oncall_shifts USING btree (participant_id);
CREATE INDEX index_incident_management_oncall_participants_on_removed_at ON incident_management_oncall_participants USING btree (removed_at);
CREATE UNIQUE INDEX index_index_statuses_on_project_id ON index_statuses USING btree (project_id); CREATE UNIQUE INDEX index_index_statuses_on_project_id ON index_statuses USING btree (project_id);
...@@ -20,5 +20,12 @@ module IncidentManagement ...@@ -20,5 +20,12 @@ module IncidentManagement
validates :user, presence: true, uniqueness: { scope: :oncall_rotation_id } validates :user, presence: true, uniqueness: { scope: :oncall_rotation_id }
delegate :project, to: :rotation, allow_nil: true delegate :project, to: :rotation, allow_nil: true
scope :not_removed, -> { where(removed_at: nil) }
scope :removed, -> { where('removed_at is NOT NULL') }
def mark_as_removed
update_column(:removed_at, Time.current)
end
end end
end end
...@@ -19,7 +19,7 @@ module IncidentManagement ...@@ -19,7 +19,7 @@ module IncidentManagement
ends_at = limit_end_time(apply_timezone(ends_at)) ends_at = limit_end_time(apply_timezone(ends_at))
return [] unless starts_at < ends_at return [] unless starts_at < ends_at
return [] unless rotation.participants.any? return [] unless participants.any?
# The first shift within the timeframe may begin before # The first shift within the timeframe may begin before
# the timeframe. We want to begin generating shifts # the timeframe. We want to begin generating shifts
...@@ -50,7 +50,7 @@ module IncidentManagement ...@@ -50,7 +50,7 @@ module IncidentManagement
return if timestamp < rotation_starts_at return if timestamp < rotation_starts_at
return if rotation_ends_at && rotation_ends_at <= timestamp return if rotation_ends_at && rotation_ends_at <= timestamp
return unless rotation.participants.any? return unless participants.any?
elapsed_shift_cycle_count = elapsed_whole_shift_cycles(timestamp) elapsed_shift_cycle_count = elapsed_whole_shift_cycles(timestamp)
shift_cycle_starts_at = shift_cycle_start_time(elapsed_shift_cycle_count) shift_cycle_starts_at = shift_cycle_start_time(elapsed_shift_cycle_count)
...@@ -175,7 +175,7 @@ module IncidentManagement ...@@ -175,7 +175,7 @@ module IncidentManagement
def participants def participants
strong_memoize(:participants) do strong_memoize(:participants) do
rotation.participants rotation.participants.not_removed
end end
end end
......
...@@ -12,5 +12,9 @@ FactoryBot.define do ...@@ -12,5 +12,9 @@ FactoryBot.define do
participant.rotation.project.add_developer(participant.user) participant.rotation.project.add_developer(participant.user)
end end
end end
trait :removed do
removed_at { Time.current }
end
end end
end end
...@@ -5,6 +5,7 @@ require 'spec_helper' ...@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe IncidentManagement::OncallParticipant do RSpec.describe IncidentManagement::OncallParticipant do
let_it_be(:rotation) { create(:incident_management_oncall_rotation) } let_it_be(:rotation) { create(:incident_management_oncall_rotation) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:participant) { create(:incident_management_oncall_participant, rotation: rotation) }
subject { build(:incident_management_oncall_participant, rotation: rotation, user: user) } subject { build(:incident_management_oncall_participant, rotation: rotation, user: user) }
...@@ -38,6 +39,34 @@ RSpec.describe IncidentManagement::OncallParticipant do ...@@ -38,6 +39,34 @@ RSpec.describe IncidentManagement::OncallParticipant do
end end
end end
describe 'scopes' do
let_it_be(:removed) { create(:incident_management_oncall_participant, :removed, rotation: rotation) }
describe '.not_removed' do
subject { described_class.not_removed }
it { is_expected.to contain_exactly(participant) }
end
describe '.removed' do
subject { described_class.removed }
it { is_expected.to contain_exactly(removed) }
end
end
describe '#mark_as_removed' do
around do |example|
freeze_time { example.run }
end
subject { participant.mark_as_removed }
it 'updates removed_at to the current time' do
expect { subject }.to change { participant.reload.removed_at }.to(Time.current)
end
end
private private
def remove_user_from_project(user, project) def remove_user_from_project(user, project)
......
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