import { FormattedMessage } from 'react-intl';
import { translate } from '../../../../helpers/l10n';
import { AlmSettingsInstance } from '../../../../types/alm-settings';
+import { usePersonalAccessToken } from '../usePersonalAccessToken';
export interface AzurePersonalAccessTokenFormProps {
almSetting: AlmSettingsInstance;
- onPersonalAccessTokenCreate: (token: string) => void;
- submitting?: boolean;
- validationFailed: boolean;
- firstConnection?: boolean;
+ onPersonalAccessTokenCreate: () => void;
+ resetPat: boolean;
}
function getAzurePatUrl(url: string) {
return `${url.replace(/\/$/, '')}/_usersSettings/tokens`;
}
-export default function AzurePersonalAccessTokenForm(props: AzurePersonalAccessTokenFormProps) {
+export default function AzurePersonalAccessTokenForm({
+ almSetting,
+ resetPat,
+ onPersonalAccessTokenCreate,
+}: AzurePersonalAccessTokenFormProps) {
const {
- almSetting: { url },
- submitting = false,
- validationFailed,
+ password,
firstConnection,
- } = props;
-
- const [touched, setTouched] = React.useState(false);
- React.useEffect(() => {
- setTouched(false);
- }, [submitting]);
+ validationFailed,
+ touched,
+ submitting,
+ validationErrorMessage,
+ checkingPat,
+ handlePasswordChange,
+ handleSubmit,
+ } = usePersonalAccessToken(almSetting, resetPat, onPersonalAccessTokenCreate);
- const [token, setToken] = React.useState('');
+ if (checkingPat) {
+ return <DeferredSpinner className="sw-ml-2" loading />;
+ }
- const isInvalid = (validationFailed && !touched) || (touched && !token);
+ const isInvalid = (validationFailed && !touched) || (touched && !password);
+ const { url } = almSetting;
let errorMessage;
- if (!token) {
+ if (!password) {
errorMessage = translate('onboarding.create_project.pat_form.pat_required');
} else if (isInvalid) {
- errorMessage = translate('onboarding.create_project.pat_incorrect.azure');
+ errorMessage =
+ validationErrorMessage ?? translate('onboarding.create_project.pat_incorrect.azure');
}
return (
- <form
- className="sw-mt-3 sw-w-[50%]"
- onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
- e.preventDefault();
- props.onPersonalAccessTokenCreate(token);
- }}
- >
+ <form className="sw-mt-3 sw-w-[50%]" onSubmit={handleSubmit}>
<LightPrimary as="h2" className="sw-heading-md">
{translate('onboarding.create_project.pat_form.title')}
</LightPrimary>
id="personal_access_token"
minLength={1}
name="personal_access_token"
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
- setToken(e.target.value);
- setTouched(true);
- }}
+ onChange={handlePasswordChange}
type="text"
- value={token}
+ value={password}
size="large"
isInvalid={isInvalid}
/>
*/
import * as React from 'react';
import {
- checkPersonalAccessTokenIsValid,
getAzureProjects,
getAzureRepositories,
searchAzureRepositories,
- setAlmPersonalAccessToken,
setupAzureProjectCreation,
} from '../../../../api/alm-integrations';
import { Location, Router } from '../../../../components/hoc/withRouter';
import { AlmSettingsInstance } from '../../../../types/alm-settings';
import { Dict } from '../../../../types/types';
import { CreateProjectApiCallback } from '../types';
-import { tokenExistedBefore } from '../utils';
import AzureCreateProjectRenderer from './AzureProjectCreateRenderer';
interface Props {
interface State {
loading: boolean;
loadingRepositories: Dict<boolean>;
- patIsValid?: boolean;
projects?: AzureProject[];
repositories: Dict<AzureRepository[]>;
searching?: boolean;
searchResults?: AzureRepository[];
searchQuery?: string;
selectedAlmInstance?: AlmSettingsInstance;
- submittingToken?: boolean;
- tokenValidationFailed: boolean;
- firstConnection?: boolean;
+ showPersonalAccessTokenForm: boolean;
}
export default class AzureProjectCreate extends React.PureComponent<Props, State> {
this.state = {
selectedAlmInstance: props.almInstances[0],
loading: false,
+ showPersonalAccessTokenForm: true,
loadingRepositories: {},
repositories: {},
- tokenValidationFailed: false,
- firstConnection: false,
};
}
}
fetchData = async () => {
- this.setState({ loading: true });
-
- const { patIsValid, error } = await this.checkPersonalAccessToken();
-
- let projects: AzureProject[] | undefined;
- if (patIsValid) {
- projects = await this.fetchAzureProjects();
- }
-
- const { repositories } = this.state;
+ const { showPersonalAccessTokenForm } = this.state;
+
+ if (!showPersonalAccessTokenForm) {
+ this.setState({ loading: true });
+ let projects: AzureProject[] | undefined;
+ try {
+ projects = await this.fetchAzureProjects();
+ } catch (_) {
+ if (this.mounted) {
+ this.setState({ showPersonalAccessTokenForm: true, loading: false });
+ }
+ }
- let firstProjectName: string;
+ const { repositories } = this.state;
- if (projects && projects.length > 0) {
- firstProjectName = projects[0].name;
+ let firstProjectName: string;
- this.setState(({ loadingRepositories }) => ({
- loadingRepositories: { ...loadingRepositories, [firstProjectName]: true },
- }));
+ if (projects && projects.length > 0) {
+ firstProjectName = projects[0].name;
- const repos = await this.fetchAzureRepositories(firstProjectName);
- repositories[firstProjectName] = repos;
- }
+ this.setState(({ loadingRepositories }) => ({
+ loadingRepositories: { ...loadingRepositories, [firstProjectName]: true },
+ }));
- if (this.mounted) {
- this.setState(({ loadingRepositories }) => {
- if (firstProjectName) {
- loadingRepositories[firstProjectName] = false;
- }
+ const repos = await this.fetchAzureRepositories(firstProjectName);
+ repositories[firstProjectName] = repos;
+ }
- return {
- patIsValid,
- loading: false,
- loadingRepositories: { ...loadingRepositories },
- projects,
- repositories,
- firstConnection: tokenExistedBefore(error),
- };
- });
+ if (this.mounted) {
+ this.setState(({ loadingRepositories }) => {
+ if (firstProjectName !== '') {
+ loadingRepositories[firstProjectName] = false;
+ }
+
+ return {
+ loading: false,
+ loadingRepositories: { ...loadingRepositories },
+ projects,
+ repositories,
+ };
+ });
+ }
}
};
}
};
- checkPersonalAccessToken = () => {
- const { selectedAlmInstance } = this.state;
-
- if (!selectedAlmInstance) {
- return Promise.resolve({ patIsValid: false, error: '' });
- }
-
- return checkPersonalAccessTokenIsValid(selectedAlmInstance.key).then(({ status, error }) => {
- return { patIsValid: status, error };
- });
- };
-
- handlePersonalAccessTokenCreate = async (token: string) => {
- const { selectedAlmInstance } = this.state;
-
- if (!selectedAlmInstance || token.length < 1) {
- return;
- }
-
- this.setState({ submittingToken: true, tokenValidationFailed: false });
-
- try {
- await setAlmPersonalAccessToken(selectedAlmInstance.key, token);
- const { patIsValid } = await this.checkPersonalAccessToken();
-
- if (this.mounted) {
- this.setState({
- submittingToken: false,
- patIsValid,
- tokenValidationFailed: !patIsValid,
- });
-
- if (patIsValid) {
- this.cleanUrl();
- this.fetchData();
- }
- }
- } catch (e) {
- if (this.mounted) {
- this.setState({ submittingToken: false });
- }
- }
+ handlePersonalAccessTokenCreate = async () => {
+ this.setState({ showPersonalAccessTokenForm: false });
+ this.cleanUrl();
+ await this.fetchData();
};
onSelectedAlmInstanceChange = (instance: AlmSettingsInstance) => {
this.setState(
- { selectedAlmInstance: instance, searchResults: undefined, searchQuery: '' },
+ {
+ selectedAlmInstance: instance,
+ searchResults: undefined,
+ searchQuery: '',
+ showPersonalAccessTokenForm: true,
+ },
() => {
this.fetchData().catch(() => {
/* noop */
const {
loading,
loadingRepositories,
- patIsValid,
+ showPersonalAccessTokenForm,
projects,
repositories,
searching,
searchResults,
searchQuery,
selectedAlmInstance,
- submittingToken,
- tokenValidationFailed,
- firstConnection,
} = this.state;
return (
searchQuery={searchQuery}
almInstances={almInstances}
selectedAlmInstance={selectedAlmInstance}
- showPersonalAccessTokenForm={!patIsValid || Boolean(location.query.resetPat)}
- submittingToken={submittingToken}
- tokenValidationFailed={tokenValidationFailed}
+ resetPat={Boolean(location.query.resetPat)}
+ showPersonalAccessTokenForm={
+ showPersonalAccessTokenForm || Boolean(location.query.resetPat)
+ }
onSelectedAlmInstanceChange={this.onSelectedAlmInstanceChange}
- firstConnection={firstConnection}
/>
);
}
loadingRepositories: Dict<boolean>;
onImportRepository: (resository: AzureRepository) => void;
onOpenProject: (key: string) => void;
- onPersonalAccessTokenCreate: (token: string) => void;
+ onPersonalAccessTokenCreate: () => void;
onSearch: (query: string) => void;
projects?: AzureProject[];
repositories: Dict<AzureRepository[]>;
almInstances?: AlmSettingsInstance[];
selectedAlmInstance?: AlmSettingsInstance;
showPersonalAccessTokenForm?: boolean;
- submittingToken?: boolean;
- tokenValidationFailed: boolean;
+ resetPat: boolean;
onSelectedAlmInstanceChange: (instance: AlmSettingsInstance) => void;
- firstConnection?: boolean;
}
export default function AzureProjectCreateRenderer(props: AzureProjectCreateRendererProps) {
searchQuery,
almInstances,
showPersonalAccessTokenForm,
- submittingToken,
- tokenValidationFailed,
+ resetPat,
selectedAlmInstance,
- firstConnection,
} = props;
const showCountError = !loading && (!almInstances || almInstances?.length === 0);
<AzurePersonalAccessTokenForm
almSetting={selectedAlmInstance}
onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
- submitting={submittingToken}
- validationFailed={tokenValidationFailed}
- firstConnection={firstConnection}
+ resetPat={resetPat}
/>
</div>
) : (