diff options
author | guillaume-peoch-sonarsource <guillaume.peoch@sonarsource.com> | 2023-01-10 17:52:05 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-01-13 20:02:47 +0000 |
commit | 1d92a9229b3c0a7c2fe8548087c2ff71512bb999 (patch) | |
tree | ac391ef1e81540a0b7a6a03245fe37cc2cdda57d /server/sonar-web/src/main/js/apps | |
parent | ea602214bb0e0ae1a79b381c8e12a424d58b085c (diff) | |
download | sonarqube-1d92a9229b3c0a7c2fe8548087c2ff71512bb999.tar.gz sonarqube-1d92a9229b3c0a7c2fe8548087c2ff71512bb999.zip |
SONAR-13109 Users should understand why they're asked for their personal access token
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
6 files changed, 73 insertions, 6 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/project/AzurePersonalAccessTokenForm.tsx b/server/sonar-web/src/main/js/apps/create/project/AzurePersonalAccessTokenForm.tsx index 6fad0fc51f9..24a81c34506 100644 --- a/server/sonar-web/src/main/js/apps/create/project/AzurePersonalAccessTokenForm.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/AzurePersonalAccessTokenForm.tsx @@ -23,6 +23,7 @@ import { FormattedMessage } from 'react-intl'; import Link from '../../../components/common/Link'; import { SubmitButton } from '../../../components/controls/buttons'; import ValidationInput from '../../../components/controls/ValidationInput'; +import { Alert } from '../../../components/ui/Alert'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; import { AlmSettingsInstance } from '../../../types/alm-settings'; @@ -32,6 +33,7 @@ export interface AzurePersonalAccessTokenFormProps { onPersonalAccessTokenCreate: (token: string) => void; submitting?: boolean; validationFailed: boolean; + firstConnection?: boolean; } function getAzurePatUrl(url: string) { @@ -43,6 +45,7 @@ export default function AzurePersonalAccessTokenForm(props: AzurePersonalAccessT almSetting: { alm, url }, submitting = false, validationFailed, + firstConnection, } = props; const [touched, setTouched] = React.useState(false); @@ -87,6 +90,13 @@ export default function AzurePersonalAccessTokenForm(props: AzurePersonalAccessT /> </div> + {!firstConnection && ( + <Alert className="big-spacer-right" variant="warning"> + <p>{translate('onboarding.create_project.pat.expired.info_message')}</p> + <p>{translate('onboarding.create_project.pat.expired.info_message_contact')}</p> + </Alert> + )} + <form onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => { e.preventDefault(); diff --git a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx index 36adf6d3a78..94a98a331ed 100644 --- a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx @@ -31,6 +31,7 @@ import { AzureProject, AzureRepository } from '../../../types/alm-integration'; import { AlmSettingsInstance } from '../../../types/alm-settings'; import { Dict } from '../../../types/types'; import AzureCreateProjectRenderer from './AzureProjectCreateRenderer'; +import { tokenExistedBefore } from './utils'; interface Props { canAdmin: boolean; @@ -55,6 +56,7 @@ interface State { selectedAlmInstance?: AlmSettingsInstance; submittingToken?: boolean; tokenValidationFailed: boolean; + firstConnection?: boolean; } export default class AzureProjectCreate extends React.PureComponent<Props, State> { @@ -71,6 +73,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State loadingRepositories: {}, repositories: {}, tokenValidationFailed: false, + firstConnection: false, }; } @@ -92,7 +95,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State fetchData = async () => { this.setState({ loading: true }); - const patIsValid = await this.checkPersonalAccessToken().catch(() => false); + const { patIsValid, error } = await this.checkPersonalAccessToken(); let projects: AzureProject[] | undefined; if (patIsValid) { @@ -126,6 +129,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State loadingRepositories: { ...loadingRepositories }, projects, repositories, + firstConnection: tokenExistedBefore(error), }; }); } @@ -239,10 +243,12 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State const { selectedAlmInstance } = this.state; if (!selectedAlmInstance) { - return Promise.resolve(false); + return Promise.resolve({ patIsValid: false, error: '' }); } - return checkPersonalAccessTokenIsValid(selectedAlmInstance.key).then(({ status }) => status); + return checkPersonalAccessTokenIsValid(selectedAlmInstance.key).then(({ status, error }) => { + return { patIsValid: status, error }; + }); }; handlePersonalAccessTokenCreate = async (token: string) => { @@ -256,10 +262,14 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State try { await setAlmPersonalAccessToken(selectedAlmInstance.key, token); - const patIsValid = await this.checkPersonalAccessToken(); + const { patIsValid } = await this.checkPersonalAccessToken(); if (this.mounted) { - this.setState({ submittingToken: false, patIsValid, tokenValidationFailed: !patIsValid }); + this.setState({ + submittingToken: false, + patIsValid, + tokenValidationFailed: !patIsValid, + }); if (patIsValid) { this.cleanUrl(); @@ -296,6 +306,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State selectedAlmInstance, submittingToken, tokenValidationFailed, + firstConnection, } = this.state; return ( @@ -321,6 +332,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State submittingToken={submittingToken} tokenValidationFailed={tokenValidationFailed} onSelectedAlmInstanceChange={this.onSelectedAlmInstanceChange} + firstConnection={firstConnection} /> ); } diff --git a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx index 6ce5722a56f..d3ad3e456b6 100644 --- a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx @@ -59,6 +59,7 @@ export interface AzureProjectCreateRendererProps { submittingToken?: boolean; tokenValidationFailed: boolean; onSelectedAlmInstanceChange: (instance: AlmSettingsInstance) => void; + firstConnection?: boolean; } export default function AzureProjectCreateRenderer(props: AzureProjectCreateRendererProps) { @@ -78,6 +79,7 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend submittingToken, tokenValidationFailed, selectedAlmInstance, + firstConnection, } = props; const showCountError = !loading && (!almInstances || almInstances?.length === 0); @@ -157,6 +159,7 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate} submitting={submittingToken} validationFailed={tokenValidationFailed} + firstConnection={firstConnection} /> </div> ) : ( diff --git a/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx b/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx index 68a478709b6..589b8c72264 100644 --- a/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx @@ -31,6 +31,7 @@ import DeferredSpinner from '../../../components/ui/DeferredSpinner'; import { translate } from '../../../helpers/l10n'; import { getBaseUrl } from '../../../helpers/system'; import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings'; +import { tokenExistedBefore } from './utils'; interface Props { almSetting: AlmSettingsInstance; @@ -46,6 +47,7 @@ interface State { username?: string; submitting: boolean; checkingPat: boolean; + firstConnection: boolean; } function getPatUrl(alm: AlmKeys, url = '') { @@ -72,6 +74,7 @@ export default class PersonalAccessTokenForm extends React.PureComponent<Props, password: '', submitting: false, validationFailed: false, + firstConnection: false, }; } @@ -107,9 +110,10 @@ export default class PersonalAccessTokenForm extends React.PureComponent<Props, } if (this.mounted) { // This is the initial message when no token was provided - if (error === `personal access token for '${key}' is missing`) { + if (tokenExistedBefore(error)) { this.setState({ checkingPat: false, + firstConnection: true, }); } else { this.setState({ @@ -329,6 +333,7 @@ export default class PersonalAccessTokenForm extends React.PureComponent<Props, username, validationFailed, validationErrorMessage, + firstConnection, } = this.state; if (checkingPat) { @@ -352,6 +357,13 @@ export default class PersonalAccessTokenForm extends React.PureComponent<Props, {translate('onboarding.create_project.pat_form.help', alm)} </p> + {!firstConnection && ( + <Alert className="big-spacer-right" variant="warning"> + <p>{translate('onboarding.create_project.pat.expired.info_message')}</p> + <p>{translate('onboarding.create_project.pat.expired.info_message_contact')}</p> + </Alert> + )} + {alm === AlmKeys.BitbucketCloud && ( <ValidationInput error={undefined} diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx index 60cbc8eb988..35ea5813023 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketCloud-it.tsx @@ -76,6 +76,13 @@ it('should ask for PAT when it is not set yet and show the import project featur ) ).toBeInTheDocument(); + expect( + screen.getByText('onboarding.create_project.pat.expired.info_message') + ).toBeInTheDocument(); + expect( + screen.getByText('onboarding.create_project.pat.expired.info_message_contact') + ).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'save' })).toBeDisabled(); await user.click( diff --git a/server/sonar-web/src/main/js/apps/create/project/utils.ts b/server/sonar-web/src/main/js/apps/create/project/utils.ts new file mode 100644 index 00000000000..6725280145e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/project/utils.ts @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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. + */ + +export function tokenExistedBefore(error?: string) { + return error?.includes('is missing'); +} |