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