Commit aced918d authored by Andrew Fontaine's avatar Andrew Fontaine

Make Environment Stop work with GraphQL

The new environments page is built using local GraphQL state management,
and so the ability to stop environments needs to rely on it instead of
the event hub.

To ensure everything still works until I am ready to delete the old
environments page, the new opt-in graphql prop is added to enable this
functionality.
parent b4bfb098
......@@ -8,6 +8,8 @@ import { GlTooltipDirective, GlButton, GlModalDirective } from '@gitlab/ui';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
import setEnvironmentToStopMutation from '../graphql/mutations/set_environment_to_stop.mutation.graphql';
import isEnvironmentStoppingQuery from '../graphql/queries/is_environment_stopping.query.graphql';
export default {
components: {
......@@ -22,6 +24,19 @@ export default {
type: Object,
required: true,
},
graphql: {
type: Boolean,
required: false,
default: false,
},
},
apollo: {
isEnvironmentStopping: {
query: isEnvironmentStoppingQuery,
variables() {
return { environment: this.environment };
},
},
},
i18n: {
title: s__('Environments|Stop environment'),
......@@ -30,6 +45,7 @@ export default {
data() {
return {
isLoading: false,
isEnvironmentStopping: false,
};
},
mounted() {
......@@ -41,7 +57,14 @@ export default {
methods: {
onClick() {
this.$root.$emit(BV_HIDE_TOOLTIP, this.$options.stopEnvironmentTooltipId);
eventHub.$emit('requestStopEnvironment', this.environment);
if (this.graphql) {
this.$apollo.mutate({
mutation: setEnvironmentToStopMutation,
variables: { environment: this.environment },
});
} else {
eventHub.$emit('requestStopEnvironment', this.environment);
}
},
onStopEnvironment(environment) {
if (this.environment.id === environment.id) {
......@@ -56,7 +79,7 @@ export default {
<gl-button
v-gl-tooltip="{ id: $options.stopEnvironmentTooltipId }"
v-gl-modal-directive="'stop-environment-modal'"
:loading="isLoading"
:loading="isLoading || isEnvironmentStopping"
:title="$options.i18n.title"
:aria-label="$options.i18n.title"
icon="stop"
......
......@@ -5,8 +5,10 @@ import { updateHistory, setUrlParams, queryToObject } from '~/lib/utils/url_util
import environmentAppQuery from '../graphql/queries/environment_app.query.graphql';
import pollIntervalQuery from '../graphql/queries/poll_interval.query.graphql';
import pageInfoQuery from '../graphql/queries/page_info.query.graphql';
import environmentToStopQuery from '../graphql/queries/environment_to_stop.query.graphql';
import EnvironmentFolder from './new_environment_folder.vue';
import EnableReviewAppModal from './enable_review_app_modal.vue';
import StopEnvironmentModal from './stop_environment_modal.vue';
export default {
components: {
......@@ -16,6 +18,7 @@ export default {
GlPagination,
GlTab,
GlTabs,
StopEnvironmentModal,
},
apollo: {
environmentApp: {
......@@ -36,6 +39,9 @@ export default {
pageInfo: {
query: pageInfoQuery,
},
environmentToStop: {
query: environmentToStopQuery,
},
},
inject: ['newEnvironmentPath', 'canCreateEnvironment'],
i18n: {
......@@ -57,6 +63,7 @@ export default {
isReviewAppModalVisible: false,
page: parseInt(page, 10),
scope,
environmentToStop: {},
};
},
computed: {
......@@ -157,6 +164,7 @@ export default {
:modal-id="$options.modalId"
data-testid="enable-review-app-modal"
/>
<stop-environment-modal :environment="environmentToStop" graphql />
<gl-tabs
:action-secondary="addEnvironment"
:action-primary="openReviewAppModal"
......
......@@ -2,6 +2,7 @@
import { GlSprintf, GlTooltipDirective, GlModal } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import eventHub from '../event_hub';
import stopEnvironmentMutation from '../graphql/mutations/stop_environment.mutation.graphql';
export default {
id: 'stop-environment-modal',
......@@ -21,6 +22,11 @@ export default {
type: Object,
required: true,
},
graphql: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
......@@ -39,7 +45,14 @@ export default {
methods: {
onSubmit() {
eventHub.$emit('stopEnvironment', this.environment);
if (this.graphql) {
this.$apollo.mutate({
mutation: stopEnvironmentMutation,
variables: { environment: this.environment },
});
} else {
eventHub.$emit('stopEnvironment', this.environment);
}
},
},
};
......
mutation SetEnvironmentToStop($environment: LocalEnvironmentInput) {
setEnvironmentToStop(environment: $environment) @client
}
query isEnvironmentStopping($environment: LocalEnvironment) {
isEnvironmentStopping(environment: $environment) @client
}
......@@ -8,6 +8,7 @@ import {
import pollIntervalQuery from './queries/poll_interval.query.graphql';
import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql';
import environmentToStopQuery from './queries/environment_to_stop.query.graphql';
import environmentToDeleteQuery from './queries/environment_to_delete.query.graphql';
import pageInfoQuery from './queries/page_info.query.graphql';
......@@ -108,6 +109,12 @@ export const resolvers = (endpoint) => ({
]);
});
},
setEnvironmentToStop(_, { environment }, { client }) {
client.writeQuery({
query: environmentToStopQuery,
data: { environmentToStop: environment },
});
},
setEnvironmentToDelete(_, { environment }, { client }) {
client.writeQuery({
query: environmentToDeleteQuery,
......
......@@ -68,6 +68,8 @@ extend type Query {
environmentToDelete: LocalEnvironment
pageInfo: LocalPageInfo
environmentToRollback: LocalEnvironment
environmentToStop: LocalEnvironment
isEnvironmentStopping(environment: LocalEnvironmentInput): Boolean
isLastDeployment: Boolean
}
......@@ -78,4 +80,5 @@ extend type Mutation {
cancelAutoStop(environment: LocalEnvironmentInput): LocalErrors
setEnvironmentToDelete(environment: LocalEnvironmentInput): LocalErrors
setEnvironmentToRollback(environment: LocalEnvironmentInput): LocalErrors
setEnvironmentToStop(environment: LocalEnvironmentInput): LocalErrors
}
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import $ from 'jquery';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import setEnvironmentToStopMutation from '~/environments/graphql/mutations/set_environment_to_stop.mutation.graphql';
import isEnvironmentStoppingQuery from '~/environments/graphql/queries/is_environment_stopping.query.graphql';
import StopComponent from '~/environments/components/environment_stop.vue';
import eventHub from '~/environments/event_hub';
$.fn.tooltip = () => {};
import createMockApollo from 'helpers/mock_apollo_helper';
import { resolvedEnvironment } from './graphql/mock_data';
describe('Stop Component', () => {
let wrapper;
const createWrapper = () => {
const createWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(StopComponent, {
propsData: {
environment: {},
...props,
},
...options,
});
};
const findButton = () => wrapper.find(GlButton);
beforeEach(() => {
jest.spyOn(window, 'confirm');
describe('eventHub', () => {
beforeEach(() => {
createWrapper();
});
createWrapper();
});
it('should render a button to stop the environment', () => {
expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('title')).toEqual('Stop environment');
});
it('should render a button to stop the environment', () => {
expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('title')).toEqual('Stop environment');
it('emits requestStopEnvironment in the event hub when button is clicked', () => {
jest.spyOn(eventHub, '$emit');
findButton().vm.$emit('click');
expect(eventHub.$emit).toHaveBeenCalledWith('requestStopEnvironment', wrapper.vm.environment);
});
});
it('emits requestStopEnvironment in the event hub when button is clicked', () => {
jest.spyOn(eventHub, '$emit');
findButton().vm.$emit('click');
expect(eventHub.$emit).toHaveBeenCalledWith('requestStopEnvironment', wrapper.vm.environment);
describe('graphql', () => {
Vue.use(VueApollo);
let mockApollo;
beforeEach(() => {
mockApollo = createMockApollo();
mockApollo.clients.defaultClient.writeQuery({
query: isEnvironmentStoppingQuery,
variables: { environment: resolvedEnvironment },
data: { isEnvironmentStopping: true },
});
createWrapper(
{ graphql: true, environment: resolvedEnvironment },
{ apolloProvider: mockApollo },
);
});
it('should render a button to stop the environment', () => {
expect(findButton().exists()).toBe(true);
expect(wrapper.attributes('title')).toEqual('Stop environment');
});
it('sets the environment to stop on click', () => {
jest.spyOn(mockApollo.defaultClient, 'mutate');
findButton().vm.$emit('click');
expect(mockApollo.defaultClient.mutate).toHaveBeenCalledWith({
mutation: setEnvironmentToStopMutation,
variables: { environment: resolvedEnvironment },
});
});
it('should show a loading icon if the environment is currently stopping', async () => {
expect(findButton().props('loading')).toBe(true);
});
});
});
......@@ -3,6 +3,7 @@ import axios from '~/lib/utils/axios_utils';
import { resolvers } from '~/environments/graphql/resolvers';
import environmentToRollback from '~/environments/graphql/queries/environment_to_rollback.query.graphql';
import environmentToDelete from '~/environments/graphql/queries/environment_to_delete.query.graphql';
import environmentToStopQuery from '~/environments/graphql/queries/environment_to_stop.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import pollIntervalQuery from '~/environments/graphql/queries/poll_interval.query.graphql';
import pageInfoQuery from '~/environments/graphql/queries/page_info.query.graphql';
......@@ -210,4 +211,19 @@ describe('~/frontend/environments/graphql/resolvers', () => {
});
});
});
describe('setEnvironmentToStop', () => {
it('should write the given environment to the cache', () => {
localState.client.writeQuery = jest.fn();
mockResolvers.Mutation.setEnvironmentToStop(
null,
{ environment: resolvedEnvironment },
localState,
);
expect(localState.client.writeQuery).toHaveBeenCalledWith({
query: environmentToStopQuery,
data: { environmentToStop: resolvedEnvironment },
});
});
});
});
......@@ -8,7 +8,8 @@ import setWindowLocation from 'helpers/set_window_location_helper';
import { sprintf, __, s__ } from '~/locale';
import EnvironmentsApp from '~/environments/components/new_environments_app.vue';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
import { resolvedEnvironmentsApp, resolvedFolder } from './graphql/mock_data';
import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue';
import { resolvedEnvironmentsApp, resolvedFolder, resolvedEnvironment } from './graphql/mock_data';
Vue.use(VueApollo);
......@@ -17,6 +18,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
let environmentAppMock;
let environmentFolderMock;
let paginationMock;
let environmentToStopMock;
const createApolloProvider = () => {
const mockResolvers = {
......@@ -24,6 +26,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
environmentApp: environmentAppMock,
folder: environmentFolderMock,
pageInfo: paginationMock,
environmentToStop: environmentToStopMock,
},
};
......@@ -45,6 +48,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
provide = {},
environmentsApp,
folder,
environmentToStop = {},
pageInfo = {
total: 20,
perPage: 5,
......@@ -58,6 +62,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
environmentAppMock.mockReturnValue(environmentsApp);
environmentFolderMock.mockReturnValue(folder);
paginationMock.mockReturnValue(pageInfo);
environmentToStopMock.mockReturnValue(environmentToStop);
const apolloProvider = createApolloProvider();
wrapper = createWrapper({ apolloProvider, provide });
......@@ -68,6 +73,7 @@ describe('~/environments/components/new_environments_app.vue', () => {
beforeEach(() => {
environmentAppMock = jest.fn();
environmentFolderMock = jest.fn();
environmentToStopMock = jest.fn();
paginationMock = jest.fn();
});
......@@ -175,6 +181,20 @@ describe('~/environments/components/new_environments_app.vue', () => {
});
});
describe('modals', () => {
it('should pass the environment to stop to the stop environment modal', async () => {
await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp,
folder: resolvedFolder,
environmentToStop: resolvedEnvironment,
});
const modal = wrapper.findComponent(StopEnvironmentModal);
expect(modal.props('environment')).toMatchObject(resolvedEnvironment);
});
});
describe('pagination', () => {
it('should sync page from query params on load', async () => {
await createWrapperWithMocked({
......
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