]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20708 Handle failed imports
authorViktor Vorona <viktor.vorona@sonarsource.com>
Wed, 18 Oct 2023 11:33:44 +0000 (13:33 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 18 Oct 2023 20:03:05 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/create/project/components/AlmRepoItem.tsx
server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index f604a644227bb8579998af228066599463879739..efbb949f15ba5c3ca1efda3dccac0e897143cbca 100644 (file)
@@ -92,7 +92,7 @@ export default function AlmRepoItem({
         />
       )}
       <div className="sw-w-[70%] sw-min-w-0 sw-flex sw-mr-1">
-        <div id={labelId} className="sw-max-w-[50%] sw-flex sw-items-center">
+        <div id={labelId} className="sw-max-w-full sw-flex sw-items-center">
           <img
             alt="" // Should be ignored by screen readers
             className="sw-h-4 sw-w-4 sw-mr-2"
@@ -110,7 +110,7 @@ export default function AlmRepoItem({
             </LightPrimary>
           )}
         </div>
-        <div className="sw-max-w-[50%] sw-min-w-0 sw-ml-2 sw-flex sw-items-center">
+        <div className="sw-max-w-full sw-min-w-0 sw-ml-2 sw-flex sw-items-center">
           <LightLabel className="sw-body-sm sw-truncate">{secondaryTextNode}</LightLabel>
         </div>
       </div>
index 521fb01226c38ab2f64241844073ebf910a7cd6a..ad08108944f3d9ffea4aa4e883c0439206ae03a9 100644 (file)
@@ -25,7 +25,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
 import { useNavigate, unstable_usePrompt as usePrompt } from 'react-router-dom';
 import NewCodeDefinitionSelector from '../../../../components/new-code-definition/NewCodeDefinitionSelector';
 import { useDocUrl } from '../../../../helpers/docs';
-import { addGlobalSuccessMessage } from '../../../../helpers/globalMessages';
+import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../../../helpers/globalMessages';
 import { translate } from '../../../../helpers/l10n';
 import { getProjectUrl, queryToSearch } from '../../../../helpers/urls';
 import {
@@ -48,13 +48,15 @@ export default function NewCodeDefinitionSelection(props: Props) {
   const { importProjects } = props;
 
   const [selectedDefinition, selectDefinition] = React.useState<NewCodeDefinitiondWithCompliance>();
-  const { mutate, isLoading, data, reset } = useImportProjectMutation();
+  const [failedImports, setFailedImports] = React.useState<number>(0);
+  const { mutateAsync, data, reset, isIdle } = useImportProjectMutation();
   const mutateCount = useImportProjectProgress();
+  const isImporting = mutateCount > 0;
   const intl = useIntl();
   const navigate = useNavigate();
   const getDocUrl = useDocUrl();
   usePrompt({
-    when: isLoading,
+    when: isImporting,
     message: translate('onboarding.create_project.please_dont_leave'),
   });
 
@@ -62,7 +64,7 @@ export default function NewCodeDefinitionSelection(props: Props) {
   const isMultipleProjects = projectCount > 1;
 
   useEffect(() => {
-    if (mutateCount > 0 || !data) {
+    if (mutateCount > 0 || isIdle) {
       return;
     }
     reset();
@@ -70,28 +72,40 @@ export default function NewCodeDefinitionSelection(props: Props) {
       intl.formatMessage(
         { id: 'onboarding.create_project.success' },
         {
-          count: projectCount,
+          count: projectCount - failedImports,
         },
       ),
     );
+    if (failedImports > 0) {
+      addGlobalErrorMessage(
+        intl.formatMessage(
+          { id: 'onboarding.create_project.failure' },
+          {
+            count: failedImports,
+          },
+        ),
+      );
+    }
 
     if (projectCount === 1) {
-      navigate(getProjectUrl(data.project.key));
+      if (data) {
+        navigate(getProjectUrl(data.project.key));
+      }
     } else {
       navigate({
         pathname: '/projects',
         search: queryToSearch({ sort: '-creation_date' }),
       });
     }
-  }, [data, projectCount, mutateCount, reset, intl, navigate]);
+  }, [data, projectCount, failedImports, mutateCount, reset, intl, navigate, isIdle]);
 
   React.useEffect(() => {
-    if (isLoading) {
+    if (isImporting) {
       window.addEventListener('beforeunload', listener);
     }
 
     return () => window.removeEventListener('beforeunload', listener);
-  }, [isLoading]);
+  }, [isImporting]);
 
   const handleProjectCreation = () => {
     if (selectedDefinition) {
@@ -101,10 +115,12 @@ export default function NewCodeDefinitionSelection(props: Props) {
           ...omit(importProjects, 'projects'),
           ...p,
         } as MutationArg;
-        mutate({
+        mutateAsync({
           newCodeDefinitionType: selectedDefinition.type,
           newCodeDefinitionValue: selectedDefinition.value,
           ...arg,
+        }).catch(() => {
+          setFailedImports((prev) => prev + 1);
         });
       });
     }
@@ -151,7 +167,7 @@ export default function NewCodeDefinitionSelection(props: Props) {
         <ButtonSecondary onClick={() => navigate(-1)}>{translate('back')}</ButtonSecondary>
         <ButtonPrimary
           onClick={handleProjectCreation}
-          disabled={!selectedDefinition?.isCompliant || isLoading}
+          disabled={!selectedDefinition?.isCompliant || isImporting}
           type="submit"
         >
           <FormattedMessage
@@ -163,9 +179,9 @@ export default function NewCodeDefinitionSelection(props: Props) {
               count: projectCount,
             }}
           />
-          <Spinner className="sw-ml-2" loading={isLoading} />
+          <Spinner className="sw-ml-2" loading={isImporting} />
         </ButtonPrimary>
-        {isLoading && (
+        {isImporting && projectCount > 1 && (
           <FlagMessage variant="warning">
             <FormattedMessage
               id="onboarding.create_project.import_in_progress"
index 578af9a8927732d5f5ba38a7cff44b86cf1b6934..4751ffc5ec9ae0d84962267c0712a94721e33c44 100644 (file)
@@ -4203,6 +4203,7 @@ onboarding.create_project.new_code_definition.description.link=Defining New Code
 onboarding.create_project.new_code_definition.create_x_projects=Create {count, plural, one {project} other {# projects}}
 onboarding.create_projects.new_code_definition.change_info=You can change this setting for each project individually at any time in the project administration settings.
 onboarding.create_project.success=Your {count, plural, one {project has} other {# projects have}} been created.
+onboarding.create_project.failure=Import of {count, plural, one {# project} other {# projects}} failed.
 
 onboarding.token.header=Provide a token
 onboarding.token.text=The token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point in time in your {link}.