]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20086 Updating Azure PAT form to use custom hook
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Fri, 11 Aug 2023 14:16:42 +0000 (16:16 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 14 Aug 2023 20:02:58 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/create/project/Azure/AzurePersonalAccessTokenForm.tsx
server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreateRenderer.tsx

index 3f71739e892474703122040bac51643449c222af..55a12b730d419925c0da3e57f2db54c92a491de6 100644 (file)
@@ -31,51 +31,52 @@ import * as React from 'react';
 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>
@@ -112,12 +113,9 @@ export default function AzurePersonalAccessTokenForm(props: AzurePersonalAccessT
             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}
           />
index 3dfad9a6ed9909883e0d45f34609f2e95045976e..1ed1577464c377651e34c398a8f7456670aec22e 100644 (file)
  */
 import * as React from 'react';
 import {
-  checkPersonalAccessTokenIsValid,
   getAzureProjects,
   getAzureRepositories,
   searchAzureRepositories,
-  setAlmPersonalAccessToken,
   setupAzureProjectCreation,
 } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
@@ -31,7 +29,6 @@ import { AzureProject, AzureRepository } from '../../../../types/alm-integration
 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 {
@@ -46,16 +43,13 @@ 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> {
@@ -66,10 +60,9 @@ 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,
     };
   }
 
@@ -93,45 +86,48 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
   }
 
   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,
+          };
+        });
+      }
     }
   };
 
@@ -224,53 +220,20 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
     }
   };
 
-  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 */
@@ -284,16 +247,13 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
     const {
       loading,
       loadingRepositories,
-      patIsValid,
+      showPersonalAccessTokenForm,
       projects,
       repositories,
       searching,
       searchResults,
       searchQuery,
       selectedAlmInstance,
-      submittingToken,
-      tokenValidationFailed,
-      firstConnection,
     } = this.state;
 
     return (
@@ -312,11 +272,11 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
         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}
       />
     );
   }
index a6e4fb2ef26a16c008c8f40aedd958857a705658..94c4ec746cd9fcaa22c229cf9956a6cb62a500fa 100644 (file)
@@ -45,7 +45,7 @@ export interface AzureProjectCreateRendererProps {
   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[]>;
@@ -55,10 +55,8 @@ export interface AzureProjectCreateRendererProps {
   almInstances?: AlmSettingsInstance[];
   selectedAlmInstance?: AlmSettingsInstance;
   showPersonalAccessTokenForm?: boolean;
-  submittingToken?: boolean;
-  tokenValidationFailed: boolean;
+  resetPat: boolean;
   onSelectedAlmInstanceChange: (instance: AlmSettingsInstance) => void;
-  firstConnection?: boolean;
 }
 
 export default function AzureProjectCreateRenderer(props: AzureProjectCreateRendererProps) {
@@ -73,10 +71,8 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
     searchQuery,
     almInstances,
     showPersonalAccessTokenForm,
-    submittingToken,
-    tokenValidationFailed,
+    resetPat,
     selectedAlmInstance,
-    firstConnection,
   } = props;
 
   const showCountError = !loading && (!almInstances || almInstances?.length === 0);
@@ -131,9 +127,7 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
             <AzurePersonalAccessTokenForm
               almSetting={selectedAlmInstance}
               onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
-              submitting={submittingToken}
-              validationFailed={tokenValidationFailed}
-              firstConnection={firstConnection}
+              resetPat={resetPat}
             />
           </div>
         ) : (