Commit 2df8eb00 authored by Frédéric Caplette's avatar Frédéric Caplette

Merge branch 'ag-328246-activation-modal-mutation-query' into 'master'

Subscription Activation Modal: Better arrange GraphQL mutation and query

See merge request gitlab-org/gitlab!60577
parents 7fc69431 8b39e171
...@@ -37,10 +37,10 @@ export default { ...@@ -37,10 +37,10 @@ export default {
currentSubscription: { currentSubscription: {
query: subscriptionQueries.query, query: subscriptionQueries.query,
update({ currentLicense }) { update({ currentLicense }) {
return currentLicense; return currentLicense || {};
}, },
skip() { result({ data }) {
return !this.canShowSubscriptionDetails; this.hasNewLicense = data?.currentLicense && !this.hasActiveLicense;
}, },
}, },
subscriptionHistory: { subscriptionHistory: {
...@@ -52,20 +52,27 @@ export default { ...@@ -52,20 +52,27 @@ export default {
}, },
data() { data() {
return { return {
canShowSubscriptionDetails: this.hasActiveLicense,
currentSubscription: {}, currentSubscription: {},
shouldShowSubscriptionActivationNotification: false, hasDismissedNotification: false,
hasNewLicense: false,
subscriptionHistory: [], subscriptionHistory: [],
notification: null, notification: null,
}; };
}, },
methods: { computed: {
didDismissSuccessAlert() { hasValidSubscriptionData() {
this.shouldShowSubscriptionActivationNotification = false; return Boolean(Object.keys(this.currentSubscription).length);
},
canShowSubscriptionDetails() {
return this.hasActiveLicense || this.hasValidSubscriptionData;
},
shouldShowActivationNotification() {
return !this.hasDismissedNotification && this.hasNewLicense && this.hasValidSubscriptionData;
}, },
handleActivation(hasLicense) { },
this.shouldShowSubscriptionActivationNotification = hasLicense; methods: {
this.canShowSubscriptionDetails = hasLicense; dismissSuccessAlert() {
this.hasDismissedNotification = true;
}, },
}, },
}; };
...@@ -76,12 +83,12 @@ export default { ...@@ -76,12 +83,12 @@ export default {
<h4 data-testid="subscription-main-title">{{ $options.i18n.subscriptionMainTitle }}</h4> <h4 data-testid="subscription-main-title">{{ $options.i18n.subscriptionMainTitle }}</h4>
<hr /> <hr />
<gl-alert <gl-alert
v-if="shouldShowSubscriptionActivationNotification" v-if="shouldShowActivationNotification"
variant="success" variant="success"
:title="$options.i18n.subscriptionActivationNotificationText" :title="$options.i18n.subscriptionActivationNotificationText"
class="mb-4" class="mb-4"
data-testid="subscription-activation-success-alert" data-testid="subscription-activation-success-alert"
@dismiss="didDismissSuccessAlert" @dismiss="dismissSuccessAlert"
/> />
<subscription-breakdown <subscription-breakdown
v-if="canShowSubscriptionDetails" v-if="canShowSubscriptionDetails"
...@@ -93,7 +100,7 @@ export default { ...@@ -93,7 +100,7 @@ export default {
<h3 class="gl-mb-7 gl-mt-6 gl-text-center" data-testid="subscription-activation-title"> <h3 class="gl-mb-7 gl-mt-6 gl-text-center" data-testid="subscription-activation-title">
{{ $options.i18n.subscriptionActivationTitle }} {{ $options.i18n.subscriptionActivationTitle }}
</h3> </h3>
<cloud-license-subscription-activation-form @subscription-activation="handleActivation" /> <cloud-license-subscription-activation-form />
<div class="row gl-mt-7"> <div class="row gl-mt-7">
<div class="col-lg-6"> <div class="col-lg-6">
<subscription-trial-card /> <subscription-trial-card />
......
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
GlLink, GlLink,
GlSprintf, GlSprintf,
} from '@gitlab/ui'; } from '@gitlab/ui';
import produce from 'immer';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import validation from '~/vue_shared/directives/validation'; import validation from '~/vue_shared/directives/validation';
import { import {
...@@ -17,19 +18,22 @@ import { ...@@ -17,19 +18,22 @@ import {
subscriptionQueries, subscriptionQueries,
} from '../constants'; } from '../constants';
export const SUBSCRIPTION_ACTIVATION_EVENT = 'subscription-activation'; const getLicenseFromData = ({
data: {
gitlabSubscriptionActivate: { license },
},
}) => license;
const getErrorsAsData = ({
data: {
gitlabSubscriptionActivate: { errors },
},
}) => errors;
export const SUBSCRIPTION_ACTIVATION_FAILURE_EVENT = 'subscription-activation-failure';
export const adminLicenseUrl = helpPagePath('/user/admin_area/license'); export const adminLicenseUrl = helpPagePath('/user/admin_area/license');
export default { export default {
i18n: {
title: subscriptionActivationForm.title,
howToActivateSubscription: subscriptionActivationForm.howToActivateSubscription,
activationCode: subscriptionActivationForm.activationCode,
pasteActivationCode: subscriptionActivationForm.pasteActivationCode,
acceptTerms: subscriptionActivationForm.acceptTerms,
activateLabel: subscriptionActivationForm.activateLabel,
fieldRequiredMessage,
},
name: 'CloudLicenseSubscriptionActivationForm', name: 'CloudLicenseSubscriptionActivationForm',
components: { components: {
GlButton, GlButton,
...@@ -41,12 +45,22 @@ export default { ...@@ -41,12 +45,22 @@ export default {
GlSprintf, GlSprintf,
GlLink, GlLink,
}, },
i18n: {
title: subscriptionActivationForm.title,
howToActivateSubscription: subscriptionActivationForm.howToActivateSubscription,
activationCode: subscriptionActivationForm.activationCode,
pasteActivationCode: subscriptionActivationForm.pasteActivationCode,
acceptTerms: subscriptionActivationForm.acceptTerms,
activateLabel: subscriptionActivationForm.activateLabel,
fieldRequiredMessage,
},
links: { links: {
adminLicenseUrl, adminLicenseUrl,
}, },
directives: { directives: {
validation: validation(), validation: validation(),
}, },
emits: [SUBSCRIPTION_ACTIVATION_FAILURE_EVENT],
data() { data() {
const form = { const form = {
state: false, state: false,
...@@ -95,21 +109,23 @@ export default { ...@@ -95,21 +109,23 @@ export default {
activationCode: this.form.fields.activationCode.value, activationCode: this.form.fields.activationCode.value,
}, },
}, },
}) update: (cache, mutation) => {
.then( const license = getLicenseFromData(mutation);
({ const { query } = subscriptionQueries;
data: { const data = produce(license, (draftData) => {
gitlabSubscriptionActivate: { errors }, draftData.currentLicense = license;
});
cache.writeQuery({ query, data });
}, },
}) => { })
.then((res) => {
const errors = getErrorsAsData(res);
if (errors.length) { if (errors.length) {
throw new Error(); throw new Error();
} }
this.$emit(SUBSCRIPTION_ACTIVATION_EVENT, true); })
},
)
.catch(() => { .catch(() => {
this.$emit(SUBSCRIPTION_ACTIVATION_EVENT, null); this.$emit(SUBSCRIPTION_ACTIVATION_FAILURE_EVENT, null);
}) })
.finally(() => { .finally(() => {
this.isLoading = false; this.isLoading = false;
......
fragment License on CurrentLicense {
id
type
plan
name
email
company
startsAt
expiresAt
activatedAt
lastSync
usersInLicenseCount
billableUsersCount
maximumUserCount
usersOverLicenseCount
}
#import "../fragments/license.fragment.graphql"
mutation($gitlabSubscriptionActivateInput: GitlabSubscriptionActivateInput!) { mutation($gitlabSubscriptionActivateInput: GitlabSubscriptionActivateInput!) {
gitlabSubscriptionActivate(input: $gitlabSubscriptionActivateInput) { gitlabSubscriptionActivate(input: $gitlabSubscriptionActivateInput) {
errors errors
license { license {
id ...License
type
plan
name
email
company
startsAt
expiresAt
activatedAt
lastSync
usersInLicenseCount
billableUsersCount
maximumUserCount
usersOverLicenseCount
} }
} }
} }
#import "../fragments/license.fragment.graphql"
query getCurrentLicense { query getCurrentLicense {
currentLicense { currentLicense {
id ...License
type
plan
name
email
company
startsAt
expiresAt
activatedAt
lastSync
usersInLicenseCount
billableUsersCount
maximumUserCount
usersOverLicenseCount
} }
} }
...@@ -8,7 +8,12 @@ import CloudLicenseShowApp from '../components/app.vue'; ...@@ -8,7 +8,12 @@ import CloudLicenseShowApp from '../components/app.vue';
Vue.use(VueApollo); Vue.use(VueApollo);
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(
{},
{
assumeImmutableResults: true,
},
),
}); });
export default () => { export default () => {
......
...@@ -52,12 +52,10 @@ describe('CloudLicenseApp', () => { ...@@ -52,12 +52,10 @@ describe('CloudLicenseApp', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
currentSubscriptionResolver.mockRestore();
subscriptionHistoryResolver.mockRestore();
}); });
describe('Subscription Activation Form', () => { describe('Subscription Activation Form', () => {
beforeEach(() => { it('shows the main title', () => {
currentSubscriptionResolver = jest currentSubscriptionResolver = jest
.fn() .fn()
.mockResolvedValue({ data: { currentLicense: license.ULTIMATE } }); .mockResolvedValue({ data: { currentLicense: license.ULTIMATE } });
...@@ -65,21 +63,23 @@ describe('CloudLicenseApp', () => { ...@@ -65,21 +63,23 @@ describe('CloudLicenseApp', () => {
.fn() .fn()
.mockResolvedValue({ data: { licenseHistoryEntries: { nodes: subscriptionHistory } } }); .mockResolvedValue({ data: { licenseHistoryEntries: { nodes: subscriptionHistory } } });
createComponent({}, [currentSubscriptionResolver, subscriptionHistoryResolver]); createComponent({}, [currentSubscriptionResolver, subscriptionHistoryResolver]);
});
it('shows the main title', () => {
expect(findSubscriptionMainTitle().text()).toBe(subscriptionMainTitle); expect(findSubscriptionMainTitle().text()).toBe(subscriptionMainTitle);
}); });
describe('without an active license', () => { describe('without an active license', () => {
beforeEach(() => {
currentSubscriptionResolver = jest
.fn()
.mockResolvedValue({ data: { currentLicense: null } });
subscriptionHistoryResolver = jest
.fn()
.mockResolvedValue({ data: { licenseHistoryEntries: { nodes: [] } } });
createComponent({}, [currentSubscriptionResolver, subscriptionHistoryResolver]);
});
it('shows a title saying there is no active subscription', () => { it('shows a title saying there is no active subscription', () => {
expect(findSubscriptionActivationTitle().text()).toBe(subscriptionActivationTitle); expect(findSubscriptionActivationTitle().text()).toBe(subscriptionActivationTitle);
}); });
it('does not query for the current license', () => {
expect(currentSubscriptionResolver).toHaveBeenCalledTimes(0);
});
it('queries for the current history', () => { it('queries for the current history', () => {
expect(subscriptionHistoryResolver).toHaveBeenCalledTimes(1); expect(subscriptionHistoryResolver).toHaveBeenCalledTimes(1);
}); });
...@@ -88,18 +88,23 @@ describe('CloudLicenseApp', () => { ...@@ -88,18 +88,23 @@ describe('CloudLicenseApp', () => {
expect(findActivateSubscriptionForm().exists()).toBe(true); expect(findActivateSubscriptionForm().exists()).toBe(true);
}); });
it('does not the activation success notification', () => { it('does not show the activation success notification', () => {
expect(findSubscriptionActivationSuccessAlert().exists()).toBe(false); expect(findSubscriptionActivationSuccessAlert().exists()).toBe(false);
}); });
}); });
describe('activate the license', () => { describe('activating the license', () => {
beforeEach(() => { beforeEach(() => {
currentSubscriptionResolver = jest
.fn()
.mockResolvedValue({ data: { currentLicense: license.ULTIMATE } });
subscriptionHistoryResolver = jest
.fn()
.mockResolvedValue({ data: { licenseHistoryEntries: { nodes: subscriptionHistory } } });
createComponent({ hasActiveLicense: false }, [ createComponent({ hasActiveLicense: false }, [
currentSubscriptionResolver, currentSubscriptionResolver,
subscriptionHistoryResolver, subscriptionHistoryResolver,
]); ]);
findActivateSubscriptionForm().vm.$emit('subscription-activation', true);
}); });
it('passes the correct data to the subscription breakdown', () => { it('passes the correct data to the subscription breakdown', () => {
......
...@@ -2,7 +2,7 @@ import { GlForm, GlFormCheckbox, GlFormInput } from '@gitlab/ui'; ...@@ -2,7 +2,7 @@ import { GlForm, GlFormCheckbox, GlFormInput } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import CloudLicenseSubscriptionActivationForm, { import CloudLicenseSubscriptionActivationForm, {
SUBSCRIPTION_ACTIVATION_EVENT, SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_form.vue'; } from 'ee/pages/admin/cloud_licenses/components/subscription_activation_form.vue';
import { fieldRequiredMessage, subscriptionQueries } from 'ee/pages/admin/cloud_licenses/constants'; import { fieldRequiredMessage, subscriptionQueries } from 'ee/pages/admin/cloud_licenses/constants';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
...@@ -132,10 +132,6 @@ describe('CloudLicenseApp', () => { ...@@ -132,10 +132,6 @@ describe('CloudLicenseApp', () => {
}, },
}); });
}); });
it('emits a successful event', () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_EVENT)).toEqual([[true]]);
});
}); });
describe('when the mutation is not successful but looks like it is', () => { describe('when the mutation is not successful but looks like it is', () => {
...@@ -147,10 +143,6 @@ describe('CloudLicenseApp', () => { ...@@ -147,10 +143,6 @@ describe('CloudLicenseApp', () => {
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent()); findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
}); });
it('emits a successful event', () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_EVENT)).toBeUndefined();
});
it.todo('deals with failures in a meaningful way'); it.todo('deals with failures in a meaningful way');
}); });
...@@ -161,8 +153,8 @@ describe('CloudLicenseApp', () => { ...@@ -161,8 +153,8 @@ describe('CloudLicenseApp', () => {
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent()); findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
}); });
it('emits a successful event', () => { it('emits a unsuccessful event', () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_EVENT)).toBeUndefined(); expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_FAILURE_EVENT)).toBeUndefined();
}); });
it.todo('deals with failures in a meaningful way'); it.todo('deals with failures in a meaningful way');
......
...@@ -86,9 +86,9 @@ export const activateLicenseMutationResponse = { ...@@ -86,9 +86,9 @@ export const activateLicenseMutationResponse = {
gitlabSubscriptionActivate: { gitlabSubscriptionActivate: {
license: { license: {
id: 'gid://gitlab/License/3', id: 'gid://gitlab/License/3',
type: 'legacy', type: 'cloud',
plan: 'ultimate', plan: 'ultimate',
name: 'Test license', name: 'Cloud License',
email: 'user@example.com', email: 'user@example.com',
company: 'Example Inc', company: 'Example Inc',
startsAt: '2020-01-01', startsAt: '2020-01-01',
......
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