aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Silva <kevin.silva@sonarsource.com>2022-11-17 12:01:13 +0100
committersonartech <sonartech@sonarsource.com>2022-11-18 20:02:49 +0000
commita292eb339f4ec2b413b855dfa1cf1cf8ac1a1667 (patch)
treee468bb8debb2d685b966b4497cb54cc861bdd41f
parent35e585d67ebb9e1af8e8caeb3f3a6552577e5ea4 (diff)
downloadsonarqube-a292eb339f4ec2b413b855dfa1cf1cf8ac1a1667.tar.gz
sonarqube-a292eb339f4ec2b413b855dfa1cf1cf8ac1a1667.zip
SONAR-17589 Allow project onboarding when multiple Azure integrations are configured
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/AlmSettingsInstanceDropdown.tsx51
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx67
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreateRenderer-test.tsx29
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreate-test.tsx.snap11
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreateRenderer-test.tsx.snap124
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap183
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/constants.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCreationMenu-test.tsx2
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
15 files changed, 312 insertions, 220 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/project/AlmSettingsInstanceDropdown.tsx b/server/sonar-web/src/main/js/apps/create/project/AlmSettingsInstanceDropdown.tsx
new file mode 100644
index 00000000000..1bd3d726f48
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/create/project/AlmSettingsInstanceDropdown.tsx
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 * as React from 'react';
+import AlmSettingsInstanceSelector from '../../../components/devops-platform/AlmSettingsInstanceSelector';
+import { translate } from '../../../helpers/l10n';
+import { AlmSettingsInstance } from '../../../types/alm-settings';
+
+export interface AlmSettingsInstanceDropdownProps {
+ almInstances?: AlmSettingsInstance[];
+ selectedAlmInstance?: AlmSettingsInstance;
+ onChangeConfig: (instance: AlmSettingsInstance) => void;
+}
+
+export default function AlmSettingsInstanceDropdown(props: AlmSettingsInstanceDropdownProps) {
+ const { almInstances, selectedAlmInstance } = props;
+ return (
+ <>
+ {almInstances && almInstances.length > 1 ? (
+ <div className="display-flex-column huge-spacer-bottom">
+ <label htmlFor="alm-config-selector" className="spacer-bottom">
+ {translate('alm.configuration.selector.label')}
+ </label>
+ <AlmSettingsInstanceSelector
+ instances={almInstances}
+ onChange={props.onChangeConfig}
+ initialValue={selectedAlmInstance ? selectedAlmInstance.key : undefined}
+ classNames="abs-width-400"
+ inputId="alm-config-selector"
+ />
+ </div>
+ ) : null}
+ </>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx
index fb2ffd7920d..1c9864fd9e4 100644
--- a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx
@@ -36,7 +36,7 @@ interface Props {
canAdmin: boolean;
loadingBindings: boolean;
onProjectCreate: (projectKey: string) => void;
- settings: AlmSettingsInstance[];
+ almInstances: AlmSettingsInstance[];
location: Location;
router: Router;
}
@@ -52,7 +52,7 @@ interface State {
searchResults?: AzureRepository[];
searchQuery?: string;
selectedRepository?: AzureRepository;
- settings?: AlmSettingsInstance;
+ selectedAlmInstance?: AlmSettingsInstance;
submittingToken?: boolean;
tokenValidationFailed: boolean;
}
@@ -65,7 +65,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
this.state = {
// For now, we only handle a single instance. So we always use the first
// one from the list.
- settings: props.settings[0],
+ selectedAlmInstance: props.almInstances[0],
importing: false,
loading: false,
loadingRepositories: {},
@@ -76,15 +76,12 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
componentDidMount() {
this.mounted = true;
- this.fetchInitialData();
+ this.fetchData();
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.settings.length === 0 && this.props.settings.length > 0) {
- this.setState(
- { settings: this.props.settings.length === 1 ? this.props.settings[0] : undefined },
- () => this.fetchInitialData()
- );
+ if (prevProps.almInstances.length === 0 && this.props.almInstances.length > 0) {
+ this.setState({ selectedAlmInstance: this.props.almInstances[0] }, () => this.fetchData());
}
}
@@ -92,7 +89,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
this.mounted = false;
}
- fetchInitialData = async () => {
+ fetchData = async () => {
this.setState({ loading: true });
const patIsValid = await this.checkPersonalAccessToken().catch(() => false);
@@ -135,23 +132,23 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
};
fetchAzureProjects = (): Promise<AzureProject[] | undefined> => {
- const { settings } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!settings) {
+ if (!selectedAlmInstance) {
return Promise.resolve(undefined);
}
- return getAzureProjects(settings.key).then(({ projects }) => projects);
+ return getAzureProjects(selectedAlmInstance.key).then(({ projects }) => projects);
};
fetchAzureRepositories = (projectName: string): Promise<AzureRepository[]> => {
- const { settings } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!settings) {
+ if (!selectedAlmInstance) {
return Promise.resolve([]);
}
- return getAzureRepositories(settings.key, projectName)
+ return getAzureRepositories(selectedAlmInstance.key, projectName)
.then(({ repositories }) => repositories)
.catch(() => []);
};
@@ -180,9 +177,9 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
};
handleSearchRepositories = async (searchQuery: string) => {
- const { settings } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!settings) {
+ if (!selectedAlmInstance) {
return;
}
@@ -194,7 +191,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
this.setState({ searching: true });
const searchResults: AzureRepository[] = await searchAzureRepositories(
- settings.key,
+ selectedAlmInstance.key,
searchQuery
)
.then(({ repositories }) => repositories)
@@ -210,16 +207,16 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
};
handleImportRepository = async () => {
- const { selectedRepository, settings } = this.state;
+ const { selectedRepository, selectedAlmInstance } = this.state;
- if (!settings || !selectedRepository) {
+ if (!selectedAlmInstance || !selectedRepository) {
return;
}
this.setState({ importing: true });
const createdProject = await importAzureRepository(
- settings.key,
+ selectedAlmInstance.key,
selectedRepository.projectName,
selectedRepository.name
)
@@ -239,26 +236,26 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
};
checkPersonalAccessToken = () => {
- const { settings } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!settings) {
+ if (!selectedAlmInstance) {
return Promise.resolve(false);
}
- return checkPersonalAccessTokenIsValid(settings.key).then(({ status }) => status);
+ return checkPersonalAccessTokenIsValid(selectedAlmInstance.key).then(({ status }) => status);
};
handlePersonalAccessTokenCreate = async (token: string) => {
- const { settings } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!settings || token.length < 1) {
+ if (!selectedAlmInstance || token.length < 1) {
return;
}
this.setState({ submittingToken: true, tokenValidationFailed: false });
try {
- await setAlmPersonalAccessToken(settings.key, token);
+ await setAlmPersonalAccessToken(selectedAlmInstance.key, token);
const patIsValid = await this.checkPersonalAccessToken();
if (this.mounted) {
@@ -266,7 +263,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
if (patIsValid) {
this.cleanUrl();
- this.fetchInitialData();
+ this.fetchData();
}
}
} catch (e) {
@@ -276,8 +273,12 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
}
};
+ onChangeConfig = (instance: AlmSettingsInstance) => {
+ this.setState({ selectedAlmInstance: instance }, () => this.fetchData());
+ };
+
render() {
- const { canAdmin, loadingBindings, location } = this.props;
+ const { canAdmin, loadingBindings, location, almInstances } = this.props;
const {
importing,
loading,
@@ -289,7 +290,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
searchResults,
searchQuery,
selectedRepository,
- settings,
+ selectedAlmInstance,
submittingToken,
tokenValidationFailed,
} = this.state;
@@ -311,10 +312,12 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State
searchResults={searchResults}
searchQuery={searchQuery}
selectedRepository={selectedRepository}
- settings={settings}
+ almInstances={almInstances}
+ selectedAlmInstance={selectedAlmInstance}
showPersonalAccessTokenForm={!patIsValid || Boolean(location.query.resetPat)}
submittingToken={submittingToken}
tokenValidationFailed={tokenValidationFailed}
+ onChangeConfig={this.onChangeConfig}
/>
);
}
diff --git a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx
index fc5b4823c9d..22fa07d7101 100644
--- a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx
@@ -31,6 +31,7 @@ import { AzureProject, AzureRepository } from '../../../types/alm-integration';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
import { Dict } from '../../../types/types';
import { ALM_INTEGRATION_CATEGORY } from '../../settings/constants';
+import AlmSettingsInstanceDropdown from './AlmSettingsInstanceDropdown';
import AzurePersonalAccessTokenForm from './AzurePersonalAccessTokenForm';
import AzureProjectsList from './AzureProjectsList';
import CreateProjectPageHeader from './CreateProjectPageHeader';
@@ -52,10 +53,12 @@ export interface AzureProjectCreateRendererProps {
searchResults?: AzureRepository[];
searchQuery?: string;
selectedRepository?: AzureRepository;
- settings?: AlmSettingsInstance;
+ almInstances?: AlmSettingsInstance[];
+ selectedAlmInstance?: AlmSettingsInstance;
showPersonalAccessTokenForm?: boolean;
submittingToken?: boolean;
tokenValidationFailed: boolean;
+ onChangeConfig: (instance: AlmSettingsInstance) => void;
}
export default function AzureProjectCreateRenderer(props: AzureProjectCreateRendererProps) {
@@ -70,15 +73,16 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
searchResults,
searchQuery,
selectedRepository,
- settings,
+ almInstances,
showPersonalAccessTokenForm,
submittingToken,
tokenValidationFailed,
+ selectedAlmInstance,
} = props;
- const settingIsValid = settings && settings.url;
- const showCountError = !loading && !settings;
- const showUrlError = !loading && settings && !settings.url;
+ const showCountError = !loading && (!almInstances || almInstances?.length === 0);
+ const settingIsValid = selectedAlmInstance && selectedAlmInstance.url;
+ const showUrlError = !loading && selectedAlmInstance && !selectedAlmInstance.url;
return (
<>
@@ -111,6 +115,12 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={almInstances}
+ selectedAlmInstance={selectedAlmInstance}
+ onChangeConfig={props.onChangeConfig}
+ />
+
{loading && <i className="spinner" />}
{showUrlError && (
@@ -137,12 +147,12 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
{showCountError && <WrongBindingCountAlert alm={AlmKeys.Azure} canAdmin={!!canAdmin} />}
{!loading &&
- settings &&
- settings.url &&
+ selectedAlmInstance &&
+ selectedAlmInstance.url &&
(showPersonalAccessTokenForm ? (
- <div className="display-flex-justify-center">
+ <div>
<AzurePersonalAccessTokenForm
- almSetting={settings}
+ almSetting={selectedAlmInstance}
onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
submitting={submittingToken}
validationFailed={tokenValidationFailed}
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 926f2fe4f89..21100831bd3 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
@@ -171,7 +171,7 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
location={location}
onProjectCreate={this.handleProjectCreate}
router={router}
- settings={azureSettings}
+ almInstances={azureSettings}
/>
);
}
diff --git a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx
index 668420dffe2..a90b987b31a 100644
--- a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx
@@ -18,12 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import AlmSettingsInstanceSelector from '../../../components/devops-platform/AlmSettingsInstanceSelector';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { GitlabProject } from '../../../types/alm-integration';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
import { Paging } from '../../../types/types';
+import AlmSettingsInstanceDropdown from './AlmSettingsInstanceDropdown';
import CreateProjectPageHeader from './CreateProjectPageHeader';
import GitlabProjectSelectionForm from './GitlabProjectSelectionForm';
import PersonalAccessTokenForm from './PersonalAccessTokenForm';
@@ -81,20 +81,11 @@ export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRe
}
/>
- {almInstances && almInstances.length > 1 && (
- <div className="display-flex-column huge-spacer-bottom">
- <label htmlFor="alm-config-selector" className="spacer-bottom">
- {translate('alm.configuration.selector.label')}
- </label>
- <AlmSettingsInstanceSelector
- instances={almInstances}
- onChange={props.onChangeConfig}
- initialValue={selectedAlmInstance ? selectedAlmInstance.key : undefined}
- classNames="abs-width-400"
- inputId="alm-config-selector"
- />
- </div>
- )}
+ <AlmSettingsInstanceDropdown
+ almInstances={almInstances}
+ selectedAlmInstance={selectedAlmInstance}
+ onChangeConfig={props.onChangeConfig}
+ />
{loading && <i className="spinner" />}
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx
index f2a80b0cf13..c033380802a 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx
@@ -195,8 +195,8 @@ it('should select and import a repository', async () => {
expect(wrapper.state().importing).toBe(false);
});
-it('should handle no settings', () => {
- const wrapper = shallowRender({ settings: [] });
+it('should handle no almInstances', () => {
+ const wrapper = shallowRender({ almInstances: [] });
wrapper.instance().fetchAzureProjects();
wrapper.instance().fetchAzureRepositories('whatever');
@@ -221,7 +221,7 @@ function shallowRender(overrides: Partial<AzureProjectCreate['props']> = {}) {
location={mockLocation()}
onProjectCreate={jest.fn()}
router={mockRouter()}
- settings={[mockAlmSettingsInstance({ alm: AlmKeys.Azure, key: 'foo' })]}
+ almInstances={[mockAlmSettingsInstance({ alm: AlmKeys.Azure, key: 'foo' })]}
{...overrides}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreateRenderer-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreateRenderer-test.tsx
index 265a1890e03..c4549235f11 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreateRenderer-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreateRenderer-test.tsx
@@ -28,19 +28,35 @@ import AzureProjectCreateRenderer, {
it('should render correctly', () => {
expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
- expect(shallowRender({ settings: undefined })).toMatchSnapshot('no settings');
+ expect(shallowRender({ almInstances: undefined })).toMatchSnapshot('no settings');
expect(shallowRender({ showPersonalAccessTokenForm: true })).toMatchSnapshot('token form');
- expect(shallowRender()).toMatchSnapshot('project list');
expect(
shallowRender({
- settings: mockAlmSettingsInstance({ alm: AlmKeys.Azure }),
+ almInstances: [
+ mockAlmSettingsInstance({ alm: AlmKeys.Azure, url: 'https://azure.company.com' }),
+ mockAlmSettingsInstance({
+ alm: AlmKeys.Azure,
+ url: 'https://azure.company.com',
+ key: 'key2',
+ }),
+ ],
+ selectedAlmInstance: mockAlmSettingsInstance({
+ alm: AlmKeys.Azure,
+ url: 'https://azure.company.com',
+ }),
+ })
+ ).toMatchSnapshot('project list');
+
+ expect(
+ shallowRender({
+ almInstances: [mockAlmSettingsInstance({ alm: AlmKeys.Azure })],
showPersonalAccessTokenForm: true,
})
).toMatchSnapshot('setting missing url, admin');
expect(
shallowRender({
canAdmin: false,
- settings: mockAlmSettingsInstance({ alm: AlmKeys.Azure }),
+ almInstances: [mockAlmSettingsInstance({ alm: AlmKeys.Azure })],
})
).toMatchSnapshot('setting missing url, not admin');
});
@@ -62,9 +78,12 @@ function shallowRender(overrides: Partial<AzureProjectCreateRendererProps> = {})
projects={[project]}
repositories={{ [project.name]: [mockAzureRepository()] }}
tokenValidationFailed={false}
- settings={mockAlmSettingsInstance({ alm: AlmKeys.Azure, url: 'https://azure.company.com' })}
+ almInstances={[
+ mockAlmSettingsInstance({ alm: AlmKeys.Azure, url: 'https://azure.company.com' }),
+ ]}
showPersonalAccessTokenForm={false}
submittingToken={false}
+ onChangeConfig={jest.fn()}
{...overrides}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
index 47ddaad401e..0301387ae44 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectModeSelection-test.tsx
@@ -102,7 +102,7 @@ it('should call the proper click handler', () => {
const onSelectMode = jest.fn();
const onConfigMode = jest.fn();
- let wrapper = shallowRender({ onSelectMode, onConfigMode }, { [AlmKeys.Azure]: 2 });
+ let wrapper = shallowRender({ onSelectMode, onConfigMode }, { [AlmKeys.Azure]: 0 });
click(wrapper.find(almButton).at(0));
expect(onConfigMode).not.toHaveBeenCalled();
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreate-test.tsx.snap
index a3c1a9a74d7..4af7d5f4433 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreate-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreate-test.tsx.snap
@@ -2,17 +2,26 @@
exports[`should render correctly 1`] = `
<AzureProjectCreateRenderer
+ almInstances={
+ Array [
+ Object {
+ "alm": "azure",
+ "key": "foo",
+ },
+ ]
+ }
canAdmin={true}
importing={false}
loading={true}
loadingRepositories={Object {}}
+ onChangeConfig={[Function]}
onImportRepository={[Function]}
onOpenProject={[Function]}
onPersonalAccessTokenCreate={[Function]}
onSearch={[Function]}
onSelectRepository={[Function]}
repositories={Object {}}
- settings={
+ selectedAlmInstance={
Object {
"alm": "azure",
"key": "foo",
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreateRenderer-test.tsx.snap
index 7b95f10bdf8..2c6ac9285f6 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreateRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreateRenderer-test.tsx.snap
@@ -3,23 +3,6 @@
exports[`should render correctly: loading 1`] = `
<Fragment>
<CreateProjectPageHeader
- additionalActions={
- <div
- className="display-flex-center pull-right"
- >
- <DeferredSpinner
- className="spacer-right"
- loading={false}
- />
- <Button
- className="button-large button-primary"
- disabled={true}
- onClick={[MockFunction]}
- >
- onboarding.create_project.import_selected_repo
- </Button>
- </div>
- }
title={
<span
className="text-middle"
@@ -34,6 +17,18 @@ exports[`should render correctly: loading 1`] = `
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "azure",
+ "key": "key",
+ "url": "https://azure.company.com",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
<i
className="spinner"
/>
@@ -57,6 +52,9 @@ exports[`should render correctly: no settings 1`] = `
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ onChangeConfig={[MockFunction]}
+ />
<WrongBindingCountAlert
alm="azure"
canAdmin={true}
@@ -98,6 +96,30 @@ exports[`should render correctly: project list 1`] = `
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "azure",
+ "key": "key",
+ "url": "https://azure.company.com",
+ },
+ Object {
+ "alm": "azure",
+ "key": "key2",
+ "url": "https://azure.company.com",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "azure",
+ "key": "key",
+ "url": "https://azure.company.com",
+ }
+ }
+ />
<div
className="huge-spacer-bottom"
>
@@ -155,29 +177,17 @@ exports[`should render correctly: setting missing url, admin 1`] = `
</span>
}
/>
- <Alert
- variant="error"
- >
- <FormattedMessage
- defaultMessage="onboarding.create_project.azure.no_url.admin"
- id="onboarding.create_project.azure.no_url.admin"
- values={
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
Object {
- "alm": "onboarding.alm.azure",
- "url": <ForwardRef(Link)
- to={
- Object {
- "pathname": "/admin/settings",
- "search": "?category=almintegration",
- }
- }
- >
- settings.page
- </ForwardRef(Link)>,
- }
- }
- />
- </Alert>
+ "alm": "azure",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
</Fragment>
`;
@@ -198,11 +208,17 @@ exports[`should render correctly: setting missing url, not admin 1`] = `
</span>
}
/>
- <Alert
- variant="error"
- >
- onboarding.create_project.azure.no_url
- </Alert>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "azure",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
</Fragment>
`;
@@ -224,21 +240,17 @@ exports[`should render correctly: token form 1`] = `
</span>
}
/>
- <div
- className="display-flex-justify-center"
- >
- <AzurePersonalAccessTokenForm
- almSetting={
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
Object {
"alm": "azure",
"key": "key",
"url": "https://azure.company.com",
- }
- }
- onPersonalAccessTokenCreate={[MockFunction]}
- submitting={false}
- validationFailed={false}
- />
- </div>
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
index 7d5f76c3837..a49fc0433bb 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap
@@ -92,6 +92,7 @@ exports[`should render correctly for azure mode 1`] = `
id="create-project"
>
<AzureProjectCreate
+ almInstances={Array []}
canAdmin={false}
loadingBindings={true}
location={
@@ -120,7 +121,6 @@ exports[`should render correctly for azure mode 1`] = `
"setRouteLeaveHook": [MockFunction],
}
}
- settings={Array []}
/>
</div>
</Fragment>
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap
index 0fcab57ab4d..a84db1f9b28 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap
@@ -17,6 +17,15 @@ exports[`should render correctly: invalid settings 1`] = `
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ }
+ }
+ />
<PersonalAccessTokenForm
almSetting={
Object {
@@ -47,6 +56,15 @@ exports[`should render correctly: invalid settings, admin user 1`] = `
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ }
+ }
+ />
<PersonalAccessTokenForm
almSetting={
Object {
@@ -77,38 +95,31 @@ exports[`should render correctly: loading 1`] = `
</span>
}
/>
- <div
- className="display-flex-column huge-spacer-bottom"
- >
- <label
- className="spacer-bottom"
- htmlFor="alm-config-selector"
- >
- alm.configuration.selector.label
- </label>
- <AlmSettingsInstanceSelector
- classNames="abs-width-400"
- initialValue="key"
- inputId="alm-config-selector"
- instances={
- Array [
- Object {
- "alm": "gitlab",
- "key": "key",
- },
- Object {
- "alm": "gitlab",
- "key": "key",
- },
- Object {
- "alm": "github",
- "key": "key",
- },
- ]
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ },
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ },
+ Object {
+ "alm": "github",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "gitlab",
+ "key": "key",
}
- onChange={[MockFunction]}
- />
- </div>
+ }
+ />
<i
className="spinner"
/>
@@ -132,38 +143,31 @@ exports[`should render correctly: pat form 1`] = `
</span>
}
/>
- <div
- className="display-flex-column huge-spacer-bottom"
- >
- <label
- className="spacer-bottom"
- htmlFor="alm-config-selector"
- >
- alm.configuration.selector.label
- </label>
- <AlmSettingsInstanceSelector
- classNames="abs-width-400"
- initialValue="key"
- inputId="alm-config-selector"
- instances={
- Array [
- Object {
- "alm": "gitlab",
- "key": "key",
- },
- Object {
- "alm": "gitlab",
- "key": "key",
- },
- Object {
- "alm": "github",
- "key": "key",
- },
- ]
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ },
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ },
+ Object {
+ "alm": "github",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "gitlab",
+ "key": "key",
}
- onChange={[MockFunction]}
- />
- </div>
+ }
+ />
<PersonalAccessTokenForm
almSetting={
Object {
@@ -194,38 +198,31 @@ exports[`should render correctly: project selection form 1`] = `
</span>
}
/>
- <div
- className="display-flex-column huge-spacer-bottom"
- >
- <label
- className="spacer-bottom"
- htmlFor="alm-config-selector"
- >
- alm.configuration.selector.label
- </label>
- <AlmSettingsInstanceSelector
- classNames="abs-width-400"
- initialValue="key"
- inputId="alm-config-selector"
- instances={
- Array [
- Object {
- "alm": "gitlab",
- "key": "key",
- },
- Object {
- "alm": "gitlab",
- "key": "key",
- },
- Object {
- "alm": "github",
- "key": "key",
- },
- ]
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ },
+ Object {
+ "alm": "gitlab",
+ "key": "key",
+ },
+ Object {
+ "alm": "github",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "gitlab",
+ "key": "key",
}
- onChange={[MockFunction]}
- />
- </div>
+ }
+ />
<GitlabProjectSelectionForm
loadingMore={false}
onImport={[MockFunction]}
diff --git a/server/sonar-web/src/main/js/apps/create/project/constants.ts b/server/sonar-web/src/main/js/apps/create/project/constants.ts
index 3c342c2804e..a620bcb52a6 100644
--- a/server/sonar-web/src/main/js/apps/create/project/constants.ts
+++ b/server/sonar-web/src/main/js/apps/create/project/constants.ts
@@ -23,4 +23,4 @@ export const PROJECT_NAME_MAX_LEN = 255;
export const DEFAULT_BBS_PAGE_SIZE = 25;
-export const ALLOWED_MULTIPLE_CONFIGS = [AlmKeys.GitLab];
+export const ALLOWED_MULTIPLE_CONFIGS = [AlmKeys.GitLab, AlmKeys.Azure];
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCreationMenu-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCreationMenu-test.tsx
index fabc60c1c97..d53f11dff02 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCreationMenu-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCreationMenu-test.tsx
@@ -121,7 +121,7 @@ it('should filter alm bindings appropriately', async () => {
wrapper = shallowRender();
await waitAndUpdate(wrapper);
- expect(wrapper.state().boundAlms).toEqual([AlmKeys.GitLab]);
+ expect(wrapper.state().boundAlms).toEqual([AlmKeys.Azure, AlmKeys.GitLab]);
});
function shallowRender(overrides: Partial<ProjectCreationMenu['props']> = {}) {
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 aa861ca230e..870ff352a71 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -3583,7 +3583,7 @@ onboarding.create_project.see_on_github=See project on GitHub
onboarding.create_project.search_prompt=Search for projects
onboarding.create_project.set_up=Set up
-onboarding.create_project.azure.title=Which Azure DevOps repository do you want to set up?
+onboarding.create_project.azure.title=Azure project onboarding
onboarding.create_project.azure.no_projects=No projects could be fetched from Azure DevOps. Contact your system administrator, or {link}.
onboarding.create_project.azure.search_results_for_project_X=Search results for "{0}"
onboarding.create_project.azure.no_repositories=Could not fetch repositories for this project. Contact your system administrator, or {link}.