]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20708 Add multi project import API call
authorMathieu Suen <mathieu.suen@sonarsource.com>
Fri, 13 Oct 2023 16:06:50 +0000 (18:06 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 18 Oct 2023 20:03:05 +0000 (20:03 +0000)
12 files changed:
server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/types.ts
server/sonar-web/src/main/js/queries/import-projects.ts [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index bb511fa73351b188006ca01d835151599007cacb..bf418da89e310465d506b9a832ffaf0b1a1e6957 100644 (file)
@@ -22,13 +22,13 @@ import {
   getAzureProjects,
   getAzureRepositories,
   searchAzureRepositories,
-  setupAzureProjectCreation,
 } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import { AzureProject, AzureRepository } from '../../../../types/alm-integration';
 import { AlmSettingsInstance } from '../../../../types/alm-settings';
 import { Dict } from '../../../../types/types';
-import { CreateProjectApiCallback } from '../types';
+import { ImportProjectParam } from '../CreateProjectPage';
+import { CreateProjectModes } from '../types';
 import AzureCreateProjectRenderer from './AzureProjectCreateRenderer';
 
 interface Props {
@@ -37,7 +37,7 @@ interface Props {
   almInstances: AlmSettingsInstance[];
   location: Location;
   router: Router;
-  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
+  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
 }
 
 interface State {
@@ -210,13 +210,16 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
     const { selectedAlmInstance } = this.state;
 
     if (selectedAlmInstance && selectedRepository) {
-      this.props.onProjectSetupDone(
-        setupAzureProjectCreation({
-          almSetting: selectedAlmInstance.key,
-          projectName: selectedRepository.projectName,
-          repositoryName: selectedRepository.name,
-        }),
-      );
+      this.props.onProjectSetupDone({
+        creationMode: CreateProjectModes.AzureDevOps,
+        almSetting: selectedAlmInstance.key,
+        projects: [
+          {
+            projectName: selectedRepository.projectName,
+            repositoryName: selectedRepository.name,
+          },
+        ],
+      });
     }
   };
 
index 4658635229222a86bdc208b557e8a00ce824dfe2..870ffe5c33b2a47f8b6b033d33e05b3e1f72c5f8 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import {
-  searchForBitbucketCloudRepositories,
-  setupBitbucketCloudProjectCreation,
-} from '../../../../api/alm-integrations';
+import { searchForBitbucketCloudRepositories } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import { BitbucketCloudRepository } from '../../../../types/alm-integration';
 import { AlmSettingsInstance } from '../../../../types/alm-settings';
 import { Paging } from '../../../../types/types';
+import { ImportProjectParam } from '../CreateProjectPage';
 import { BITBUCKET_CLOUD_PROJECTS_PAGESIZE } from '../constants';
-import { CreateProjectApiCallback } from '../types';
+import { CreateProjectModes } from '../types';
 import BitbucketCloudProjectCreateRenderer from './BitbucketCloudProjectCreateRender';
 
 interface Props {
@@ -36,7 +34,7 @@ interface Props {
   loadingBindings: boolean;
   location: Location;
   router: Router;
-  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
+  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
 }
 
 interface State {
@@ -193,12 +191,15 @@ export default class BitbucketCloudProjectCreate extends React.PureComponent<Pro
     const { selectedAlmInstance } = this.state;
 
     if (selectedAlmInstance) {
-      this.props.onProjectSetupDone(
-        setupBitbucketCloudProjectCreation({
-          almSetting: selectedAlmInstance.key,
-          repositorySlug,
-        }),
-      );
+      this.props.onProjectSetupDone({
+        creationMode: CreateProjectModes.BitbucketCloud,
+        almSetting: selectedAlmInstance.key,
+        projects: [
+          {
+            repositorySlug,
+          },
+        ],
+      });
     }
   };
 
index 0815912f45be65470fbe476dcfd3aae73257aeca..d6f7d6815bbfb39bed2d293183924cfbf91c24ed 100644 (file)
@@ -22,7 +22,6 @@ import {
   getBitbucketServerProjects,
   getBitbucketServerRepositories,
   searchForBitbucketServerRepositories,
-  setupBitbucketServerProjectCreation,
 } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import {
@@ -31,8 +30,9 @@ import {
   BitbucketRepository,
 } from '../../../../types/alm-integration';
 import { AlmSettingsInstance } from '../../../../types/alm-settings';
+import { ImportProjectParam } from '../CreateProjectPage';
 import { DEFAULT_BBS_PAGE_SIZE } from '../constants';
-import { CreateProjectApiCallback } from '../types';
+import { CreateProjectModes } from '../types';
 import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer';
 
 interface Props {
@@ -41,7 +41,7 @@ interface Props {
   loadingBindings: boolean;
   location: Location;
   router: Router;
-  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
+  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
 }
 
 interface State {
@@ -184,13 +184,16 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S
     const { selectedAlmInstance } = this.state;
 
     if (selectedAlmInstance) {
-      this.props.onProjectSetupDone(
-        setupBitbucketServerProjectCreation({
-          almSetting: selectedAlmInstance.key,
-          projectKey: selectedRepository.projectKey,
-          repositorySlug: selectedRepository.slug,
-        }),
-      );
+      this.props.onProjectSetupDone({
+        creationMode: CreateProjectModes.BitbucketServer,
+        almSetting: selectedAlmInstance.key,
+        projects: [
+          {
+            projectKey: selectedRepository.projectKey,
+            repositorySlug: selectedRepository.slug,
+          },
+        ],
+      });
     }
   };
 
index ead4d43953663c5327c6e72f4646a30aeb3644c0..e16595621d84d9320c5ef6d880e2f82181f6ec33 100644 (file)
@@ -41,7 +41,7 @@ import GitHubProjectCreate from './Github/GitHubProjectCreate';
 import GitlabProjectCreate from './Gitlab/GitlabProjectCreate';
 import NewCodeDefinitionSelection from './components/NewCodeDefinitionSelection';
 import ManualProjectCreate from './manual/ManualProjectCreate';
-import { CreateProjectApiCallback, CreateProjectModes } from './types';
+import { CreateProjectModes } from './types';
 
 export interface CreateProjectPageProps extends WithAvailableFeaturesProps {
   appState: AppState;
@@ -57,7 +57,7 @@ interface State {
   gitlabSettings: AlmSettingsInstance[];
   loading: boolean;
   creatingAlmDefinition?: AlmKeys;
-  nbrOfProjects?: number;
+  importProjects?: ImportProjectParam;
 }
 
 const PROJECT_MODE_FOR_ALM_KEY = {
@@ -68,9 +68,56 @@ const PROJECT_MODE_FOR_ALM_KEY = {
   [AlmKeys.GitLab]: CreateProjectModes.GitLab,
 };
 
+export type ImportProjectParam =
+  | {
+      creationMode: CreateProjectModes.AzureDevOps;
+      almSetting: string;
+      projects: {
+        projectName: string;
+        repositoryName: string;
+      }[];
+    }
+  | {
+      creationMode: CreateProjectModes.BitbucketCloud;
+      almSetting: string;
+      projects: {
+        repositorySlug: string;
+      }[];
+    }
+  | {
+      creationMode: CreateProjectModes.BitbucketServer;
+      almSetting: string;
+      projects: {
+        repositorySlug: string;
+        projectKey: string;
+      }[];
+    }
+  | {
+      creationMode: CreateProjectModes.GitHub;
+      almSetting: string;
+      projects: {
+        organization: string;
+        repositoryKey: string;
+      }[];
+    }
+  | {
+      creationMode: CreateProjectModes.GitLab;
+      almSetting: string;
+      projects: {
+        gitlabProjectId: string;
+      }[];
+    }
+  | {
+      creationMode: CreateProjectModes.Manual;
+      projects: {
+        project: string;
+        name: string;
+        mainBranch: string;
+      }[];
+    };
+
 export class CreateProjectPage extends React.PureComponent<CreateProjectPageProps, State> {
   mounted = false;
-  createProjectFnRef: CreateProjectApiCallback | null = null;
 
   state: State = {
     azureSettings: [],
@@ -95,7 +142,7 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
   cleanQueryParameters() {
     const { location, router } = this.props;
 
-    if (location.query?.setncd === 'true' && this.createProjectFnRef === null) {
+    if (location.query?.setncd === 'true') {
       // Timeout is required to force the refresh of the URL
       setTimeout(() => {
         location.query.setncd = undefined;
@@ -138,11 +185,10 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
     this.setState({ creatingAlmDefinition: alm });
   };
 
-  handleProjectSetupDone = (createProject: CreateProjectApiCallback, nbrOfProjects?: number) => {
+  handleProjectSetupDone = (importProjects: ImportProjectParam) => {
     const { location, router } = this.props;
-    this.createProjectFnRef = createProject;
 
-    this.setState({ nbrOfProjects });
+    this.setState({ importProjects });
 
     location.query.setncd = 'true';
     router.push(location);
@@ -275,8 +321,8 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
   }
 
   render() {
-    const { location, router } = this.props;
-    const { creatingAlmDefinition, nbrOfProjects } = this.state;
+    const { location } = this.props;
+    const { creatingAlmDefinition, importProjects } = this.state;
     const mode: CreateProjectModes | undefined = location.query?.mode;
     const isProjectSetupDone = location.query?.setncd === 'true';
     const gridLayoutStyle = mode ? 'sw-col-start-2 sw-col-span-10' : 'sw-col-span-12';
@@ -296,13 +342,9 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp
           <div className={classNames({ 'sw-hidden': isProjectSetupDone })}>
             {this.renderProjectCreation(mode)}
           </div>
-          <div className={classNames({ 'sw-hidden': !isProjectSetupDone })}>
-            <NewCodeDefinitionSelection
-              router={router}
-              createProjectFnRef={this.createProjectFnRef}
-              numberOfProjects={nbrOfProjects}
-            />
-          </div>
+          {importProjects !== undefined && isProjectSetupDone && (
+            <NewCodeDefinitionSelection importProjects={importProjects} />
+          )}
 
           {creatingAlmDefinition && (
             <AlmBindingDefinitionForm
index 67e742ceae294f6b7eb09e619927948ff7195ee3..6572243a44a6f263d70cd02328235c304681dde8 100644 (file)
@@ -24,20 +24,20 @@ import {
   getGithubClientId,
   getGithubOrganizations,
   getGithubRepositories,
-  setupGithubProjectCreation,
 } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import { getHostUrl } from '../../../../helpers/urls';
 import { GithubOrganization, GithubRepository } from '../../../../types/alm-integration';
 import { AlmKeys, AlmSettingsInstance } from '../../../../types/alm-settings';
 import { Paging } from '../../../../types/types';
-import { CreateProjectApiCallback } from '../types';
+import { ImportProjectParam } from '../CreateProjectPage';
+import { CreateProjectModes } from '../types';
 import GitHubProjectCreateRenderer from './GitHubProjectCreateRenderer';
 
 interface Props {
   canAdmin: boolean;
   loadingBindings: boolean;
-  onProjectSetupDone: (createProject: CreateProjectApiCallback, nbrOfProjects: number) => void;
+  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
   almInstances: AlmSettingsInstance[];
   location: Location;
   router: Router;
@@ -263,14 +263,14 @@ export default class GitHubProjectCreate extends React.Component<Props, State> {
     const { selectedOrganization, selectedAlmInstance } = this.state;
 
     if (selectedAlmInstance && selectedOrganization && repoKeys.length > 0) {
-      this.props.onProjectSetupDone(
-        setupGithubProjectCreation({
-          almSetting: selectedAlmInstance.key,
+      this.props.onProjectSetupDone({
+        almSetting: selectedAlmInstance.key,
+        creationMode: CreateProjectModes.GitHub,
+        projects: repoKeys.map((repositoryKey) => ({
+          repositoryKey,
           organization: selectedOrganization.key,
-          repositoryKey: repoKeys.join(','), // TBD
-        }),
-        repoKeys.length,
-      );
+        })),
+      });
     }
   };
 
index 228f67171b564c07192da92b0dfce2ab4d2f83ad..e2032788c0fe61c94652bf5e10f7fb5d047b11e8 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { getGitlabProjects, setupGitlabProjectCreation } from '../../../../api/alm-integrations';
+import { getGitlabProjects } from '../../../../api/alm-integrations';
 import { Location, Router } from '../../../../components/hoc/withRouter';
 import { GitlabProject } from '../../../../types/alm-integration';
 import { AlmSettingsInstance } from '../../../../types/alm-settings';
 import { Paging } from '../../../../types/types';
-import { CreateProjectApiCallback } from '../types';
+import { ImportProjectParam } from '../CreateProjectPage';
+import { CreateProjectModes } from '../types';
 import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer';
 
 interface Props {
@@ -32,7 +33,7 @@ interface Props {
   almInstances: AlmSettingsInstance[];
   location: Location;
   router: Router;
-  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
+  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
 }
 
 interface State {
@@ -139,10 +140,11 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat
     const { selectedAlmInstance } = this.state;
 
     if (selectedAlmInstance) {
-      this.props.onProjectSetupDone(
-        // eslint-disable-next-line local-rules/no-api-imports
-        setupGitlabProjectCreation({ almSetting: selectedAlmInstance.key, gitlabProjectId }),
-      );
+      this.props.onProjectSetupDone({
+        creationMode: CreateProjectModes.GitLab,
+        almSetting: selectedAlmInstance.key,
+        projects: [{ gitlabProjectId }],
+      });
     }
   };
 
index e54eb0096a7100c810267d9e98220319ba8c51fb..ff9adf035db93c135abc11e95e04a087833e725a 100644 (file)
@@ -32,9 +32,7 @@ import CreateProjectPage, { CreateProjectPageProps } from '../CreateProjectPage'
 jest.mock('../../../../api/alm-settings');
 jest.mock('../../../../api/newCodeDefinition');
 jest.mock('../../../../api/project-management', () => ({
-  setupManualProjectCreation: jest
-    .fn()
-    .mockReturnValue(() => Promise.resolve({ project: mockProject() })),
+  createProject: jest.fn().mockReturnValue(Promise.resolve({ project: mockProject() })),
 }));
 jest.mock('../../../../api/components', () => ({
   ...jest.requireActual('../../../../api/components'),
index c9c56f65fa39803731bf41500570013508fa9d25..e2d22150707004a7874bff76bf14bf1735988bae 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { ButtonPrimary, ButtonSecondary, FlagMessage, Link, Spinner, Title } from 'design-system';
+import { omit } from 'lodash';
 import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
+import { useEffect } from 'react';
+import { FormattedMessage, useIntl } from 'react-intl';
 import { useNavigate } from 'react-router-dom';
-import { Router } from '../../../../components/hoc/withRouter';
 import NewCodeDefinitionSelector from '../../../../components/new-code-definition/NewCodeDefinitionSelector';
 import { useDocUrl } from '../../../../helpers/docs';
 import { addGlobalSuccessMessage } from '../../../../helpers/globalMessages';
 import { translate } from '../../../../helpers/l10n';
-import { getProjectUrl } from '../../../../helpers/urls';
+import { getProjectUrl, queryToSearch } from '../../../../helpers/urls';
+import {
+  MutationArg,
+  useImportProjectMutation,
+  useImportProjectProgress,
+} from '../../../../queries/import-projects';
 import { NewCodeDefinitiondWithCompliance } from '../../../../types/new-code-definition';
-import { CreateProjectApiCallback } from '../types';
+import { ImportProjectParam } from '../CreateProjectPage';
 
 interface Props {
-  createProjectFnRef: CreateProjectApiCallback | null;
-  router: Router;
-  numberOfProjects?: number;
+  importProjects: ImportProjectParam;
 }
 
 export default function NewCodeDefinitionSelection(props: Props) {
-  const { createProjectFnRef, router, numberOfProjects } = props;
+  const { importProjects } = props;
 
-  const [submitting, setSubmitting] = React.useState(false);
   const [selectedDefinition, selectDefinition] = React.useState<NewCodeDefinitiondWithCompliance>();
-
+  const { mutate, isLoading, data, reset } = useImportProjectMutation();
+  const mutateCount = useImportProjectProgress();
+  const intl = useIntl();
   const navigate = useNavigate();
-
   const getDocUrl = useDocUrl();
 
-  const isMultipleProjects = numberOfProjects !== undefined && numberOfProjects !== 1;
-  const projectCount = isMultipleProjects ? numberOfProjects : 1;
+  const projectCount = importProjects.projects.length;
+  const isMultipleProjects = projectCount > 1;
 
-  const handleProjectCreation = React.useCallback(async () => {
-    if (createProjectFnRef && selectedDefinition) {
-      setSubmitting(true);
-      const { project } = await createProjectFnRef(
-        selectedDefinition.type,
-        selectedDefinition.value,
-      );
-      setSubmitting(false);
-      router.push(getProjectUrl(project.key));
+  useEffect(() => {
+    if (mutateCount > 0 || !data) {
+      return;
+    }
+    reset();
+    addGlobalSuccessMessage(
+      intl.formatMessage(
+        { id: 'onboarding.create_project.success' },
+        {
+          count: projectCount,
+        },
+      ),
+    );
+
+    if (projectCount === 1) {
+      navigate(getProjectUrl(data.project.key));
+    } else {
+      navigate({
+        pathname: '/projects',
+        search: queryToSearch({ recent: true }),
+      });
+    }
+  }, [data, projectCount, mutateCount, reset, intl, navigate]);
 
-      addGlobalSuccessMessage(translate('onboarding.create_project.success'));
+  const handleProjectCreation = () => {
+    if (selectedDefinition) {
+      importProjects.projects.forEach((p) => {
+        const arg = {
+          // eslint-disable-next-line local-rules/use-metrickey-enum
+          ...omit(importProjects, 'projects'),
+          ...p,
+        } as MutationArg;
+        mutate({
+          newCodeDefinitionType: selectedDefinition.type,
+          newCodeDefinitionValue: selectedDefinition.value,
+          ...arg,
+        });
+      });
     }
-  }, [createProjectFnRef, router, selectedDefinition]);
+  };
 
   return (
     <div id="project-ncd-selection" className="sw-body-sm">
@@ -106,7 +137,7 @@ export default function NewCodeDefinitionSelection(props: Props) {
         </ButtonSecondary>
         <ButtonPrimary
           onClick={handleProjectCreation}
-          disabled={!selectedDefinition?.isCompliant || submitting}
+          disabled={!selectedDefinition?.isCompliant || isLoading}
           type="submit"
         >
           <FormattedMessage
@@ -118,7 +149,7 @@ export default function NewCodeDefinitionSelection(props: Props) {
               count: projectCount,
             }}
           />
-          <Spinner className="sw-ml-2" loading={submitting} />
+          <Spinner className="sw-ml-2" loading={isLoading} />
         </ButtonPrimary>
       </div>
     </div>
index 5cf0729d81dc0038ca3028391f4092708aa1cf4a..f567f7f2f06b51118f49cf4e4dd481ccce7582c7 100644 (file)
@@ -33,19 +33,19 @@ import { debounce, isEmpty } from 'lodash';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { doesComponentExists } from '../../../../api/components';
-import { setupManualProjectCreation } from '../../../../api/project-management';
 import { getValue } from '../../../../api/settings';
 import { useDocUrl } from '../../../../helpers/docs';
 import { translate } from '../../../../helpers/l10n';
 import { PROJECT_KEY_INVALID_CHARACTERS, validateProjectKey } from '../../../../helpers/projects';
 import { ProjectKeyValidationResult } from '../../../../types/component';
 import { GlobalSettingKeys } from '../../../../types/settings';
+import { ImportProjectParam } from '../CreateProjectPage';
 import { PROJECT_NAME_MAX_LEN } from '../constants';
-import { CreateProjectApiCallback } from '../types';
+import { CreateProjectModes } from '../types';
 
 interface Props {
   branchesEnabled: boolean;
-  onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
+  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
 }
 
 interface State {
@@ -133,13 +133,16 @@ export default class ManualProjectCreate extends React.PureComponent<Props, Stat
     event.preventDefault();
     const { projectKey, projectName, mainBranchName } = this.state;
     if (this.canSubmit(this.state)) {
-      this.props.onProjectSetupDone(
-        setupManualProjectCreation({
-          project: projectKey,
-          name: (projectName || projectKey).trim(),
-          mainBranch: mainBranchName,
-        }),
-      );
+      this.props.onProjectSetupDone({
+        creationMode: CreateProjectModes.Manual,
+        projects: [
+          {
+            project: projectKey,
+            name: (projectName || projectKey).trim(),
+            mainBranch: mainBranchName,
+          },
+        ],
+      });
     }
   };
 
index 3933da55379c94fa60258c5414f2c792efe46693..fdb2f01e800a75ffadb786544ebed3dc3564378d 100644 (file)
@@ -17,8 +17,6 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { ProjectBase } from '../../../api/components';
-import { NewCodeDefinitionType } from '../../../types/new-code-definition';
 
 export enum CreateProjectModes {
   Manual = 'manual',
@@ -28,8 +26,3 @@ export enum CreateProjectModes {
   GitHub = 'github',
   GitLab = 'gitlab',
 }
-
-export type CreateProjectApiCallback = (
-  newCodeDefinitionType?: NewCodeDefinitionType,
-  newCodeDefinitionValue?: string,
-) => Promise<{ project: ProjectBase }>;
diff --git a/server/sonar-web/src/main/js/queries/import-projects.ts b/server/sonar-web/src/main/js/queries/import-projects.ts
new file mode 100644 (file)
index 0000000..2510b37
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+import { useIsMutating, useMutation } from '@tanstack/react-query';
+import {
+  importAzureRepository,
+  importBitbucketCloudRepository,
+  importBitbucketServerProject,
+  importGithubRepository,
+  importGitlabProject,
+} from '../api/alm-integrations';
+import { createProject } from '../api/project-management';
+import { ImportProjectParam } from '../apps/create/project/CreateProjectPage';
+import { CreateProjectModes } from '../apps/create/project/types';
+
+export type MutationArg<AlmImport extends ImportProjectParam = ImportProjectParam> =
+  AlmImport extends {
+    creationMode: infer A;
+    almSetting: string;
+    projects: (infer R)[];
+  }
+    ? { creationMode: A; almSetting: string } & R
+    : never;
+
+export function useImportProjectMutation() {
+  return useMutation({
+    mutationFn: (
+      data: {
+        newCodeDefinitionType?: string;
+        newCodeDefinitionValue?: string;
+      } & MutationArg,
+    ) => {
+      if (data.creationMode === CreateProjectModes.GitHub) {
+        return importGithubRepository(data);
+      } else if (data.creationMode === CreateProjectModes.AzureDevOps) {
+        return importAzureRepository(data);
+      } else if (data.creationMode === CreateProjectModes.BitbucketCloud) {
+        return importBitbucketCloudRepository(data);
+      } else if (data.creationMode === CreateProjectModes.BitbucketServer) {
+        return importBitbucketServerProject(data);
+      } else if (data.creationMode === CreateProjectModes.GitLab) {
+        return importGitlabProject(data);
+      }
+
+      return createProject(data);
+    },
+    mutationKey: ['import'],
+  });
+}
+
+export function useImportProjectProgress() {
+  return useIsMutating({ mutationKey: ['import'] });
+}
index fb19ad89c0f6d48dc03424094c1d5632cb2eea5c..09b12b70209a04a668997c2a90e4450f7b6a4d21 100644 (file)
@@ -4200,8 +4200,7 @@ onboarding.create_project.new_code_definition.description.link=Defining New Code
 onboarding.create_project.new_code_definition.create_project=Create project
 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 for each project individually at any time in the project administration.
-
-onboarding.create_project.success=Congratulations! Your project has been created.
+onboarding.create_project.success=Your {count, plural, one {project has} other {# projects have}} been created.
 
 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}.