aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathieu Suen <mathieu.suen@sonarsource.com>2023-10-13 18:06:50 +0200
committersonartech <sonartech@sonarsource.com>2023-10-18 20:03:05 +0000
commit8ff9a3138ec0c7a0056b1dca7e47c71e9dad8494 (patch)
tree1cfd0786d063237667a6425b2d391ca5374aa01b
parentcb55aa8cb1c94ea1e14115222550f58a98897729 (diff)
downloadsonarqube-8ff9a3138ec0c7a0056b1dca7e47c71e9dad8494.tar.gz
sonarqube-8ff9a3138ec0c7a0056b1dca7e47c71e9dad8494.zip
SONAR-20708 Add multi project import API call
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreate.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx74
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx83
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/types.ts7
-rw-r--r--server/sonar-web/src/main/js/queries/import-projects.ts69
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties3
12 files changed, 257 insertions, 113 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx
index bb511fa7335..bf418da89e3 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreate.tsx
@@ -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,
+ },
+ ],
+ });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreate.tsx
index 46586352292..870ffe5c33b 100644
--- a/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudProjectCreate.tsx
@@ -18,16 +18,14 @@
* 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,
+ },
+ ],
+ });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx
index 0815912f45b..d6f7d6815bb 100644
--- a/server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx
@@ -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,
+ },
+ ],
+ });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
index ead4d439536..e16595621d8 100644
--- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx
@@ -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
diff --git a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
index 67e742ceae2..6572243a44a 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreate.tsx
@@ -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,
- );
+ })),
+ });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx
index 228f67171b5..e2032788c0f 100644
--- a/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectCreate.tsx
@@ -18,12 +18,13 @@
* 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 }],
+ });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
index e54eb0096a7..ff9adf035db 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Manual-it.tsx
@@ -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'),
diff --git a/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx b/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
index c9c56f65fa3..e2d22150707 100644
--- a/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/components/NewCodeDefinitionSelection.tsx
@@ -18,50 +18,81 @@
* 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>
diff --git a/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx
index 5cf0729d81d..f567f7f2f06 100644
--- a/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/manual/ManualProjectCreate.tsx
@@ -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,
+ },
+ ],
+ });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/project/types.ts b/server/sonar-web/src/main/js/apps/create/project/types.ts
index 3933da55379..fdb2f01e800 100644
--- a/server/sonar-web/src/main/js/apps/create/project/types.ts
+++ b/server/sonar-web/src/main/js/apps/create/project/types.ts
@@ -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
index 00000000000..2510b371c1a
--- /dev/null
+++ b/server/sonar-web/src/main/js/queries/import-projects.ts
@@ -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'] });
+}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index fb19ad89c0f..09b12b70209 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -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}.