diff options
author | Fatih Acet <acetfatih@gmail.com> | 2019-02-01 13:44:50 +0300 |
---|---|---|
committer | Fatih Acet <acetfatih@gmail.com> | 2019-02-01 13:44:50 +0300 |
commit | d7a3f875d13b8d4a599a61232938f762d9c5e735 (patch) | |
tree | 6d0b631c503599b5bff4738b9619e8047ad21735 | |
parent | 9a0dd375f38384ea70448e4da945e6864b923c4d (diff) | |
parent | 5b2110553ee0d5d524875deda3a20446bb7eb7c4 (diff) |
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
7 files changed, 81 insertions, 130 deletions
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index b1f992c03ff..fc4779632f9 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -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.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.store.updateAppProperty(appId, 'statusReason', null); + + 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() { diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index d4354dcfebd..3c3ce1dec56 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -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}'), { diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js index e31afadf186..360511e8882 100644 --- a/app/assets/javascripts/clusters/constants.js +++ b/app/assets/javascripts/clusters/constants.js @@ -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'; diff --git a/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml b/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml new file mode 100644 index 00000000000..19ff408ddf4 --- /dev/null +++ b/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml @@ -0,0 +1,5 @@ +--- +title: Fix cluster installation processing spinner +merge_request: 24814 +author: +type: fixed diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index fab9e035d53..2c8d014c36d 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -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! diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js index 880b469284b..7928feeadfa 100644 --- a/spec/javascripts/clusters/clusters_bundle_spec.js +++ b/spec/javascripts/clusters/clusters_bundle_spec.js @@ -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(); diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js index 45d56514930..d1f4a1cebb4 100644 --- a/spec/javascripts/clusters/components/application_row_spec.js +++ b/spec/javascripts/clusters/components/application_row_spec.js @@ -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, |