Commit 643a821b authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Brandon Labuschagne

Introduce multiple oncall schedules feature

Changelog: added
EE: true
parent 50eee8e0
---
name: multiple_oncall_schedules
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59829
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328474
milestone: '13.11'
type: development
group: group::monitor
default_enabled: false
......@@ -11,7 +11,6 @@ import {
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { escalationPolicyUrl } from '../constants';
import getOncallSchedulesWithRotationsQuery from '../graphql/queries/get_oncall_schedules.query.graphql';
import AddScheduleModal from './add_edit_schedule_modal.vue';
......@@ -32,13 +31,7 @@ export const i18n = {
successNotification: {
title: s__('OnCallSchedules|Try adding a rotation'),
description: s__(
'OnCallSchedules|Your schedule has been successfully created. To add individual users to this schedule, use the add a rotation button.',
),
descriptionSingle: s__(
'OnCallSchedules|To create an escalation policy using this schedule, visit the %{linkStart}escalation policy%{linkEnd} page.',
),
descriptionMulti: s__(
'OnCallSchedules|To create an escalation policy that defines which schedule is used when, visit the %{linkStart}escalation policy%{linkEnd} page.',
'OnCallSchedules|Your schedule has been successfully created. To add individual users to this schedule, use the add a rotation button. To create an escalation policy that defines which schedule is used when, visit the %{linkStart}escalation policy%{linkEnd} page.',
),
},
};
......@@ -61,7 +54,6 @@ export default {
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
},
mixins: [glFeatureFlagMixin()],
inject: ['emptyOncallSchedulesSvgPath', 'projectPath'],
data() {
return {
......@@ -78,11 +70,7 @@ export default {
};
},
update(data) {
const nodes = data.project?.incidentManagementOncallSchedules?.nodes ?? [];
if (this.glFeatures.multipleOncallSchedules) {
return nodes;
}
return nodes.length ? [nodes[nodes.length - 1]] : [];
return data.project?.incidentManagementOncallSchedules?.nodes ?? [];
},
error(error) {
Sentry.captureException(error);
......@@ -90,18 +78,6 @@ export default {
},
},
computed: {
alertMessage() {
const {
$options: {
i18n: {
successNotification: { description, descriptionMulti, descriptionSingle },
},
},
} = this;
return this.glFeatures.multipleOncallSchedules
? `${description} ${descriptionMulti}`
: `${description} ${descriptionSingle}`;
},
isLoading() {
return this.$apollo.queries.schedules.loading;
},
......@@ -120,7 +96,6 @@ export default {
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
<h2>{{ $options.i18n.title }}</h2>
<gl-button
v-if="glFeatures.multipleOncallSchedules"
v-gl-modal="$options.addScheduleModalId"
v-gl-tooltip.left.viewport.hover
:title="$options.i18n.add.tooltip"
......@@ -140,7 +115,7 @@ export default {
class="gl-my-3"
@dismiss="showSuccessNotification = false"
>
<gl-sprintf :message="alertMessage">
<gl-sprintf :message="$options.i18n.successNotification.description">
<template #link="{ content }">
<gl-link :href="$options.escalationPolicyUrl" target="_blank">
{{ content }}
......
......@@ -4,9 +4,6 @@ module Projects
module IncidentManagement
class OncallSchedulesController < Projects::ApplicationController
before_action :authorize_read_incident_management_oncall_schedule!
before_action do
push_frontend_feature_flag(:multiple_oncall_schedules, @project)
end
feature_category :incident_management
......
......@@ -9,7 +9,6 @@ RSpec.describe 'On-call Schedules', :js do
before do
stub_licensed_features(oncall_schedules: true)
stub_feature_flags(multiple_oncall_schedules: true)
project.add_maintainer(user)
sign_in(user)
......
......@@ -20,7 +20,7 @@ describe('On-call schedule wrapper', () => {
const emptyOncallSchedulesSvgPath = 'illustration/path.svg';
const projectPath = 'group/project';
function mountComponent({ loading, schedules, multipleOncallSchedules = false } = {}) {
function mountComponent({ loading, schedules } = {}) {
const $apollo = {
queries: {
schedules: {
......@@ -39,7 +39,6 @@ describe('On-call schedule wrapper', () => {
provide: {
emptyOncallSchedulesSvgPath,
projectPath,
glFeatures: { multipleOncallSchedules },
},
directives: {
GlTooltip: createMockDirective(),
......@@ -85,7 +84,6 @@ describe('On-call schedule wrapper', () => {
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findSchedules = () => wrapper.findAllComponents(OnCallSchedule);
const findAlert = () => wrapper.findComponent(GlAlert);
const findAlertDescription = () => wrapper.findComponent(GlSprintf);
const findAlertLink = () => wrapper.findComponent(GlLink);
const findModal = () => wrapper.findComponent(AddScheduleModal);
const findAddAdditionalButton = () => wrapper.findByTestId('add-additional-schedules-button');
......@@ -107,33 +105,34 @@ describe('On-call schedule wrapper', () => {
});
});
describe('Schedule created', () => {
describe('Schedules created', () => {
beforeEach(() => {
mountComponent({ loading: false, schedules: [{ name: 'monitor rotation' }] });
mountComponent({
loading: false,
schedules: [{ name: 'monitor rotation' }, { name: 'monitor rotation 2' }],
});
});
it('renders the schedule when data received ', () => {
const schedule = findSchedules().at(0);
it('renders the schedules when data received', () => {
expect(findLoader().exists()).toBe(false);
expect(findEmptyState().exists()).toBe(false);
expect(schedule.exists()).toBe(true);
expect(findSchedules()).toHaveLength(2);
});
it('shows success alert with distinct description for single schedule', async () => {
it('renders an add button with a tooltip for additional schedules', () => {
const button = findAddAdditionalButton();
expect(button.exists()).toBe(true);
const tooltip = getBinding(button.element, 'gl-tooltip');
expect(tooltip).toBeDefined();
expect(button.attributes('title')).toBe(i18n.add.tooltip);
});
it('shows success alert on new schedule creation', async () => {
await findModal().vm.$emit('scheduleCreated');
const alert = findAlert();
expect(alert.exists()).toBe(true);
expect(alert.props('title')).toBe(i18n.successNotification.title);
expect(findAlertLink().attributes('href')).toBe(escalationPolicyUrl);
expect(findAlertDescription().text()).toContain(
'To create an escalation policy using this schedule',
);
});
it('renders a newly created schedule', async () => {
const schedule = findSchedules().at(0);
await findModal().vm.$emit('scheduleCreated');
expect(schedule.exists()).toBe(true);
});
});
......@@ -154,38 +153,8 @@ describe('On-call schedule wrapper', () => {
mountComponentWithApollo();
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
const schedule = findSchedules().at(0);
const schedule = findSchedules().at(1);
expect(schedule.props('schedule')).toEqual(newlyCreatedSchedule);
});
});
describe('when multiple schedules are allowed to be shown', () => {
beforeEach(() => {
mountComponent({
loading: false,
schedules: [{ name: 'monitor rotation' }, { name: 'monitor rotation 2' }],
multipleOncallSchedules: true,
});
});
it('renders the schedules when data received ', () => {
expect(findLoader().exists()).toBe(false);
expect(findEmptyState().exists()).toBe(false);
expect(findSchedules()).toHaveLength(2);
});
it('renders an add button with a tooltip for additional schedules ', () => {
const button = findAddAdditionalButton();
expect(button.exists()).toBe(true);
const tooltip = getBinding(button.element, 'gl-tooltip');
expect(tooltip).toBeDefined();
});
it('shows success alert with distinct description for multiple schedules', async () => {
await findModal().vm.$emit('scheduleCreated');
expect(findAlertDescription().text()).toContain(
'To create an escalation policy that defines which schedule is used when',
);
});
});
});
......@@ -22920,12 +22920,6 @@ msgstr ""
msgid "OnCallSchedules|The schedule could not be updated. Please try again."
msgstr ""
msgid "OnCallSchedules|To create an escalation policy that defines which schedule is used when, visit the %{linkStart}escalation policy%{linkEnd} page."
msgstr ""
msgid "OnCallSchedules|To create an escalation policy using this schedule, visit the %{linkStart}escalation policy%{linkEnd} page."
msgstr ""
msgid "OnCallSchedules|Try adding a rotation"
msgstr ""
......@@ -22941,7 +22935,7 @@ msgstr ""
msgid "OnCallSchedules|You are currently a part of:"
msgstr ""
msgid "OnCallSchedules|Your schedule has been successfully created. To add individual users to this schedule, use the add a rotation button."
msgid "OnCallSchedules|Your schedule has been successfully created. To add individual users to this schedule, use the add a rotation button. To create an escalation policy that defines which schedule is used when, visit the %{linkStart}escalation policy%{linkEnd} page."
msgstr ""
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
......
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