Merge branch '56398-fix-broken-cluster-installation-loading-state' into 'master'
Fix cluster installation processing spinner (reopened) Closes #56398 See merge request gitlab-org/gitlab-ce!24814
This commit is contained in:
commit
d7a3f875d1
|
@ -6,7 +6,7 @@ import Flash from '../flash';
|
|||
import Poll from '../lib/utils/poll';
|
||||
import initSettingsPanels from '../settings_panels';
|
||||
import eventHub from './event_hub';
|
||||
import { APPLICATION_STATUS, REQUEST_LOADING, REQUEST_SUCCESS, REQUEST_FAILURE } from './constants';
|
||||
import { APPLICATION_STATUS, REQUEST_SUBMITTED, REQUEST_FAILURE } from './constants';
|
||||
import ClustersService from './services/clusters_service';
|
||||
import ClustersStore from './stores/clusters_store';
|
||||
import Applications from './components/applications.vue';
|
||||
|
@ -231,22 +231,18 @@ export default class Clusters {
|
|||
|
||||
installApplication(data) {
|
||||
const appId = data.id;
|
||||
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_LOADING);
|
||||
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_SUBMITTED);
|
||||
this.store.updateAppProperty(appId, 'requestReason', null);
|
||||
this.store.updateAppProperty(appId, 'statusReason', null);
|
||||
|
||||
this.service
|
||||
.installApplication(appId, data.params)
|
||||
.then(() => {
|
||||
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_SUCCESS);
|
||||
})
|
||||
.catch(() => {
|
||||
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_FAILURE);
|
||||
this.store.updateAppProperty(
|
||||
appId,
|
||||
'requestReason',
|
||||
s__('ClusterIntegration|Request to begin installing failed'),
|
||||
);
|
||||
});
|
||||
this.service.installApplication(appId, data.params).catch(() => {
|
||||
this.store.updateAppProperty(appId, 'requestStatus', REQUEST_FAILURE);
|
||||
this.store.updateAppProperty(
|
||||
appId,
|
||||
'requestReason',
|
||||
s__('ClusterIntegration|Request to begin installing failed'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
|
|
@ -4,12 +4,7 @@ import { s__, sprintf } from '../../locale';
|
|||
import eventHub from '../event_hub';
|
||||
import identicon from '../../vue_shared/components/identicon.vue';
|
||||
import loadingButton from '../../vue_shared/components/loading_button.vue';
|
||||
import {
|
||||
APPLICATION_STATUS,
|
||||
REQUEST_LOADING,
|
||||
REQUEST_SUCCESS,
|
||||
REQUEST_FAILURE,
|
||||
} from '../constants';
|
||||
import { APPLICATION_STATUS, REQUEST_SUBMITTED, REQUEST_FAILURE } from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -72,6 +67,13 @@ export default {
|
|||
isKnownStatus() {
|
||||
return Object.values(APPLICATION_STATUS).includes(this.status);
|
||||
},
|
||||
isInstalling() {
|
||||
return (
|
||||
this.status === APPLICATION_STATUS.SCHEDULED ||
|
||||
this.status === APPLICATION_STATUS.INSTALLING ||
|
||||
(this.requestStatus === REQUEST_SUBMITTED && !this.statusReason && !this.isInstalled)
|
||||
);
|
||||
},
|
||||
isInstalled() {
|
||||
return (
|
||||
this.status === APPLICATION_STATUS.INSTALLED ||
|
||||
|
@ -79,6 +81,18 @@ export default {
|
|||
this.status === APPLICATION_STATUS.UPDATING
|
||||
);
|
||||
},
|
||||
canInstall() {
|
||||
if (this.isInstalling) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
|
||||
this.status === APPLICATION_STATUS.INSTALLABLE ||
|
||||
this.status === APPLICATION_STATUS.ERROR ||
|
||||
this.isUnknownStatus
|
||||
);
|
||||
},
|
||||
hasLogo() {
|
||||
return !!this.logoUrl;
|
||||
},
|
||||
|
@ -90,12 +104,7 @@ export default {
|
|||
return `js-cluster-application-row-${this.id}`;
|
||||
},
|
||||
installButtonLoading() {
|
||||
return (
|
||||
!this.status ||
|
||||
this.status === APPLICATION_STATUS.SCHEDULED ||
|
||||
this.status === APPLICATION_STATUS.INSTALLING ||
|
||||
this.requestStatus === REQUEST_LOADING
|
||||
);
|
||||
return !this.status || this.status === APPLICATION_STATUS.SCHEDULED || this.isInstalling;
|
||||
},
|
||||
installButtonDisabled() {
|
||||
// Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
|
||||
|
@ -104,30 +113,17 @@ export default {
|
|||
return (
|
||||
((this.status !== APPLICATION_STATUS.INSTALLABLE &&
|
||||
this.status !== APPLICATION_STATUS.ERROR) ||
|
||||
this.requestStatus === REQUEST_LOADING ||
|
||||
this.requestStatus === REQUEST_SUCCESS) &&
|
||||
this.isInstalling) &&
|
||||
this.isKnownStatus
|
||||
);
|
||||
},
|
||||
installButtonLabel() {
|
||||
let label;
|
||||
if (
|
||||
this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
|
||||
this.status === APPLICATION_STATUS.INSTALLABLE ||
|
||||
this.status === APPLICATION_STATUS.ERROR ||
|
||||
this.isUnknownStatus
|
||||
) {
|
||||
if (this.canInstall) {
|
||||
label = s__('ClusterIntegration|Install');
|
||||
} else if (
|
||||
this.status === APPLICATION_STATUS.SCHEDULED ||
|
||||
this.status === APPLICATION_STATUS.INSTALLING
|
||||
) {
|
||||
} else if (this.isInstalling) {
|
||||
label = s__('ClusterIntegration|Installing');
|
||||
} else if (
|
||||
this.status === APPLICATION_STATUS.INSTALLED ||
|
||||
this.status === APPLICATION_STATUS.UPDATED ||
|
||||
this.status === APPLICATION_STATUS.UPDATING
|
||||
) {
|
||||
} else if (this.isInstalled) {
|
||||
label = s__('ClusterIntegration|Installed');
|
||||
}
|
||||
|
||||
|
@ -140,7 +136,10 @@ export default {
|
|||
return s__('ClusterIntegration|Manage');
|
||||
},
|
||||
hasError() {
|
||||
return this.status === APPLICATION_STATUS.ERROR || this.requestStatus === REQUEST_FAILURE;
|
||||
return (
|
||||
!this.isInstalling &&
|
||||
(this.status === APPLICATION_STATUS.ERROR || this.requestStatus === REQUEST_FAILURE)
|
||||
);
|
||||
},
|
||||
generalErrorDescription() {
|
||||
return sprintf(s__('ClusterIntegration|Something went wrong while installing %{title}'), {
|
||||
|
|
|
@ -18,8 +18,7 @@ export const APPLICATION_STATUS = {
|
|||
};
|
||||
|
||||
// These are only used client-side
|
||||
export const REQUEST_LOADING = 'request-loading';
|
||||
export const REQUEST_SUCCESS = 'request-success';
|
||||
export const REQUEST_SUBMITTED = 'request-submitted';
|
||||
export const REQUEST_FAILURE = 'request-failure';
|
||||
export const INGRESS = 'ingress';
|
||||
export const JUPYTER = 'jupyter';
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix cluster installation processing spinner
|
||||
merge_request: 24814
|
||||
author:
|
||||
type: fixed
|
|
@ -48,9 +48,9 @@ describe 'Clusters Applications', :js do
|
|||
|
||||
it 'they see status transition' do
|
||||
page.within('.js-cluster-application-row-helm') do
|
||||
# FE sends request and gets the response, then the buttons is "Install"
|
||||
# FE sends request and gets the response, then the buttons is "Installing"
|
||||
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
|
||||
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
|
||||
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
|
||||
|
||||
wait_until_helm_created!
|
||||
|
||||
|
@ -118,7 +118,7 @@ describe 'Clusters Applications', :js do
|
|||
|
||||
page.within('.js-cluster-application-row-cert_manager') do
|
||||
expect(email_form_value).to eq(cluster.user.email)
|
||||
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
|
||||
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
|
||||
|
||||
page.find('.js-email').set("new_email@example.org")
|
||||
Clusters::Cluster.last.application_cert_manager.make_installing!
|
||||
|
@ -153,9 +153,9 @@ describe 'Clusters Applications', :js do
|
|||
|
||||
it 'they see status transition' do
|
||||
page.within('.js-cluster-application-row-ingress') do
|
||||
# FE sends request and gets the response, then the buttons is "Install"
|
||||
# FE sends request and gets the response, then the buttons is "Installing"
|
||||
expect(page).to have_css('.js-cluster-application-install-button[disabled]')
|
||||
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
|
||||
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
|
||||
|
||||
Clusters::Cluster.last.application_ingress.make_installing!
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
import Clusters from '~/clusters/clusters_bundle';
|
||||
import {
|
||||
REQUEST_LOADING,
|
||||
REQUEST_SUCCESS,
|
||||
REQUEST_FAILURE,
|
||||
APPLICATION_STATUS,
|
||||
} from '~/clusters/constants';
|
||||
import { REQUEST_SUBMITTED, REQUEST_FAILURE, APPLICATION_STATUS } from '~/clusters/constants';
|
||||
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
|
||||
|
||||
describe('Clusters', () => {
|
||||
|
@ -196,67 +191,43 @@ describe('Clusters', () => {
|
|||
});
|
||||
|
||||
describe('installApplication', () => {
|
||||
it('tries to install helm', done => {
|
||||
it('tries to install helm', () => {
|
||||
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
|
||||
|
||||
expect(cluster.store.state.applications.helm.requestStatus).toEqual(null);
|
||||
|
||||
cluster.installApplication({ id: 'helm' });
|
||||
|
||||
expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_LOADING);
|
||||
expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUBMITTED);
|
||||
expect(cluster.store.state.applications.helm.requestReason).toEqual(null);
|
||||
expect(cluster.service.installApplication).toHaveBeenCalledWith('helm', undefined);
|
||||
|
||||
getSetTimeoutPromise()
|
||||
.then(() => {
|
||||
expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUCCESS);
|
||||
expect(cluster.store.state.applications.helm.requestReason).toEqual(null);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('tries to install ingress', done => {
|
||||
it('tries to install ingress', () => {
|
||||
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
|
||||
|
||||
expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null);
|
||||
|
||||
cluster.installApplication({ id: 'ingress' });
|
||||
|
||||
expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_LOADING);
|
||||
expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_SUBMITTED);
|
||||
expect(cluster.store.state.applications.ingress.requestReason).toEqual(null);
|
||||
expect(cluster.service.installApplication).toHaveBeenCalledWith('ingress', undefined);
|
||||
|
||||
getSetTimeoutPromise()
|
||||
.then(() => {
|
||||
expect(cluster.store.state.applications.ingress.requestStatus).toEqual(REQUEST_SUCCESS);
|
||||
expect(cluster.store.state.applications.ingress.requestReason).toEqual(null);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('tries to install runner', done => {
|
||||
it('tries to install runner', () => {
|
||||
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
|
||||
|
||||
expect(cluster.store.state.applications.runner.requestStatus).toEqual(null);
|
||||
|
||||
cluster.installApplication({ id: 'runner' });
|
||||
|
||||
expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_LOADING);
|
||||
expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_SUBMITTED);
|
||||
expect(cluster.store.state.applications.runner.requestReason).toEqual(null);
|
||||
expect(cluster.service.installApplication).toHaveBeenCalledWith('runner', undefined);
|
||||
|
||||
getSetTimeoutPromise()
|
||||
.then(() => {
|
||||
expect(cluster.store.state.applications.runner.requestStatus).toEqual(REQUEST_SUCCESS);
|
||||
expect(cluster.store.state.applications.runner.requestReason).toEqual(null);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('tries to install jupyter', done => {
|
||||
it('tries to install jupyter', () => {
|
||||
spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve());
|
||||
|
||||
expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(null);
|
||||
|
@ -265,19 +236,11 @@ describe('Clusters', () => {
|
|||
params: { hostname: cluster.store.state.applications.jupyter.hostname },
|
||||
});
|
||||
|
||||
expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_LOADING);
|
||||
expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUBMITTED);
|
||||
expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null);
|
||||
expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', {
|
||||
hostname: cluster.store.state.applications.jupyter.hostname,
|
||||
});
|
||||
|
||||
getSetTimeoutPromise()
|
||||
.then(() => {
|
||||
expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUCCESS);
|
||||
expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sets error request status when the request fails', done => {
|
||||
|
@ -289,7 +252,7 @@ describe('Clusters', () => {
|
|||
|
||||
cluster.installApplication({ id: 'helm' });
|
||||
|
||||
expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_LOADING);
|
||||
expect(cluster.store.state.applications.helm.requestStatus).toEqual(REQUEST_SUBMITTED);
|
||||
expect(cluster.store.state.applications.helm.requestReason).toEqual(null);
|
||||
expect(cluster.service.installApplication).toHaveBeenCalled();
|
||||
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import eventHub from '~/clusters/event_hub';
|
||||
import {
|
||||
APPLICATION_STATUS,
|
||||
REQUEST_LOADING,
|
||||
REQUEST_SUCCESS,
|
||||
REQUEST_FAILURE,
|
||||
} from '~/clusters/constants';
|
||||
import { APPLICATION_STATUS, REQUEST_SUBMITTED, REQUEST_FAILURE } from '~/clusters/constants';
|
||||
import applicationRow from '~/clusters/components/application_row.vue';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
import { DEFAULT_APPLICATION_STATE } from '../services/mock_data';
|
||||
|
@ -57,6 +52,12 @@ describe('Application Row', () => {
|
|||
expect(vm.installButtonLabel).toBeUndefined();
|
||||
});
|
||||
|
||||
it('has install button', () => {
|
||||
const installationBtn = vm.$el.querySelector('.js-cluster-application-install-button');
|
||||
|
||||
expect(installationBtn).not.toBe(null);
|
||||
});
|
||||
|
||||
it('has disabled "Install" when APPLICATION_STATUS.NOT_INSTALLABLE', () => {
|
||||
vm = mountComponent(ApplicationRow, {
|
||||
...DEFAULT_APPLICATION_STATE,
|
||||
|
@ -101,6 +102,18 @@ describe('Application Row', () => {
|
|||
expect(vm.installButtonDisabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('has loading "Installing" when REQUEST_SUBMITTED', () => {
|
||||
vm = mountComponent(ApplicationRow, {
|
||||
...DEFAULT_APPLICATION_STATE,
|
||||
status: APPLICATION_STATUS.INSTALLABLE,
|
||||
requestStatus: REQUEST_SUBMITTED,
|
||||
});
|
||||
|
||||
expect(vm.installButtonLabel).toEqual('Installing');
|
||||
expect(vm.installButtonLoading).toEqual(true);
|
||||
expect(vm.installButtonDisabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('has disabled "Installed" when APPLICATION_STATUS.INSTALLED', () => {
|
||||
vm = mountComponent(ApplicationRow, {
|
||||
...DEFAULT_APPLICATION_STATE,
|
||||
|
@ -134,30 +147,6 @@ describe('Application Row', () => {
|
|||
expect(vm.installButtonDisabled).toEqual(false);
|
||||
});
|
||||
|
||||
it('has loading "Install" when REQUEST_LOADING', () => {
|
||||
vm = mountComponent(ApplicationRow, {
|
||||
...DEFAULT_APPLICATION_STATE,
|
||||
status: APPLICATION_STATUS.INSTALLABLE,
|
||||
requestStatus: REQUEST_LOADING,
|
||||
});
|
||||
|
||||
expect(vm.installButtonLabel).toEqual('Install');
|
||||
expect(vm.installButtonLoading).toEqual(true);
|
||||
expect(vm.installButtonDisabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('has disabled "Install" when REQUEST_SUCCESS', () => {
|
||||
vm = mountComponent(ApplicationRow, {
|
||||
...DEFAULT_APPLICATION_STATE,
|
||||
status: APPLICATION_STATUS.INSTALLABLE,
|
||||
requestStatus: REQUEST_SUCCESS,
|
||||
});
|
||||
|
||||
expect(vm.installButtonLabel).toEqual('Install');
|
||||
expect(vm.installButtonLoading).toEqual(false);
|
||||
expect(vm.installButtonDisabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('has enabled "Install" when REQUEST_FAILURE (so you can try installing again)', () => {
|
||||
vm = mountComponent(ApplicationRow, {
|
||||
...DEFAULT_APPLICATION_STATE,
|
||||
|
|
Loading…
Reference in New Issue