diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2018-10-22 16:23:54 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-11-16 20:21:04 +0100 |
commit | 9a64997f8446afe86429895b3795b2d61a448221 (patch) | |
tree | b1209ba93de6600dc3012ba869e91f5ce1182f03 /server/sonar-web/src/main/js | |
parent | 07546d5e1f4047a1030a91d0ffaa39fb96e66a41 (diff) | |
download | sonarqube-9a64997f8446afe86429895b3795b2d61a448221.tar.gz sonarqube-9a64997f8446afe86429895b3795b2d61a448221.zip |
SONAR-11321 Retry up to 5 times to get the alm organization
Diffstat (limited to 'server/sonar-web/src/main/js')
9 files changed, 99 insertions, 23 deletions
diff --git a/server/sonar-web/src/main/js/api/alm-integration.ts b/server/sonar-web/src/main/js/api/alm-integration.ts index 568eb2407ff..9b9df940713 100644 --- a/server/sonar-web/src/main/js/api/alm-integration.ts +++ b/server/sonar-web/src/main/js/api/alm-integration.ts @@ -29,16 +29,30 @@ export function getAlmAppInfo(): Promise<{ application: AlmApplication }> { return getJSON('/api/alm_integration/show_app_info').catch(throwGlobalError); } -export function getAlmOrganization(data: { installationId: string }): Promise<AlmOrganization> { - return getJSON('/api/alm_integration/show_organization', data).then( - ({ organization }) => ({ - ...organization, - name: organization.name || organization.key - }), - throwGlobalError +function fetchAlmOrganization(data: { installationId: string }, remainingTries: number) { + return getJSON('/api/alm_integration/show_organization', data).catch( + (error: { response: Response }) => { + remainingTries--; + if (error.response.status === 404) { + if (remainingTries > 0) { + return new Promise(resolve => { + setTimeout(() => resolve(fetchAlmOrganization(data, remainingTries)), 500); + }); + } + return Promise.reject(); + } + return throwGlobalError(error); + } ); } +export function getAlmOrganization(data: { installationId: string }): Promise<AlmOrganization> { + return fetchAlmOrganization(data, 5).then(({ organization }) => ({ + ...organization, + name: organization.name || organization.key + })); +} + export function getRepositories(data: { organization: string; }): Promise<{ repositories: AlmRepository[] }> { diff --git a/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx b/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx new file mode 100644 index 00000000000..72c6dc0197d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/organization/AlmApplicationInstalling.tsx @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { translate } from '../../../helpers/l10n'; + +export default function AlmApplicationInstalling({ almKey }: { almKey?: string }) { + return ( + <div className="sonarcloud page page-limited"> + <div className="huge-spacer-top text-center"> + <i className="spinner" /> + <p className="big-spacer-top"> + {almKey + ? translate('onboarding.import_organization.installing', almKey) + : translate('onboarding.import_organization.installing')} + </p> + </div> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx index 602d3139d47..96318218442 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx @@ -35,7 +35,7 @@ export default class ChooseRemoteOrganizationStep extends React.PureComponent<Pr return ( <div className="boxed-group-inner"> {almInstallId && ( - <Alert className="markdown big-spacer-bottom width-60" variant="warning"> + <Alert className="markdown big-spacer-bottom width-60" variant="error"> {translate('onboarding.create_organization.import_org_not_found')} <ul> <li>{translate('onboarding.create_organization.import_org_not_found.tips_1')}</li> diff --git a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx index 17c0e3a83ee..f3301cf3e6b 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx @@ -25,6 +25,7 @@ import { Helmet } from 'react-helmet'; import { FormattedMessage } from 'react-intl'; import { Link, withRouter, WithRouterProps } from 'react-router'; import { formatPrice, parseQuery } from './utils'; +import AlmApplicationInstalling from './AlmApplicationInstalling'; import AutoOrganizationCreate from './AutoOrganizationCreate'; import ManualOrganizationCreate from './ManualOrganizationCreate'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; @@ -68,6 +69,7 @@ interface Props { interface State { almApplication?: AlmApplication; almOrganization?: AlmOrganization; + almOrgLoading: boolean; loading: boolean; organization?: Organization; subscriptionPlans?: SubscriptionPlan[]; @@ -82,7 +84,7 @@ interface LocationState { export class CreateOrganization extends React.PureComponent<Props & WithRouterProps, State> { mounted = false; - state: State = { loading: true }; + state: State = { almOrgLoading: false, loading: true }; componentDidMount() { this.mounted = true; @@ -96,7 +98,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr const query = parseQuery(this.props.location.query); if (query.almInstallId) { - initRequests.push(this.fetchAlmOrganization(query.almInstallId)); + this.fetchAlmOrganization(query.almInstallId); } } Promise.all(initRequests).then(this.stopLoading, this.stopLoading); @@ -116,11 +118,19 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr }; fetchAlmOrganization = (installationId: string) => { - return getAlmOrganization({ installationId }).then(almOrganization => { - if (this.mounted) { - this.setState({ almOrganization }); + this.setState({ almOrgLoading: true }); + return getAlmOrganization({ installationId }).then( + almOrganization => { + if (this.mounted) { + this.setState({ almOrganization, almOrgLoading: false }); + } + }, + () => { + if (this.mounted) { + this.setState({ almOrgLoading: false }); + } } - }); + ); }; fetchSubscriptionPlans = () => { @@ -158,9 +168,14 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr render() { const { currentUser, location } = this.props; - const { almApplication, almOrganization, loading, subscriptionPlans } = this.state; const state = (location.state || {}) as LocationState; const query = parseQuery(location.query); + + if (this.state.almOrgLoading) { + return <AlmApplicationInstalling almKey={query.almKey} />; + } + + const { almApplication, almOrganization, subscriptionPlans } = this.state; const importPersonalOrg = isPersonal(almOrganization) ? this.props.userOrganizations.find(o => o.key === currentUser.personalOrganization) : undefined; @@ -172,7 +187,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr : translate('onboarding.create_organization.page.description'); const startedPrice = subscriptionPlans && subscriptionPlans[0] && subscriptionPlans[0].price; const formattedPrice = formatPrice(startedPrice); - const showManualTab = state.tab === 'manual' && !query.almInstallId; + const showManualTab = state.tab === 'manual' && !almOrganization; return ( <> @@ -198,8 +213,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr </p> )} </header> - - {loading ? ( + {this.state.loading ? ( <DeferredSpinner /> ) : ( <> @@ -224,7 +238,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr ) }, { - disabled: Boolean(query.almInstallId), + disabled: Boolean(almOrganization), key: 'manual', node: translate('onboarding.create_organization.create_manually') } diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx index 6721e089b15..c11537a0ef4 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx @@ -25,7 +25,7 @@ it('should render', () => { expect(shallowRender()).toMatchSnapshot(); }); -it('should display a warning message', () => { +it('should display an alert message', () => { expect(shallowRender({ almInstallId: 'foo' }).find('Alert')).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx index 330f8062730..7db9ebf2636 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx @@ -84,6 +84,7 @@ it('should render with auto tab selected and manual disabled', async () => { currentUser: { ...user, externalProvider: 'github' }, location: { query: { installation_id: 'foo' } } as Location // eslint-disable-line camelcase }); + expect(wrapper).toMatchSnapshot(); await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap index 3a79e945db1..227a1be6a19 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should display a warning message 1`] = ` +exports[`should display an alert message 1`] = ` <Alert className="markdown big-spacer-bottom width-60" - variant="warning" + variant="error" > onboarding.create_organization.import_org_not_found <ul> diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap index c25c32f3e2c..9d3dcf6dbc8 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap @@ -83,6 +83,12 @@ exports[`should render with auto tab displayed 1`] = ` `; exports[`should render with auto tab selected and manual disabled 1`] = ` +<AlmApplicationInstalling + almKey="github" +/> +`; + +exports[`should render with auto tab selected and manual disabled 2`] = ` <Fragment> <HelmetWrapper defer={true} diff --git a/server/sonar-web/src/main/js/apps/create/organization/utils.ts b/server/sonar-web/src/main/js/apps/create/organization/utils.ts index bfe1825632a..71795b85c78 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/utils.ts +++ b/server/sonar-web/src/main/js/apps/create/organization/utils.ts @@ -31,6 +31,7 @@ export function formatPrice(price?: number, noSign?: boolean) { export interface Query { almInstallId?: string; + almKey?: string; } export const parseQuery = memoize( @@ -38,7 +39,11 @@ export const parseQuery = memoize( return { almInstallId: parseAsOptionalString(urlQuery['installation_id']) || - parseAsOptionalString(urlQuery['clientKey']) + parseAsOptionalString(urlQuery['clientKey']), + almKey: + (urlQuery['installation_id'] && 'github') || + (urlQuery['clientKey'] && 'bitbucket') || + undefined }; } ); |