Add Component to Confirm Environment Rollback
Ensure a vue.js component to confirm the re-deployment/rollback of an environment pops up when rollback/re-deploy has been clicked so the user must confirm their actions before accidentally rolling back or re-deploying an environment. Only on the `environment-table` view.
This commit is contained in:
parent
5fd1dc1562
commit
ba98e91c06
13 changed files with 392 additions and 17 deletions
|
@ -0,0 +1,108 @@
|
|||
<script>
|
||||
/**
|
||||
* Render modal to confirm rollback/redeploy.
|
||||
*/
|
||||
|
||||
import _ from 'underscore';
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
export default {
|
||||
name: 'ConfirmRollbackModal',
|
||||
|
||||
components: {
|
||||
GlModal,
|
||||
},
|
||||
|
||||
props: {
|
||||
environment: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
modalTitle() {
|
||||
const title = this.environment.isLastDeployment
|
||||
? s__('Environments|Re-deploy environment %{name}?')
|
||||
: s__('Environments|Rollback environment %{name}?');
|
||||
|
||||
return sprintf(title, {
|
||||
name: _.escape(this.environment.name),
|
||||
});
|
||||
},
|
||||
|
||||
commitShortSha() {
|
||||
const { last_deployment } = this.environment;
|
||||
return this.commitData(last_deployment, 'short_id');
|
||||
},
|
||||
|
||||
commitUrl() {
|
||||
const { last_deployment } = this.environment;
|
||||
return this.commitData(last_deployment, 'commit_path');
|
||||
},
|
||||
|
||||
commitTitle() {
|
||||
const { last_deployment } = this.environment;
|
||||
return this.commitData(last_deployment, 'title');
|
||||
},
|
||||
|
||||
modalText() {
|
||||
const linkStart = `<a class="commit-sha" href="${_.escape(this.commitUrl)}">`;
|
||||
const commitId = _.escape(this.commitShortSha);
|
||||
const linkEnd = '</a>';
|
||||
const name = _.escape(this.name);
|
||||
const body = this.environment.isLastDeployment
|
||||
? s__(
|
||||
'Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?',
|
||||
)
|
||||
: s__(
|
||||
'Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?',
|
||||
);
|
||||
return sprintf(
|
||||
body,
|
||||
{
|
||||
commitId,
|
||||
linkStart,
|
||||
linkEnd,
|
||||
name,
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
|
||||
modalActionText() {
|
||||
return this.environment.isLastDeployment
|
||||
? s__('Environments|Re-deploy')
|
||||
: s__('Environments|Rollback');
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onOk() {
|
||||
eventHub.$emit('rollbackEnvironment', this.environment);
|
||||
},
|
||||
|
||||
commitData(lastDeployment, key) {
|
||||
if (lastDeployment && lastDeployment.commit) {
|
||||
return lastDeployment.commit[key];
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-modal
|
||||
:title="modalTitle"
|
||||
modal-id="confirm-rollback-modal"
|
||||
:ok-title="modalActionText"
|
||||
ok-variant="danger"
|
||||
@ok="onOk"
|
||||
>
|
||||
<p v-html="modalText"></p>
|
||||
</gl-modal>
|
||||
</template>
|
|
@ -556,6 +556,7 @@ export default {
|
|||
|
||||
<rollback-component
|
||||
v-if="canRetry"
|
||||
:environment="model"
|
||||
:is-last-deployment="isLastDeployment"
|
||||
:retry-url="retryUrl"
|
||||
/>
|
||||
|
|
|
@ -5,29 +5,38 @@
|
|||
*
|
||||
* Makes a post request when the button is clicked.
|
||||
*/
|
||||
import { GlTooltipDirective, GlLoadingIcon, GlModalDirective, GlButton } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import ConfirmRollbackModal from './confirm_rollback_modal.vue';
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
GlLoadingIcon,
|
||||
GlButton,
|
||||
ConfirmRollbackModal,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
GlModal: GlModalDirective,
|
||||
},
|
||||
props: {
|
||||
retryUrl: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
isLastDeployment: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
environment: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
|
||||
retryUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -45,23 +54,31 @@ export default {
|
|||
|
||||
methods: {
|
||||
onClick() {
|
||||
eventHub.$emit('requestRollbackEnvironment', {
|
||||
...this.environment,
|
||||
retryUrl: this.retryUrl,
|
||||
isLastDeployment: this.isLastDeployment,
|
||||
});
|
||||
eventHub.$on('rollbackEnvironment', environment => {
|
||||
if (environment.id === this.environment.id) {
|
||||
this.isLoading = true;
|
||||
|
||||
eventHub.$emit('postAction', { endpoint: this.retryUrl });
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<button
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
v-gl-modal.confirm-rollback-modal
|
||||
variant="secondary"
|
||||
:disabled="isLoading"
|
||||
:title="title"
|
||||
type="button"
|
||||
class="btn d-none d-sm-none d-md-block"
|
||||
class="d-none d-md-block"
|
||||
@click="onClick"
|
||||
>
|
||||
<icon v-if="isLastDeployment" name="repeat" /> <icon v-else name="redo" />
|
||||
<gl-loading-icon v-if="isLoading" />
|
||||
</button>
|
||||
</gl-button>
|
||||
</template>
|
||||
|
|
|
@ -6,11 +6,13 @@ import eventHub from '../event_hub';
|
|||
import environmentsMixin from '../mixins/environments_mixin';
|
||||
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
|
||||
import StopEnvironmentModal from './stop_environment_modal.vue';
|
||||
import ConfirmRollbackModal from './confirm_rollback_modal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
emptyState,
|
||||
StopEnvironmentModal,
|
||||
ConfirmRollbackModal,
|
||||
},
|
||||
|
||||
mixins: [CIPaginationMixin, environmentsMixin],
|
||||
|
@ -87,6 +89,7 @@ export default {
|
|||
<template>
|
||||
<div :class="cssContainerClass">
|
||||
<stop-environment-modal :environment="environmentInStopModal" />
|
||||
<confirm-rollback-modal :environment="environmentInRollbackModal" />
|
||||
|
||||
<div class="top-area">
|
||||
<tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" />
|
||||
|
|
|
@ -36,6 +36,7 @@ export default {
|
|||
page: getParameterByName('page') || '1',
|
||||
requestData: {},
|
||||
environmentInStopModal: {},
|
||||
environmentInRollbackModal: {},
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -116,6 +117,10 @@ export default {
|
|||
this.environmentInStopModal = environment;
|
||||
},
|
||||
|
||||
updateRollbackModal(environment) {
|
||||
this.environmentInRollbackModal = environment;
|
||||
},
|
||||
|
||||
stopEnvironment(environment) {
|
||||
const endpoint = environment.stop_path;
|
||||
const errorMessage = s__(
|
||||
|
@ -123,6 +128,16 @@ export default {
|
|||
);
|
||||
this.postAction({ endpoint, errorMessage });
|
||||
},
|
||||
|
||||
rollbackEnvironment(environment) {
|
||||
const { retryUrl, isLastDeployment } = environment;
|
||||
const errorMessage = isLastDeployment
|
||||
? s__('Environments|An error occurred while re-deploying the environment, please try again')
|
||||
: s__(
|
||||
'Environments|An error occurred while rolling back the environment, please try again',
|
||||
);
|
||||
this.postAction({ endpoint: retryUrl, errorMessage });
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -181,11 +196,17 @@ export default {
|
|||
eventHub.$on('postAction', this.postAction);
|
||||
eventHub.$on('requestStopEnvironment', this.updateStopModal);
|
||||
eventHub.$on('stopEnvironment', this.stopEnvironment);
|
||||
|
||||
eventHub.$on('requestRollbackEnvironment', this.updateRollbackModal);
|
||||
eventHub.$on('rollbackEnvironment', this.rollbackEnvironment);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
eventHub.$off('postAction', this.postAction);
|
||||
eventHub.$off('requestStopEnvironment', this.updateStopModal);
|
||||
eventHub.$off('stopEnvironment', this.stopEnvironment);
|
||||
|
||||
eventHub.$off('requestRollbackEnvironment', this.updateRollbackModal);
|
||||
eventHub.$off('rollbackEnvironment', this.rollbackEnvironment);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -404,6 +404,7 @@ img.emoji {
|
|||
.flex-grow { flex-grow: 1; }
|
||||
.flex-no-shrink { flex-shrink: 0; }
|
||||
.ws-initial { white-space: initial; }
|
||||
.ws-normal { white-space: normal; }
|
||||
.overflow-auto { overflow: auto; }
|
||||
|
||||
.d-flex-center {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
- commit_sha = link_to deployment.short_sha, project_commit_path(@project, deployment.sha), class: "commit-sha has-tooltip", title: h(deployment.commit_title)
|
||||
.modal.ws-normal.fade{ tabindex: -1, id: "confirm-rollback-modal-#{deployment.id}" }
|
||||
.modal-dialog
|
||||
.modal-content
|
||||
.modal-header
|
||||
%h4.modal-title.d-flex.mw-100
|
||||
- if deployment.last?
|
||||
= s_("Environments|Re-deploy environment %{environment_name}?") % {environment_name: @environment.name}
|
||||
- else
|
||||
= s_("Environments|Rollback environment %{environment_name}?") % {environment_name: @environment.name}
|
||||
.modal-body
|
||||
- if deployment.last?
|
||||
%p= s_('Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?').html_safe % {commit_id: commit_sha}
|
||||
- else
|
||||
%p
|
||||
= s_('Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?').html_safe % {commit_id: commit_sha}
|
||||
.modal-footer
|
||||
= button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' }
|
||||
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-danger' do
|
||||
- if deployment.last?
|
||||
= s_('Environments|Re-deploy')
|
||||
- else
|
||||
= s_('Environments|Rollback')
|
|
@ -1,7 +1,8 @@
|
|||
- if can?(current_user, :create_deployment, deployment)
|
||||
- tooltip = deployment.last? ? s_('Environments|Re-deploy to environment') : s_('Environments|Rollback environment')
|
||||
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build has-tooltip', title: tooltip do
|
||||
= button_tag class: 'btn btn-default btn-build has-tooltip', type: 'button', data: { toggle: 'modal', target: "#confirm-rollback-modal-#{deployment.id}" }, title: tooltip do
|
||||
- if deployment.last?
|
||||
= sprite_icon('repeat')
|
||||
- else
|
||||
= sprite_icon('redo')
|
||||
= render 'projects/deployments/confirm_rollback_modal', deployment: deployment
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Confirmation Modal to Rollback on Environment
|
||||
merge_request: 25110
|
||||
author:
|
||||
type: added
|
|
@ -3055,6 +3055,12 @@ msgstr ""
|
|||
msgid "Environments|An error occurred while making the request."
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|An error occurred while re-deploying the environment, please try again"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|An error occurred while rolling back the environment, please try again"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|An error occurred while stopping the environment, please try again"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3100,15 +3106,33 @@ msgstr ""
|
|||
msgid "Environments|Open live environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Re-deploy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Re-deploy environment %{environment_name}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Re-deploy environment %{name}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Re-deploy to environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Read more about environments"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Rollback"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Rollback environment"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Rollback environment %{environment_name}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Rollback environment %{name}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Show all"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3121,6 +3145,18 @@ msgstr ""
|
|||
msgid "Environments|Stopping"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Updated"
|
||||
msgstr ""
|
||||
|
||||
|
|
70
spec/javascripts/environments/confirm_rollback_modal_spec.js
Normal file
70
spec/javascripts/environments/confirm_rollback_modal_spec.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
|
||||
import eventHub from '~/environments/event_hub';
|
||||
|
||||
describe('Confirm Rollback Modal Component', () => {
|
||||
let environment;
|
||||
|
||||
beforeEach(() => {
|
||||
environment = {
|
||||
name: 'test',
|
||||
last_deployment: {
|
||||
commit: {
|
||||
short_id: 'abc0123',
|
||||
},
|
||||
},
|
||||
modalId: 'test',
|
||||
};
|
||||
});
|
||||
|
||||
it('should show "Rollback" when isLastDeployment is false', () => {
|
||||
const component = shallowMount(ConfirmRollbackModal, {
|
||||
propsData: {
|
||||
environment: {
|
||||
...environment,
|
||||
isLastDeployment: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
const modal = component.find(GlModal);
|
||||
|
||||
expect(modal.attributes('title')).toContain('Rollback');
|
||||
expect(modal.attributes('title')).toContain('test');
|
||||
expect(modal.attributes('ok-title')).toBe('Rollback');
|
||||
expect(modal.text()).toContain('commit abc0123');
|
||||
expect(modal.text()).toContain('Are you sure you want to continue?');
|
||||
});
|
||||
|
||||
it('should show "Re-deploy" when isLastDeployment is true', () => {
|
||||
const component = shallowMount(ConfirmRollbackModal, {
|
||||
propsData: {
|
||||
environment: {
|
||||
...environment,
|
||||
isLastDeployment: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const modal = component.find(GlModal);
|
||||
|
||||
expect(modal.attributes('title')).toContain('Re-deploy');
|
||||
expect(modal.attributes('title')).toContain('test');
|
||||
expect(modal.attributes('ok-title')).toBe('Re-deploy');
|
||||
expect(modal.text()).toContain('commit abc0123');
|
||||
expect(modal.text()).toContain('Are you sure you want to continue?');
|
||||
});
|
||||
|
||||
it('should emit the "rollback" event when "ok" is clicked', () => {
|
||||
environment = { ...environment, isLastDeployment: true };
|
||||
const component = shallowMount(ConfirmRollbackModal, {
|
||||
propsData: {
|
||||
environment,
|
||||
},
|
||||
});
|
||||
const eventHubSpy = spyOn(eventHub, '$emit');
|
||||
const modal = component.find(GlModal);
|
||||
modal.vm.$emit('ok');
|
||||
|
||||
expect(eventHubSpy).toHaveBeenCalledWith('rollbackEnvironment', environment);
|
||||
});
|
||||
});
|
|
@ -1,8 +1,11 @@
|
|||
import Vue from 'vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import eventHub from '~/environments/event_hub';
|
||||
import rollbackComp from '~/environments/components/environment_rollback.vue';
|
||||
|
||||
describe('Rollback Component', () => {
|
||||
const retryURL = 'https://gitlab.com/retry';
|
||||
const retryUrl = 'https://gitlab.com/retry';
|
||||
let RollbackComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -13,8 +16,9 @@ describe('Rollback Component', () => {
|
|||
const component = new RollbackComponent({
|
||||
el: document.querySelector('.test-dom-element'),
|
||||
propsData: {
|
||||
retryUrl: retryURL,
|
||||
retryUrl,
|
||||
isLastDeployment: true,
|
||||
environment: {},
|
||||
},
|
||||
}).$mount();
|
||||
|
||||
|
@ -25,11 +29,33 @@ describe('Rollback Component', () => {
|
|||
const component = new RollbackComponent({
|
||||
el: document.querySelector('.test-dom-element'),
|
||||
propsData: {
|
||||
retryUrl: retryURL,
|
||||
retryUrl,
|
||||
isLastDeployment: false,
|
||||
environment: {},
|
||||
},
|
||||
}).$mount();
|
||||
|
||||
expect(component.$el).toHaveSpriteIcon('redo');
|
||||
});
|
||||
|
||||
it('should emit a "rollback" event on button click', () => {
|
||||
const eventHubSpy = spyOn(eventHub, '$emit');
|
||||
const component = shallowMount(RollbackComponent, {
|
||||
propsData: {
|
||||
retryUrl,
|
||||
environment: {
|
||||
name: 'test',
|
||||
},
|
||||
},
|
||||
});
|
||||
const button = component.find(GlButton);
|
||||
|
||||
button.vm.$emit('click');
|
||||
|
||||
expect(eventHubSpy).toHaveBeenCalledWith('requestRollbackEnvironment', {
|
||||
retryUrl,
|
||||
isLastDeployment: true,
|
||||
name: 'test',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'projects/deployments/_confirm_rollback_modal' do
|
||||
let(:environment) { create(:environment, :with_review_app) }
|
||||
let(:deployments) { environment.deployments }
|
||||
let(:project) { environment.project }
|
||||
|
||||
before do
|
||||
assign(:environment, environment)
|
||||
assign(:deployments, deployments)
|
||||
assign(:project, project)
|
||||
end
|
||||
|
||||
context 'when re-deploying last deployment' do
|
||||
let(:deployment) { deployments.first }
|
||||
|
||||
before do
|
||||
allow(view).to receive(:deployment).and_return(deployment)
|
||||
end
|
||||
|
||||
it 'shows "re-deploy"' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('h4', text: "Re-deploy environment #{environment.name}?")
|
||||
expect(rendered).to have_selector('p', text: "This action will relaunch the job for commit #{deployment.short_sha}, putting the environment in a previous version. Are you sure you want to continue?")
|
||||
expect(rendered).to have_selector('a.btn-danger', text: 'Re-deploy')
|
||||
end
|
||||
|
||||
it 'links to re-deploying the environment' do
|
||||
expected_link = retry_project_job_path(environment.project, deployment.deployable)
|
||||
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector("a[href='#{expected_link}']", text: 'Re-deploy')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when rolling back to previous deployment' do
|
||||
let(:deployment) { create(:deployment, environment: environment) }
|
||||
|
||||
before do
|
||||
allow(view).to receive(:deployment).and_return(deployment)
|
||||
end
|
||||
|
||||
it 'shows "rollback"' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector('h4', text: "Rollback environment #{environment.name}?")
|
||||
expect(rendered).to have_selector('p', text: "This action will run the job defined by staging for commit #{deployment.short_sha}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?")
|
||||
expect(rendered).to have_selector('a.btn-danger', text: 'Rollback')
|
||||
end
|
||||
|
||||
it 'links to re-deploying the environment' do
|
||||
expected_link = retry_project_job_path(environment.project, deployment.deployable)
|
||||
|
||||
render
|
||||
|
||||
expect(rendered).to have_selector("a[href='#{expected_link}']", text: 'Rollback')
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue