From 8bf1ad56841f7bdff94762b50937ffece1e2375b Mon Sep 17 00:00:00 2001 From: Grégoire Aubert Date: Wed, 1 Nov 2017 15:38:47 +0100 Subject: SONAR-9937 New workflow for uninstallation of edition in marketplace --- server/sonar-web/src/main/js/api/marketplace.ts | 2 +- .../nav/settings/SettingsEditionsNotif.tsx | 37 ++++- .../__tests__/SettingsEditionsNotif-test.tsx | 2 +- .../src/main/js/apps/marketplace/EditionBoxes.tsx | 56 +++++-- .../marketplace/__tests__/EditionBoxes-test.tsx | 52 +++++- .../__snapshots__/EditionBoxes-test.tsx.snap | 22 ++- .../js/apps/marketplace/components/EditionBox.tsx | 53 ++---- .../marketplace/components/EditionBoxBadge.tsx | 42 +++-- .../marketplace/components/LicenseEditionSet.tsx | 2 +- .../marketplace/components/PluginAvailable.tsx | 2 +- .../marketplace/components/PluginUpdateItem.tsx | 2 +- .../components/UninstallEditionForm.tsx | 6 +- .../components/__tests__/EditionBox-test.tsx | 61 ++----- .../__tests__/LicenseEditionForm-test.tsx | 2 +- .../__tests__/LicenseEditionSet-test.tsx | 2 +- .../__tests__/UninstallEditionForm-test.tsx | 2 +- .../__snapshots__/EditionBox-test.tsx.snap | 177 +-------------------- .../__snapshots__/LicenseEditionForm-test.tsx.snap | 4 +- .../UninstallEditionForm-test.tsx.snap | 7 +- server/sonar-web/src/main/less/init/misc.less | 7 +- .../main/resources/org/sonar/l10n/core.properties | 13 +- 21 files changed, 208 insertions(+), 345 deletions(-) diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts index 8bb8a462a42..7e3de777006 100644 --- a/server/sonar-web/src/main/js/api/marketplace.ts +++ b/server/sonar-web/src/main/js/api/marketplace.ts @@ -25,7 +25,7 @@ export interface Edition { name: string; textDescription: string; homeUrl: string; - requestUrl: string; + licenseRequestUrl: string; downloadUrl: string; } diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx index 527b8479ada..4e39bd19030 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx @@ -46,6 +46,25 @@ export default class SettingsEditionsNotif extends React.PureComponent {} ); + renderStatusMsg(edition?: Edition) { + const { editionStatus } = this.props; + return ( + + + + {edition ? ( + translateWithParameters( + 'marketplace.status_x.' + editionStatus.installationStatus, + edition.name + ) + ) : ( + translate('marketplace.status', editionStatus.installationStatus) + )} + + + ); + } + renderRestartMsg(edition?: Edition) { const { editionStatus, preventRestart } = this.props; return ( @@ -104,21 +123,23 @@ export default class SettingsEditionsNotif extends React.PureComponent edition.key === nextEditionKey); + const currentEdition = + this.props.editions && + this.props.editions.find( + edition => + edition.key === currentEditionKey || (!currentEditionKey && edition.key === 'community') + ); switch (installationStatus) { case 'AUTOMATIC_IN_PROGRESS': - return ( - - - {translate('marketplace.status.AUTOMATIC_IN_PROGRESS')} - - ); + return this.renderStatusMsg(nextEdition); case 'AUTOMATIC_READY': - case 'UNINSTALL_IN_PROGRESS': return this.renderRestartMsg(nextEdition); + case 'UNINSTALL_IN_PROGRESS': + return this.renderRestartMsg(currentEdition); case 'MANUAL_IN_PROGRESS': return this.renderManualMsg(nextEdition); } diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx index 3614b9b06fe..9c16173bead 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx @@ -65,7 +65,7 @@ it('should display a manual installation notification', () => { textDescription: 'Foo desc', downloadUrl: 'download_url', homeUrl: 'more_url', - requestUrl: 'license_url' + licenseRequestUrl: 'license_url' } ]} preventRestart={false} diff --git a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx index ddba3af1b72..1dbbefc1153 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -87,7 +87,7 @@ export default class EditionBoxes extends React.PureComponent { } render() { - const { canInstall, canUninstall, editions, editionStatus, loading } = this.props; + const { canInstall, canUninstall, editions, loading } = this.props; if (loading) { return ; @@ -114,23 +114,47 @@ export default class EditionBoxes extends React.PureComponent { } const sortedEditions = sortEditions(editions); - const installedIdx = - editionStatus && - sortedEditions.findIndex(edition => edition.key === editionStatus.currentEditionKey); + const status = this.props.editionStatus || { installationStatus: 'NONE' }; + const inProgressStatus = [ + 'AUTOMATIC_IN_PROGRESS', + 'AUTOMATIC_READY', + 'UNINSTALL_IN_PROGRESS' + ].includes(status.installationStatus); + const installedIdx = sortedEditions.findIndex( + edition => edition.key === status.currentEditionKey + ); + const nextIdx = sortedEditions.findIndex(edition => edition.key === status.nextEditionKey); + const currentIdx = inProgressStatus ? nextIdx : installedIdx; return (
- {sortedEditions.map((edition, idx) => ( - - ))} + 0} + edition={sortedEditions[0]} + editionStatus={status} + key={sortedEditions[0].key} + onAction={this.handleOpenUninstallForm} + /> + {sortedEditions + .slice(1) + .map((edition, idx) => ( + idx + 1 ? ( + translate('marketplace.downgrade') + ) : ( + translate('marketplace.upgrade') + ) + } + disableAction={inProgressStatus} + displayAction={canInstall && currentIdx !== idx + 1} + edition={edition} + editionStatus={status} + key={edition.key} + onAction={this.handleOpenLicenseForm} + /> + ))} {this.renderForms(sortedEditions, installedIdx)}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx index 0201372b086..fc8babe79d2 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx @@ -35,7 +35,7 @@ const DEFAULT_EDITIONS = [ textDescription: 'foo', downloadUrl: 'download_url', homeUrl: 'more_url', - requestUrl: 'license_url' + licenseRequestUrl: 'license_url' }, { key: 'comunity', @@ -43,7 +43,7 @@ const DEFAULT_EDITIONS = [ textDescription: 'bar', downloadUrl: 'download_url', homeUrl: 'more_url', - requestUrl: 'license_url' + licenseRequestUrl: 'license_url' } ]; @@ -59,6 +59,54 @@ it('should display an error message', () => { expect(wrapper).toMatchSnapshot(); }); +it('should display community without the downgrade button', () => { + const communityBox = getWrapper({ + editions: DEFAULT_EDITIONS, + editionStatus: { + currentEditionKey: '', + installationStatus: 'NONE' + }, + loading: false + }) + .find('EditionBox') + .first(); + expect(communityBox.prop('displayAction')).toBeFalsy(); +}); + +it('should not display action buttons', () => { + const wrapper = getWrapper({ + editions: DEFAULT_EDITIONS, + editionStatus: { + currentEditionKey: '', + installationStatus: 'NONE' + }, + loading: false, + canInstall: false, + canUninstall: false + }); + wrapper.find('EditionBox').forEach(box => expect(box.prop('displayAction')).toBeFalsy()); +}); + +it('should display disabled action buttons', () => { + const wrapper = getWrapper({ + editions: DEFAULT_EDITIONS, + editionStatus: { installationStatus: 'AUTOMATIC_IN_PROGRESS', nextEditionKey: 'developer' }, + loading: false + }); + + wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy()); + expect(wrapper.find('EditionBox').map(box => box.prop('displayAction'))).toEqual([true, false]); + + wrapper.setProps({ + editionStatus: { currentEditionKey: 'developer', installationStatus: 'UNINSTALL_IN_PROGRESS' } + }); + wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy()); + expect(wrapper.find('EditionBox').map(box => box.prop('displayAction'))).toEqual([false, true]); + + wrapper.setProps({ editionStatus: { installationStatus: 'AUTOMATIC_READY' } }); + wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy()); +}); + it('should open the license form', () => { const wrapper = getWrapper({ editions: DEFAULT_EDITIONS }); (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS[0]); diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap index 952e8dc78c1..644471ccdca 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap @@ -36,15 +36,16 @@ exports[`should display the edition boxes correctly 2`] = ` className="spacer-bottom marketplace-editions" > `; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx index 6f90fda970e..40d89bc665e 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx @@ -23,54 +23,19 @@ import { Edition, EditionStatus } from '../../../api/marketplace'; import { translate } from '../../../helpers/l10n'; interface Props { - canInstall: boolean; - canUninstall: boolean; + actionLabel: string; + disableAction: boolean; + displayAction: boolean; edition: Edition; editionStatus?: EditionStatus; - isDowngrade?: boolean; - onInstall: (edition: Edition) => void; - onUninstall: () => void; + onAction: (edition: Edition) => void; } export default class EditionBox extends React.PureComponent { - handleInstall = () => this.props.onInstall(this.props.edition); - - renderActions(isInstalled?: boolean, installInProgress?: boolean) { - const { canInstall, canUninstall, editionStatus } = this.props; - const uninstallInProgress = - editionStatus && editionStatus.installationStatus === 'UNINSTALL_IN_PROGRESS'; - - if (canInstall && !isInstalled) { - return ( - - ); - } - if (canUninstall && isInstalled) { - return ( - - ); - } - - return null; - } + handleAction = () => this.props.onAction(this.props.edition); render() { - const { edition, editionStatus } = this.props; - const isInstalled = editionStatus && editionStatus.currentEditionKey === edition.key; - const installInProgress = - editionStatus && - ['AUTOMATIC_IN_PROGRESS', 'AUTOMATIC_READY'].includes(editionStatus.installationStatus); + const { disableAction, displayAction, edition, editionStatus } = this.props; return (
{editionStatus && } @@ -82,7 +47,11 @@ export default class EditionBox extends React.PureComponent { {translate('marketplace.learn_more')} - {this.renderActions(isInstalled, installInProgress)} + {displayAction && ( + + )}
); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx index 47b3e767fe2..e58c8edf24a 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx @@ -28,25 +28,33 @@ interface Props { } export default function EditionBoxBadge({ editionKey, status }: Props) { - const inProgress = ['AUTOMATIC_IN_PROGRESS', 'AUTOMATIC_READY'].includes( - status.installationStatus - ); - const isInstalling = inProgress && status.nextEditionKey === editionKey; + const isInstalled = + status.currentEditionKey === editionKey || + (!status.currentEditionKey && editionKey === 'community'); + const isProgressing = + status.nextEditionKey === editionKey || (!status.nextEditionKey && editionKey === 'community'); + const inProgressStatus = [ + 'AUTOMATIC_READY', + 'AUTOMATIC_IN_PROGRESS', + 'UNINSTALL_IN_PROGRESS' + ].includes(status.installationStatus); - if (isInstalling) { - const installReady = status.installationStatus === 'AUTOMATIC_READY'; + if (inProgressStatus) { + if (isProgressing) { + return ( + + {status.installationStatus === 'AUTOMATIC_IN_PROGRESS' ? ( + translate('marketplace.installing') + ) : ( + translate('marketplace.pending') + )} + + ); + } + } else if (isInstalled) { return ( - - {installReady ? translate('marketplace.pending') : translate('marketplace.installing')} - - ); - } - - const isInstalled = status.currentEditionKey === editionKey; - if (isInstalled) { - return ( - - + + {translate('marketplace.installed')} ); diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx index 74e2874c1a0..7ef5650c19f 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx @@ -95,7 +95,7 @@ export default class LicenseEditionSet extends React.PureComponent }; getLicenseFormUrl = (edition: Edition) => { - let url = edition.requestUrl; + let url = edition.licenseRequestUrl; if (this.state.formData) { const query = stringify(omitNil(this.state.formData)); if (query) { diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/PluginAvailable.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/PluginAvailable.tsx index aabb86e0f6d..cbae64fd069 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/PluginAvailable.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/PluginAvailable.tsx @@ -48,7 +48,7 @@ export default function PluginAvailable({