Commit a56f42c6 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 2c284f46 e31a07b8
---
title: Add is_removed column on Oncall Participant model
merge_request: 54779
author:
type: added
# frozen_string_literal: true
class AddIsRemovedToOncallParticipant < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :incident_management_oncall_participants, :is_removed, :boolean, default: false, null: false
end
end
def down
with_lock_retries do
remove_column :incident_management_oncall_participants, :is_removed
end
end
end
# frozen_string_literal: true
class AddIsRemovedIndexToOncallParticipant < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
EXISTING_INDEX_NAME = 'index_inc_mgmnt_oncall_participants_on_oncall_rotation_id'
NEW_INDEX_NAME = 'index_inc_mgmnt_oncall_pcpnt_on_oncall_rotation_id_is_removed'
def up
add_concurrent_index :incident_management_oncall_participants, [:oncall_rotation_id, :is_removed], name: NEW_INDEX_NAME
remove_concurrent_index_by_name(:incident_management_oncall_participants, EXISTING_INDEX_NAME)
end
def down
add_concurrent_index :incident_management_oncall_participants, :oncall_rotation_id, name: EXISTING_INDEX_NAME
remove_concurrent_index_by_name(:incident_management_oncall_participants, NEW_INDEX_NAME)
end
end
44b0e2b3e32e45fb557a5c9c41f69de94a5d41bad43267f00090b9c527b2b1e1
\ No newline at end of file
88bbd8cbc4398c3c05a834c4d8d36ee55aca9d698da3cf3c1df0afe916bc051f
\ No newline at end of file
......@@ -13235,7 +13235,8 @@ CREATE TABLE incident_management_oncall_participants (
oncall_rotation_id bigint NOT NULL,
user_id bigint NOT NULL,
color_palette smallint NOT NULL,
color_weight smallint NOT NULL
color_weight smallint NOT NULL,
is_removed boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE incident_management_oncall_participants_id_seq
......@@ -22450,12 +22451,12 @@ CREATE INDEX index_import_failures_on_project_id_not_null ON import_failures USI
CREATE INDEX index_imported_projects_on_import_type_creator_id_created_at ON projects USING btree (import_type, creator_id, created_at) WHERE (import_type IS NOT NULL);
CREATE INDEX index_inc_mgmnt_oncall_participants_on_oncall_rotation_id ON incident_management_oncall_participants USING btree (oncall_rotation_id);
CREATE INDEX index_inc_mgmnt_oncall_participants_on_oncall_user_id ON incident_management_oncall_participants USING btree (user_id);
CREATE UNIQUE INDEX index_inc_mgmnt_oncall_participants_on_user_id_and_rotation_id ON incident_management_oncall_participants USING btree (user_id, oncall_rotation_id);
CREATE INDEX index_inc_mgmnt_oncall_pcpnt_on_oncall_rotation_id_is_removed ON incident_management_oncall_participants USING btree (oncall_rotation_id, is_removed);
CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_id ON incident_management_oncall_rotations USING btree (oncall_schedule_id, id);
CREATE UNIQUE INDEX index_inc_mgmnt_oncall_rotations_on_oncall_schedule_id_and_name ON incident_management_oncall_rotations USING btree (oncall_schedule_id, name);
......@@ -77,6 +77,11 @@ depending on your final package recipe. For example:
CONAN_LOGIN_USERNAME=<gitlab-username> CONAN_PASSWORD=<personal_access_token> conan upload MyPackage/1.0.0@foo+bar+my-proj/channel --all --remote=gitlab
```
### Composer
You can't publish a Composer package outside of its project. An [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/250633)
exists to implement functionality that allows you to publish such packages to other projects.
### All other package types
[All package types supported by GitLab](../index.md) can be published in
......
......@@ -139,7 +139,7 @@ export default {
<gl-tab data-testid="licensesTab">
<template #title>
<span data-testid="licensesTabTitle">{{ s__('Licenses|Detected in Project') }}</span>
<gl-badge>{{ licenseCount }}</gl-badge>
<gl-badge size="sm" class="gl-tab-counter-badge">{{ licenseCount }}</gl-badge>
</template>
<detected-licenses-table />
......@@ -150,7 +150,7 @@ export default {
<span data-qa-selector="policies_tab" data-testid="policiesTabTitle">{{
s__('Licenses|Policies')
}}</span>
<gl-badge>{{ policyCount }}</gl-badge>
<gl-badge size="sm" class="gl-tab-counter-badge">{{ policyCount }}</gl-badge>
</template>
<license-management />
......
......@@ -20,5 +20,12 @@ module IncidentManagement
validates :user, presence: true, uniqueness: { scope: :oncall_rotation_id }
delegate :project, to: :rotation, allow_nil: true
scope :not_removed, -> { where(is_removed: false) }
scope :removed, -> { where(is_removed: true) }
def mark_as_removed
update_column(:is_removed, true)
end
end
end
......@@ -30,8 +30,9 @@ module IncidentManagement
NAME_LENGTH = 200
belongs_to :schedule, class_name: 'OncallSchedule', inverse_of: 'rotations', foreign_key: 'oncall_schedule_id'
# Note! If changing the order of participants, also change the :with_shift_generation_associations scope.
has_many :participants, -> { order(id: :asc) }, class_name: 'OncallParticipant', inverse_of: :rotation
# Note! If changing the order of participants, also change the :with_shift_generation_associations scope.
has_many :active_participants, -> { not_removed.order(id: :asc) }, class_name: 'OncallParticipant', inverse_of: :rotation
has_many :users, through: :participants
has_many :shifts, class_name: 'OncallShift', inverse_of: :rotation, foreign_key: :rotation_id
......@@ -49,9 +50,9 @@ module IncidentManagement
scope :in_progress, -> { where('starts_at < :time AND (ends_at > :time OR ends_at IS NULL)', time: Time.current) }
scope :except_ids, -> (ids) { where.not(id: ids) }
scope :with_shift_generation_associations, -> do
joins(:participants, :schedule)
joins(:active_participants, :schedule)
.distinct
.includes(:participants, :schedule)
.includes(:active_participants, :schedule)
.order(:id, 'incident_management_oncall_participants.id ASC')
end
......
---
title: Counter badges are misaligned in License Compliance tabs
merge_request: 55517
author:
type: fixed
......@@ -19,7 +19,7 @@ module IncidentManagement
ends_at = limit_end_time(apply_timezone(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 timeframe. We want to begin generating shifts
......@@ -50,7 +50,7 @@ module IncidentManagement
return if timestamp < rotation_starts_at
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)
shift_cycle_starts_at = shift_cycle_start_time(elapsed_shift_cycle_count)
......@@ -175,7 +175,7 @@ module IncidentManagement
def participants
strong_memoize(:participants) do
rotation.participants
rotation.active_participants
end
end
......
......@@ -12,5 +12,9 @@ FactoryBot.define do
participant.rotation.project.add_developer(participant.user)
end
end
trait :removed do
is_removed { true }
end
end
end
......@@ -177,7 +177,7 @@ describe('Project Licenses', () => {
it('renders a "Detected in project" tab and a "Policies" tab', () => {
expect(wrapper.find(GlTabs).exists()).toBe(true);
expect(wrapper.find(GlTab).exists()).toBe(true);
expect(wrapper.findAll(GlTab)).toHaveLength(2);
expect(wrapper.findAllComponents(GlTab)).toHaveLength(2);
});
it('it renders the "Detected in project" table', () => {
......@@ -279,11 +279,21 @@ describe('Project Licenses', () => {
);
it('it renders the correct count in "Detected in project" tab', () => {
expect(wrapper.findAll(GlBadge).at(0).text()).toBe(pageInfo.total.toString());
expect(wrapper.findAllComponents(GlBadge).at(0).text()).toBe(pageInfo.total.toString());
});
it('it renders the correct count in "Policies" tab', () => {
expect(wrapper.findAll(GlBadge).at(1).text()).toBe(managedLicenses.length.toString());
expect(wrapper.findAllComponents(GlBadge).at(1).text()).toBe(
managedLicenses.length.toString(),
);
});
it('it renders the correct type of badge styling', () => {
const badges = [
wrapper.findAllComponents(GlBadge).at(0),
wrapper.findAllComponents(GlBadge).at(1),
];
badges.forEach((badge) => expect(badge.classes()).toContain('gl-tab-counter-badge'));
});
});
......
......@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe IncidentManagement::OncallParticipant do
let_it_be(:rotation) { create(:incident_management_oncall_rotation) }
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) }
......@@ -38,6 +39,30 @@ RSpec.describe IncidentManagement::OncallParticipant do
end
end
describe 'scopes' do
let_it_be(:removed_participant) { 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_participant) }
end
end
describe '#mark_as_removed' do
subject { participant.mark_as_removed }
it 'updates is_removed to true' do
expect { subject }.to change { participant.reload.is_removed }.to(true)
end
end
private
def remove_user_from_project(user, project)
......
......@@ -8,8 +8,19 @@ RSpec.describe IncidentManagement::OncallRotation do
describe '.associations' do
it { is_expected.to belong_to(:schedule).class_name('OncallSchedule').inverse_of(:rotations) }
it { is_expected.to have_many(:participants).order(id: :asc).class_name('OncallParticipant').inverse_of(:rotation) }
it { is_expected.to have_many(:active_participants).order(id: :asc).class_name('OncallParticipant').inverse_of(:rotation) }
it { is_expected.to have_many(:users).through(:participants) }
it { is_expected.to have_many(:shifts).class_name('OncallShift').inverse_of(:rotation) }
describe '.active_participants' do
let(:rotation) { create(:incident_management_oncall_rotation, schedule: schedule) }
let(:participant) { create(:incident_management_oncall_participant, rotation: rotation) }
let(:removed_participant) { create(:incident_management_oncall_participant, :removed, rotation: rotation) }
subject { rotation.active_participants }
it { is_expected.to contain_exactly(participant) }
end
end
describe '.validations' do
......
......@@ -582,6 +582,26 @@ RSpec.describe QuickActions::InterpretService do
end
end
context 'parent_epic command' do
let(:epic) { create(:epic, group: group) }
let(:epic2) { create(:epic, group: group) }
let(:referenced_epic) { create(:epic, group: epic.group) }
before do
group.add_developer(current_user)
stub_licensed_features(epics: true, subepics: true)
end
it_behaves_like 'adds quick action parameter', :quick_action_assign_to_parent_epic, :parent_epic
context 'when target epic is not persisted yet' do
let(:epic) { build(:epic, group: group) }
let(:referenced_epic) { epic2 }
it_behaves_like 'adds quick action parameter', :quick_action_assign_to_parent_epic, :parent_epic
end
end
context 'child_epic command' do
let(:subgroup) { create(:group, parent: group) }
let(:another_group) { create(:group) }
......@@ -1259,7 +1279,6 @@ RSpec.describe QuickActions::InterpretService do
context 'parent_epic command' do
let(:referenced_epic) { epic2 }
it_behaves_like 'adds quick action parameter', :quick_action_assign_to_parent_epic, :parent_epic
it_behaves_like 'returns execution messages', :parent
context 'when epic is already a parent epic' do
......@@ -1288,13 +1307,6 @@ RSpec.describe QuickActions::InterpretService do
it_behaves_like 'target epic does not exist', :parent
end
context 'when target epic is not persisted yet' do
let(:epic) { build(:epic, group: group) }
let(:referenced_epic) { epic2 }
it_behaves_like 'adds quick action parameter', :quick_action_assign_to_parent_epic, :parent_epic
end
context 'when user has no permission to read epic' do
let(:content) { "/parent_epic #{epic2&.to_reference(epic)}" }
......
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