onChangeConfig: (instance: AlmSettingsInstance) => void;
}
+const MIN_SIZE_INSTANCES = 2;
+
export default function AlmSettingsInstanceDropdown(props: AlmSettingsInstanceDropdownProps) {
const { almInstances, selectedAlmInstance } = props;
+ if (!almInstances || almInstances.length < MIN_SIZE_INSTANCES) {
+ return null;
+ }
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}
- </>
+ <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>
);
}
interface Props {
canAdmin: boolean;
- settings: AlmSettingsInstance[];
+ almInstances: AlmSettingsInstance[];
loadingBindings: boolean;
onProjectCreate: (projectKey: string) => void;
location: Location;
repositories: BitbucketCloudRepository[];
searching: boolean;
searchQuery: string;
- settings: AlmSettingsInstance;
+ selectedAlmInstance: AlmSettingsInstance;
showPersonalAccessTokenForm: boolean;
}
repositories: [],
searching: false,
searchQuery: '',
- settings: props.settings[0],
+ selectedAlmInstance: props.almInstances[0],
showPersonalAccessTokenForm: true,
};
}
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.settings.length === 0 && this.props.settings.length > 0) {
- this.setState({ settings: this.props.settings[0] }, () => this.fetchData());
+ if (prevProps.almInstances.length === 0 && this.props.almInstances.length > 0) {
+ this.setState({ selectedAlmInstance: this.props.almInstances[0] }, () => this.fetchData());
}
}
async fetchData(more = false) {
const {
- settings,
+ selectedAlmInstance,
searchQuery,
projectsPaging: { pageIndex, pageSize },
showPersonalAccessTokenForm,
} = this.state;
- if (settings && !showPersonalAccessTokenForm) {
+ if (selectedAlmInstance && !showPersonalAccessTokenForm) {
const { isLastPage, repositories } = await searchForBitbucketCloudRepositories(
- settings.key,
+ selectedAlmInstance.key,
searchQuery,
pageSize,
pageIndex
};
handleImport = async (repositorySlug: string) => {
- const { settings } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!settings) {
+ if (!selectedAlmInstance) {
return;
}
this.setState({ importingSlug: repositorySlug });
- const result = await importBitbucketCloudRepository(settings.key, repositorySlug).catch(
- () => undefined
- );
+ const result = await importBitbucketCloudRepository(
+ selectedAlmInstance.key,
+ repositorySlug
+ ).catch(() => undefined);
if (this.mounted) {
this.setState({ importingSlug: undefined });
}
};
+ onSelectedAlmInstanceChange = (instance: AlmSettingsInstance) => {
+ this.setState({
+ selectedAlmInstance: instance,
+ showPersonalAccessTokenForm: true,
+ resetPat: false,
+ searching: false,
+ searchQuery: '',
+ projectsPaging: { pageIndex: 1, pageSize: BITBUCKET_CLOUD_PROJECTS_PAGESIZE },
+ });
+ };
+
render() {
- const { canAdmin, loadingBindings, location } = this.props;
+ const { canAdmin, loadingBindings, location, almInstances } = this.props;
const {
importingSlug,
isLastPage = true,
- settings,
+ selectedAlmInstance,
loading,
loadingMore,
repositories,
<BitbucketCloudProjectCreateRenderer
importingSlug={importingSlug}
isLastPage={isLastPage}
- settings={settings}
+ selectedAlmInstance={selectedAlmInstance}
+ almInstances={almInstances}
canAdmin={canAdmin}
loadingMore={loadingMore}
loading={loading || loadingBindings}
onLoadMore={this.handleLoadMore}
onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated}
onSearch={this.handleSearch}
+ onSelectedAlmInstanceChange={this.onSelectedAlmInstanceChange}
repositories={repositories}
searching={searching}
searchQuery={searchQuery}
import { getBaseUrl } from '../../../helpers/system';
import { BitbucketCloudRepository } from '../../../types/alm-integration';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
+import AlmSettingsInstanceDropdown from './AlmSettingsInstanceDropdown';
import BitbucketCloudSearchForm from './BitbucketCloudSearchForm';
import CreateProjectPageHeader from './CreateProjectPageHeader';
import PersonalAccessTokenForm from './PersonalAccessTokenForm';
onLoadMore: () => void;
onPersonalAccessTokenCreated: () => void;
onSearch: (searchQuery: string) => void;
+ onSelectedAlmInstanceChange: (instance: AlmSettingsInstance) => void;
repositories?: BitbucketCloudRepository[];
resetPat: boolean;
searching: boolean;
searchQuery: string;
showPersonalAccessTokenForm: boolean;
- settings?: AlmSettingsInstance;
+ almInstances: AlmSettingsInstance[];
+ selectedAlmInstance?: AlmSettingsInstance;
}
export default function BitbucketCloudProjectCreateRenderer(
props: BitbucketCloudProjectCreateRendererProps
) {
const {
+ almInstances,
importingSlug,
isLastPage,
- settings,
+ selectedAlmInstance,
canAdmin,
loading,
loadingMore,
</span>
}
/>
+
+ <AlmSettingsInstanceDropdown
+ almInstances={almInstances}
+ selectedAlmInstance={selectedAlmInstance}
+ onChangeConfig={props.onSelectedAlmInstanceChange}
+ />
+
{loading && <i className="spinner" />}
- {!loading && !settings && (
+ {!loading && !selectedAlmInstance && (
<WrongBindingCountAlert alm={AlmKeys.BitbucketCloud} canAdmin={!!canAdmin} />
)}
{!loading &&
- settings &&
+ selectedAlmInstance &&
(showPersonalAccessTokenForm ? (
<PersonalAccessTokenForm
- almSetting={settings}
+ almSetting={selectedAlmInstance}
resetPat={resetPat}
onPersonalAccessTokenCreated={props.onPersonalAccessTokenCreated}
/>
interface Props {
canAdmin: boolean;
- bitbucketSettings: AlmSettingsInstance[];
+ almInstances: AlmSettingsInstance[];
loadingBindings: boolean;
onProjectCreate: (projectKey: string) => void;
location: Location;
}
interface State {
- bitbucketSetting?: AlmSettingsInstance;
+ selectedAlmInstance?: AlmSettingsInstance;
importing: boolean;
loading: boolean;
projects?: BitbucketProject[];
this.state = {
// For now, we only handle a single instance. So we always use the first
// one from the list.
- bitbucketSetting: props.bitbucketSettings[0],
+ selectedAlmInstance: props.almInstances[0],
importing: false,
loading: false,
searching: false,
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.bitbucketSettings.length === 0 && this.props.bitbucketSettings.length > 0) {
- this.setState({ bitbucketSetting: this.props.bitbucketSettings[0] }, () =>
+ if (prevProps.almInstances.length === 0 && this.props.almInstances.length > 0) {
+ this.setState({ selectedAlmInstance: this.props.almInstances[0] }, () =>
this.fetchInitialData()
);
}
};
fetchBitbucketProjects = (): Promise<BitbucketProject[] | undefined> => {
- const { bitbucketSetting } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!bitbucketSetting) {
+ if (!selectedAlmInstance) {
return Promise.resolve(undefined);
}
- return getBitbucketServerProjects(bitbucketSetting.key).then(({ projects }) => projects);
+ return getBitbucketServerProjects(selectedAlmInstance.key).then(({ projects }) => projects);
};
fetchBitbucketRepositories = (
projects: BitbucketProject[]
): Promise<BitbucketProjectRepositories | undefined> => {
- const { bitbucketSetting } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!bitbucketSetting) {
+ if (!selectedAlmInstance) {
return Promise.resolve(undefined);
}
return Promise.all(
projects.map((p) => {
- return getBitbucketServerRepositories(bitbucketSetting.key, p.name).then(
+ return getBitbucketServerRepositories(selectedAlmInstance.key, p.name).then(
({ isLastPage, repositories }) => {
// Because the WS uses the project name rather than its key to find
// repositories, we can match more repositories than we expect. For
};
handleImportRepository = () => {
- const { bitbucketSetting, selectedRepository } = this.state;
+ const { selectedAlmInstance, selectedRepository } = this.state;
- if (!bitbucketSetting || !selectedRepository) {
+ if (!selectedAlmInstance || !selectedRepository) {
return;
}
this.setState({ importing: true });
importBitbucketServerProject(
- bitbucketSetting.key,
+ selectedAlmInstance.key,
selectedRepository.projectKey,
selectedRepository.slug
)
};
handleSearch = (query: string) => {
- const { bitbucketSetting } = this.state;
+ const { selectedAlmInstance } = this.state;
- if (!bitbucketSetting) {
+ if (!selectedAlmInstance) {
return;
}
}
this.setState({ searching: true, selectedRepository: undefined });
- searchForBitbucketServerRepositories(bitbucketSetting.key, query)
+ searchForBitbucketServerRepositories(selectedAlmInstance.key, query)
.then(({ repositories }) => {
if (this.mounted) {
this.setState({ searching: false, searchResults: repositories });
this.setState({ selectedRepository });
};
+ onSelectedAlmInstanceChange = (instance: AlmSettingsInstance) => {
+ this.setState({
+ selectedAlmInstance: instance,
+ showPersonalAccessTokenForm: true,
+ searching: false,
+ searchResults: undefined,
+ });
+ };
+
render() {
- const { canAdmin, loadingBindings, location } = this.props;
+ const { canAdmin, loadingBindings, location, almInstances } = this.props;
const {
- bitbucketSetting,
+ selectedAlmInstance,
importing,
loading,
projectRepositories,
return (
<BitbucketCreateProjectRenderer
- bitbucketSetting={bitbucketSetting}
+ selectedAlmInstance={selectedAlmInstance}
+ almInstances={almInstances}
canAdmin={canAdmin}
importing={importing}
loading={loading || loadingBindings}
onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated}
onSearch={this.handleSearch}
onSelectRepository={this.handleSelectRepository}
+ onSelectedAlmInstanceChange={this.onSelectedAlmInstanceChange}
projectRepositories={projectRepositories}
projects={projects}
resetPat={Boolean(location.query.resetPat)}
BitbucketRepository,
} from '../../../types/alm-integration';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
+import AlmSettingsInstanceDropdown from './AlmSettingsInstanceDropdown';
import BitbucketImportRepositoryForm from './BitbucketImportRepositoryForm';
import CreateProjectPageHeader from './CreateProjectPageHeader';
import PersonalAccessTokenForm from './PersonalAccessTokenForm';
import WrongBindingCountAlert from './WrongBindingCountAlert';
export interface BitbucketProjectCreateRendererProps {
- bitbucketSetting?: AlmSettingsInstance;
+ selectedAlmInstance?: AlmSettingsInstance;
+ almInstances: AlmSettingsInstance[];
canAdmin?: boolean;
importing: boolean;
loading: boolean;
onSearch: (query: string) => void;
onSelectRepository: (repo: BitbucketRepository) => void;
onPersonalAccessTokenCreated: () => void;
+ onSelectedAlmInstanceChange: (instance: AlmSettingsInstance) => void;
projects?: BitbucketProject[];
projectRepositories?: BitbucketProjectRepositories;
resetPat: boolean;
export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCreateRendererProps) {
const {
- bitbucketSetting,
+ almInstances,
+ selectedAlmInstance,
canAdmin,
importing,
loading,
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={almInstances}
+ selectedAlmInstance={selectedAlmInstance}
+ onChangeConfig={props.onSelectedAlmInstanceChange}
+ />
+
{loading && <i className="spinner" />}
- {!loading && !bitbucketSetting && (
+ {!loading && !selectedAlmInstance && (
<WrongBindingCountAlert alm={AlmKeys.BitbucketServer} canAdmin={!!canAdmin} />
)}
{!loading &&
- bitbucketSetting &&
+ selectedAlmInstance &&
(showPersonalAccessTokenForm ? (
<PersonalAccessTokenForm
- almSetting={bitbucketSetting}
+ almSetting={selectedAlmInstance}
onPersonalAccessTokenCreated={props.onPersonalAccessTokenCreated}
resetPat={resetPat}
/>
import * as React from 'react';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import ChevronsIcon from '../../../components/icons/ChevronsIcon';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
-import { AlmKeys } from '../../../types/alm-settings';
+import { AlmKeys, AlmKeysUnion } from '../../../types/alm-settings';
import { AppState } from '../../../types/appstate';
-import { ALLOWED_MULTIPLE_CONFIGS } from './constants';
import { CreateProjectModes } from './types';
export interface CreateProjectModeSelectionProps {
const DEFAULT_ICON_SIZE = 50;
-function getErrorMessage(
- hasTooManyConfig: boolean,
- hasConfig: boolean,
- canAdmin: boolean | undefined,
- alm: AlmKeys
-) {
- if (hasTooManyConfig) {
- return translateWithParameters(
- 'onboarding.create_project.too_many_alm_instances_X',
- translate('alm', alm)
- );
- } else if (!hasConfig) {
+function getErrorMessage(hasConfig: boolean, canAdmin: boolean | undefined) {
+ if (!hasConfig) {
return canAdmin
? translate('onboarding.create_project.alm_not_configured.admin')
: translate('onboarding.create_project.alm_not_configured');
}
-}
-
-function getMode(
- isBitbucketOption: boolean,
- hasBitbucketCloudConf: boolean,
- mode: CreateProjectModes
-) {
- return isBitbucketOption && hasBitbucketCloudConf ? CreateProjectModes.BitbucketCloud : mode;
+ return undefined;
}
function renderAlmOption(
props: CreateProjectModeSelectionProps,
- alm: AlmKeys.Azure | AlmKeys.BitbucketServer | AlmKeys.GitHub | AlmKeys.GitLab,
+ alm: AlmKeysUnion,
mode: CreateProjectModes,
last = false
) {
appState: { canAdmin },
loadingBindings,
} = props;
-
- const hasBitbucketCloudConf = almCounts[AlmKeys.BitbucketCloud] > 0;
- const isBitbucketOption = alm === AlmKeys.BitbucketServer;
-
- const count = isBitbucketOption
- ? almCounts[AlmKeys.BitbucketServer] + almCounts[AlmKeys.BitbucketCloud]
- : almCounts[alm];
+ const count = almCounts[alm];
const hasConfig = count > 0;
- const hasTooManyConfig = count > 1 && !ALLOWED_MULTIPLE_CONFIGS.includes(alm);
- const disabled = loadingBindings || hasTooManyConfig || (!hasConfig && !canAdmin);
+ const disabled = loadingBindings || (!hasConfig && !canAdmin);
const onClick = () => {
- if (hasTooManyConfig || (!hasConfig && !canAdmin)) {
+ if (!hasConfig && !canAdmin) {
return null;
}
if (!hasConfig && canAdmin) {
- return props.onConfigMode(alm);
+ const configMode = alm === AlmKeys.BitbucketCloud ? AlmKeys.BitbucketServer : alm;
+ return props.onConfigMode(configMode);
}
- return props.onSelectMode(getMode(isBitbucketOption, hasBitbucketCloudConf, mode));
+ return props.onSelectMode(mode);
};
- const errorMessage = getErrorMessage(hasTooManyConfig, hasConfig, canAdmin, alm);
+ const errorMessage = getErrorMessage(hasConfig, canAdmin);
+
+ const svgFileName = alm === AlmKeys.BitbucketCloud ? AlmKeys.BitbucketServer : alm;
return (
<div className="display-flex-column">
<img
alt="" // Should be ignored by screen readers
height={DEFAULT_ICON_SIZE}
- src={`${getBaseUrl()}/images/alm/${alm}.svg`}
+ src={`${getBaseUrl()}/images/alm/${svgFileName}.svg`}
/>
<div className="medium big-spacer-top abs-height-50 display-flex-center">
{translate('onboarding.create_project.select_method', alm)}
<div className="big-spacer-top huge-spacer-bottom display-flex-center">
{renderAlmOption(props, AlmKeys.Azure, CreateProjectModes.AzureDevOps)}
{renderAlmOption(props, AlmKeys.BitbucketServer, CreateProjectModes.BitbucketServer)}
+ {renderAlmOption(props, AlmKeys.BitbucketCloud, CreateProjectModes.BitbucketCloud)}
{renderAlmOption(props, AlmKeys.GitHub, CreateProjectModes.GitHub)}
{renderAlmOption(props, AlmKeys.GitLab, CreateProjectModes.GitLab, true)}
</div>
return (
<BitbucketProjectCreate
canAdmin={!!canAdmin}
- bitbucketSettings={bitbucketSettings}
+ almInstances={bitbucketSettings}
loadingBindings={loading}
location={location}
onProjectCreate={this.handleProjectCreate}
location={location}
onProjectCreate={this.handleProjectCreate}
router={router}
- settings={bitbucketCloudSettings}
+ almInstances={bitbucketCloudSettings}
/>
);
}
});
it('Should render correctly', async () => {
- const wrapper = shallowRender({ settings: [] });
+ const wrapper = shallowRender({ almInstances: [] });
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
- wrapper.setProps({ settings: [mockBitbucketCloudAlmSettingsInstance()] });
+ wrapper.setProps({ almInstances: [mockBitbucketCloudAlmSettingsInstance()] });
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot('Setting changeds');
});
location={mockLocation()}
canAdmin={true}
router={mockRouter()}
- settings={[mockBitbucketCloudAlmSettingsInstance()]}
+ almInstances={[mockBitbucketCloudAlmSettingsInstance()]}
{...props}
/>
);
it('Should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
- expect(shallowRender({ settings: undefined })).toMatchSnapshot('Wrong config');
+ expect(shallowRender({ selectedAlmInstance: undefined })).toMatchSnapshot('Wrong config');
expect(shallowRender({ loading: true })).toMatchSnapshot('Loading...');
expect(
shallowRender({
resetPat={false}
searching={false}
searchQuery=""
- settings={mockBitbucketCloudAlmSettingsInstance()}
+ selectedAlmInstance={mockBitbucketCloudAlmSettingsInstance()}
+ almInstances={[mockBitbucketCloudAlmSettingsInstance()]}
+ onSelectedAlmInstanceChange={jest.fn()}
showPersonalAccessTokenForm={false}
{...props}
/>
it('should render correctly', async () => {
expect(shallowRender()).toMatchSnapshot();
- expect(shallowRender({ bitbucketSettings: [] })).toMatchSnapshot('No setting');
+ expect(shallowRender({ almInstances: [] })).toMatchSnapshot('No setting');
const wrapper = shallowRender();
(getBitbucketServerRepositories as jest.Mock).mockRejectedValueOnce({});
});
it('should behave correctly when no setting', async () => {
- const wrapper = shallowRender({ bitbucketSettings: [] });
+ const wrapper = shallowRender({ almInstances: [] });
await wrapper.instance().handleSearch('');
await wrapper.instance().handleImportRepository();
await wrapper.instance().fetchBitbucketRepositories([mockBitbucketProject()]);
return shallow<BitbucketProjectCreate>(
<BitbucketProjectCreate
canAdmin={false}
- bitbucketSettings={[mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer, key: 'foo' })]}
+ almInstances={[mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer, key: 'foo' })]}
loadingBindings={false}
location={mockLocation()}
router={mockRouter()}
expect(shallowRender({ selectedRepository: mockBitbucketRepository() })).toMatchSnapshot(
'selected repo'
);
- expect(shallowRender({ bitbucketSetting: undefined })).toMatchSnapshot(
+ expect(shallowRender({ selectedAlmInstance: undefined })).toMatchSnapshot(
'invalid config, regular user'
);
- expect(shallowRender({ bitbucketSetting: undefined, canAdmin: true })).toMatchSnapshot(
+ expect(shallowRender({ selectedAlmInstance: undefined, canAdmin: true })).toMatchSnapshot(
'invalid config, admin user'
);
});
function shallowRender(props: Partial<BitbucketProjectCreateRendererProps> = {}) {
return shallow<BitbucketProjectCreateRendererProps>(
<BitbucketProjectCreateRenderer
- bitbucketSetting={mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer })}
+ selectedAlmInstance={mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer })}
+ almInstances={[mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer })]}
+ onSelectedAlmInstanceChange={jest.fn()}
importing={false}
loading={false}
onImportRepository={jest.fn()}
onSelectMode.mockClear();
click(wrapper.find(almButton).at(2));
- expect(onSelectMode).toHaveBeenCalledWith(CreateProjectModes.GitHub);
+ expect(onSelectMode).toHaveBeenCalledWith(CreateProjectModes.BitbucketCloud);
onSelectMode.mockClear();
click(wrapper.find(almButton).at(3));
+ expect(onSelectMode).toHaveBeenCalledWith(CreateProjectModes.GitHub);
+ onSelectMode.mockClear();
+
+ click(wrapper.find(almButton).at(4));
expect(onSelectMode).toHaveBeenCalledWith(CreateProjectModes.GitLab);
onSelectMode.mockClear();
{ [AlmKeys.BitbucketCloud]: 1, [AlmKeys.BitbucketServer]: 0 }
);
- click(wrapper.find(almButton).at(1));
+ click(wrapper.find(almButton).at(2));
expect(onSelectMode).toHaveBeenCalledWith(CreateProjectModes.BitbucketCloud);
onSelectMode.mockClear();
});
) {
const almCounts = {
[AlmKeys.Azure]: 1,
- [AlmKeys.BitbucketCloud]: 0,
+ [AlmKeys.BitbucketCloud]: 1,
[AlmKeys.BitbucketServer]: 1,
[AlmKeys.GitHub]: 1,
[AlmKeys.GitLab]: 1,
exports[`Should render correctly 1`] = `
<BitbucketCloudProjectCreateRenderer
+ almInstances={Array []}
canAdmin={true}
isLastPage={true}
loading={false}
onLoadMore={[Function]}
onPersonalAccessTokenCreated={[Function]}
onSearch={[Function]}
+ onSelectedAlmInstanceChange={[Function]}
repositories={Array []}
resetPat={false}
searchQuery=""
exports[`Should render correctly: Setting changeds 1`] = `
<BitbucketCloudProjectCreateRenderer
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ },
+ ]
+ }
canAdmin={true}
isLastPage={true}
loading={false}
onLoadMore={[Function]}
onPersonalAccessTokenCreated={[Function]}
onSearch={[Function]}
+ onSelectedAlmInstanceChange={[Function]}
repositories={Array []}
resetPat={false}
searchQuery=""
searching={false}
- settings={
+ selectedAlmInstance={
Object {
"alm": "bitbucketcloud",
"key": "key",
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ }
+ }
+ />
<BitbucketCloudSearchForm
isLastPage={true}
loadingMore={false}
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ }
+ }
+ />
<i
className="spinner"
/>
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ }
+ }
+ />
<PersonalAccessTokenForm
almSetting={
Object {
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucketcloud",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
<WrongBindingCountAlert
alm="bitbucketcloud"
canAdmin={false}
exports[`should render correctly 1`] = `
<BitbucketProjectCreateRenderer
- bitbucketSetting={
- Object {
- "alm": "bitbucket",
- "key": "foo",
- }
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "foo",
+ },
+ ]
}
canAdmin={false}
importing={false}
onPersonalAccessTokenCreated={[Function]}
onSearch={[Function]}
onSelectRepository={[Function]}
+ onSelectedAlmInstanceChange={[Function]}
resetPat={false}
searching={false}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "foo",
+ }
+ }
showPersonalAccessTokenForm={true}
/>
`;
exports[`should render correctly: No repository 1`] = `
<BitbucketProjectCreateRenderer
- bitbucketSetting={
- Object {
- "alm": "bitbucket",
- "key": "foo",
- }
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "foo",
+ },
+ ]
}
canAdmin={false}
importing={false}
onPersonalAccessTokenCreated={[Function]}
onSearch={[Function]}
onSelectRepository={[Function]}
+ onSelectedAlmInstanceChange={[Function]}
projects={
Array [
Object {
}
resetPat={false}
searching={false}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "foo",
+ }
+ }
showPersonalAccessTokenForm={false}
/>
`;
exports[`should render correctly: No setting 1`] = `
<BitbucketProjectCreateRenderer
+ almInstances={Array []}
canAdmin={false}
importing={false}
loading={false}
onPersonalAccessTokenCreated={[Function]}
onSearch={[Function]}
onSelectRepository={[Function]}
+ onSelectedAlmInstanceChange={[Function]}
resetPat={false}
searching={false}
showPersonalAccessTokenForm={true}
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ }
+ }
+ />
<BitbucketImportRepositoryForm
disableRepositories={false}
onSearch={[MockFunction]}
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ }
+ }
+ />
<BitbucketImportRepositoryForm
disableRepositories={true}
onSearch={[MockFunction]}
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
<WrongBindingCountAlert
alm="bitbucket"
canAdmin={true}
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ />
<WrongBindingCountAlert
alm="bitbucket"
canAdmin={false}
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ }
+ }
+ />
<i
className="spinner"
/>
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ }
+ }
+ />
<PersonalAccessTokenForm
almSetting={
Object {
</span>
}
/>
+ <AlmSettingsInstanceDropdown
+ almInstances={
+ Array [
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ },
+ ]
+ }
+ onChangeConfig={[MockFunction]}
+ selectedAlmInstance={
+ Object {
+ "alm": "bitbucket",
+ "key": "key",
+ }
+ }
+ />
<BitbucketImportRepositoryForm
disableRepositories={false}
onSearch={[MockFunction]}
</div>
</button>
</div>
+ <div
+ className="display-flex-column"
+ >
+ <button
+ className="button button-huge display-flex-column create-project-mode-type-alm big-spacer-right"
+ disabled={false}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={50}
+ src="/images/alm/bitbucket.svg"
+ />
+ <div
+ className="medium big-spacer-top abs-height-50 display-flex-center"
+ >
+ onboarding.create_project.select_method.bitbucketcloud
+ </div>
+ </button>
+ </div>
<div
className="display-flex-column"
>
</p>
</button>
</div>
+ <div
+ className="display-flex-column"
+ >
+ <button
+ className="button button-huge display-flex-column create-project-mode-type-alm big-spacer-right"
+ disabled={false}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={50}
+ src="/images/alm/bitbucket.svg"
+ />
+ <div
+ className="medium big-spacer-top abs-height-50 display-flex-center"
+ >
+ onboarding.create_project.select_method.bitbucketcloud
+ </div>
+ </button>
+ </div>
<div
className="display-flex-column"
>
</p>
</button>
</div>
+ <div
+ className="display-flex-column"
+ >
+ <button
+ className="button button-huge display-flex-column create-project-mode-type-alm big-spacer-right"
+ disabled={false}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={50}
+ src="/images/alm/bitbucket.svg"
+ />
+ <div
+ className="medium big-spacer-top abs-height-50 display-flex-center"
+ >
+ onboarding.create_project.select_method.bitbucketcloud
+ </div>
+ <p
+ className="text-muted small spacer-top"
+ style={
+ Object {
+ "lineHeight": 1.5,
+ }
+ }
+ >
+ onboarding.create_project.alm_not_configured.admin
+ </p>
+ </button>
+ </div>
<div
className="display-flex-column"
>
</p>
</button>
</div>
+ <div
+ className="display-flex-column"
+ >
+ <button
+ className="button button-huge display-flex-column create-project-mode-type-alm big-spacer-right"
+ disabled={false}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={50}
+ src="/images/alm/bitbucket.svg"
+ />
+ <div
+ className="medium big-spacer-top abs-height-50 display-flex-center"
+ >
+ onboarding.create_project.select_method.bitbucketcloud
+ </div>
+ </button>
+ </div>
<div
className="display-flex-column"
>
</span>
</button>
</div>
+ <div
+ className="display-flex-column"
+ >
+ <button
+ className="button button-huge display-flex-column create-project-mode-type-alm disabled big-spacer-right"
+ disabled={true}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={50}
+ src="/images/alm/bitbucket.svg"
+ />
+ <div
+ className="medium big-spacer-top abs-height-50 display-flex-center"
+ >
+ onboarding.create_project.select_method.bitbucketcloud
+ </div>
+ <span>
+ onboarding.create_project.check_alm_supported
+ <i
+ className="little-spacer-left spinner"
+ />
+ </span>
+ </button>
+ </div>
<div
className="display-flex-column"
>
</p>
</button>
</div>
+ <div
+ className="display-flex-column"
+ >
+ <button
+ className="button button-huge display-flex-column create-project-mode-type-alm big-spacer-right"
+ disabled={false}
+ onClick={[Function]}
+ type="button"
+ >
+ <img
+ alt=""
+ height={50}
+ src="/images/alm/bitbucket.svg"
+ />
+ <div
+ className="medium big-spacer-top abs-height-50 display-flex-center"
+ >
+ onboarding.create_project.select_method.bitbucketcloud
+ </div>
+ <p
+ className="text-muted small spacer-top"
+ style={
+ Object {
+ "lineHeight": 1.5,
+ }
+ }
+ >
+ onboarding.create_project.alm_not_configured.admin
+ </p>
+ </button>
+ </div>
<div
className="display-flex-column"
>
id="create-project"
>
<BitbucketProjectCreate
- bitbucketSettings={Array []}
+ almInstances={Array []}
canAdmin={false}
loadingBindings={true}
location={
id="create-project"
>
<BitbucketCloudProjectCreate
+ almInstances={Array []}
canAdmin={false}
loadingBindings={true}
location={
"setRouteLeaveHook": [MockFunction],
}
}
- settings={Array []}
/>
</div>
</Fragment>
-import { AlmKeys } from '../../../types/alm-settings';
-
/*
* SonarQube
* Copyright (C) 2009-2022 SonarSource SA
export const PROJECT_NAME_MAX_LEN = 255;
export const DEFAULT_BBS_PAGE_SIZE = 25;
-
-export const ALLOWED_MULTIPLE_CONFIGS = [AlmKeys.GitLab, AlmKeys.Azure, AlmKeys.GitHub];
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
import { Permissions } from '../../../types/permissions';
import { LoggedInUser } from '../../../types/users';
-import { ALLOWED_MULTIPLE_CONFIGS } from '../../create/project/constants';
import ProjectCreationMenuItem from './ProjectCreationMenuItem';
interface Props {
const almSettings: AlmSettingsInstance[] = await getAlmSettings().catch(() => []);
- // Import is only available if exactly one binding is configured
const boundAlms = IMPORT_COMPATIBLE_ALMS.filter((key) => {
- let currentAlmSettings: AlmSettingsInstance[];
- if (key === AlmKeys.BitbucketServer || key === AlmKeys.BitbucketCloud) {
- currentAlmSettings = almSettings.filter(
- (s) => s.alm === AlmKeys.BitbucketCloud || s.alm === AlmKeys.BitbucketServer
- );
- } else {
- currentAlmSettings = almSettings.filter((s) => s.alm === key);
- }
+ const currentAlmSettings = almSettings.filter((s) => s.alm === key);
return (
- this.configLengthChecker(key, currentAlmSettings.length) &&
+ currentAlmSettings.length > 0 &&
key === currentAlmSettings[0].alm &&
this.almSettingIsValid(currentAlmSettings[0])
);
}
};
- configLengthChecker = (key: AlmKeys, length: number) => {
- return ALLOWED_MULTIPLE_CONFIGS.includes(key) ? length > 0 : length === 1;
- };
-
render() {
const { className, currentUser } = this.props;
const { boundAlms } = this.state;
wrapper = shallowRender();
await waitAndUpdate(wrapper);
- expect(wrapper.state().boundAlms).toEqual([AlmKeys.Azure, AlmKeys.GitHub, AlmKeys.GitLab]);
+ expect(wrapper.state().boundAlms).toEqual([
+ AlmKeys.Azure,
+ AlmKeys.BitbucketServer,
+ AlmKeys.BitbucketCloud,
+ AlmKeys.GitHub,
+ AlmKeys.GitLab,
+ ]);
wrapper = shallowRender();
await waitAndUpdate(wrapper);
- expect(wrapper.state().boundAlms).toEqual([AlmKeys.Azure, AlmKeys.GitHub, AlmKeys.GitLab]);
+ expect(wrapper.state().boundAlms).toEqual([
+ AlmKeys.Azure,
+ AlmKeys.BitbucketServer,
+ AlmKeys.GitHub,
+ AlmKeys.GitLab,
+ ]);
});
function shallowRender(overrides: Partial<ProjectCreationMenu['props']> = {}) {
AlmSettingsBindingStatusType,
} from '../../../../types/alm-settings';
import { EditionKey } from '../../../../types/editions';
-import { ALLOWED_MULTIPLE_CONFIGS } from '../../../create/project/constants';
export interface AlmBindingDefinitionBoxProps {
alm: AlmKeys;
branchesEnabled: boolean;
definition: AlmBindingDefinitionBase;
- multipleDefinitions: boolean;
onCheck: (definitionKey: string) => void;
onDelete: (definitionKey: string) => void;
onEdit: (definitionKey: string) => void;
function getImportFeatureStatus(
alm: AlmKeys,
definition: AlmBindingDefinitionBase,
- multipleDefinitions: boolean,
type: AlmSettingsBindingStatusType.Success | AlmSettingsBindingStatusType.Failure
) {
- if (multipleDefinitions && !ALLOWED_MULTIPLE_CONFIGS.includes(alm)) {
- return (
- <div className="display-inline-flex-center">
- <strong className="spacer-left">
- {translate('settings.almintegration.feature.alm_repo_import.disabled')}
- </strong>
- <HelpTooltip
- className="little-spacer-left"
- overlay={translate('settings.almintegration.feature.alm_repo_import.disabled.multiple')}
- />
- </div>
- );
- }
-
if (!definition.url && alm !== AlmKeys.BitbucketCloud) {
return (
<div className="display-inline-flex-center">
}
export default function AlmBindingDefinitionBox(props: AlmBindingDefinitionBoxProps) {
- const { alm, branchesEnabled, definition, multipleDefinitions, status = DEFAULT_STATUS } = props;
+ const { alm, branchesEnabled, definition, status = DEFAULT_STATUS } = props;
return (
<div className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition">
{translate('settings.almintegration.feature.alm_repo_import.title')}
</span>
</Tooltip>
- {getImportFeatureStatus(alm, definition, multipleDefinitions, status.type)}
+ {getImportFeatureStatus(alm, definition, status.type)}
</div>
)}
</div>
branchesEnabled={branchesEnabled}
definition={def}
key={def.key}
- multipleDefinitions={definitions.length > 1}
onCheck={props.onCheck}
onDelete={props.onDelete}
onEdit={props.onEdit}
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ multipleDefinitions: true })).toMatchSnapshot('multiple definitions');
expect(
shallowRender({
status: mockAlmSettingsBindingStatus({
alm={AlmKeys.GitHub}
branchesEnabled={true}
definition={mockGithubBindingDefinition()}
- multipleDefinitions={false}
onCheck={jest.fn()}
onDelete={jest.fn()}
onEdit={jest.fn()}
</div>
`;
-exports[`should render correctly: multiple definitions 1`] = `
-<div
- className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition"
->
- <div
- className="actions pull-right"
- >
- <Button
- onClick={[Function]}
- >
- <EditIcon
- className="spacer-right"
- />
- edit
- </Button>
- <Button
- className="button-red spacer-left"
- onClick={[Function]}
- >
- <DeleteIcon
- className="spacer-right"
- />
- delete
- </Button>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <h3>
- key
- </h3>
- <span>
- http://github.enterprise.com
- </span>
- </div>
- <i
- className="spinner spacer-right"
- />
- settings.almintegration.checking_configuration
-</div>
-`;
-
exports[`should render correctly: success 1`] = `
<div
className="boxed-group-inner bordered spacer-top spacer-bottom it__alm-binding-definition"
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
}
}
key="key"
- multipleDefinitions={false}
onCheck={[MockFunction]}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
GitLab = 'gitlab',
}
+export type AlmKeysUnion =
+ | AlmKeys.Azure
+ | AlmKeys.BitbucketServer
+ | AlmKeys.BitbucketCloud
+ | AlmKeys.GitHub
+ | AlmKeys.GitLab;
+
export type AlmBindingDefinition =
| AzureBindingDefinition
| GithubBindingDefinition
my_account.add_project=Add Project
my_account.add_project.manual=Manually
my_account.add_project.azure=Azure DevOps
-my_account.add_project.bitbucket=Bitbucket
-my_account.add_project.bitbucketcloud=Bitbucket
+my_account.add_project.bitbucket=Bitbucket Server
+my_account.add_project.bitbucketcloud=Bitbucket Cloud
my_account.add_project.github=GitHub
my_account.add_project.gitlab=GitLab
my_account.reset_password=Update your password
onboarding.create_project.select_method.no_alm_yet.admin=First, you need to set up a DevOps platform configuration.
onboarding.create_project.select_method.manual=Manually
onboarding.create_project.select_method.azure=From Azure DevOps
-onboarding.create_project.select_method.bitbucket=From Bitbucket
+onboarding.create_project.select_method.bitbucket=From Bitbucket Server
+onboarding.create_project.select_method.bitbucketcloud=From Bitbucket Cloud
onboarding.create_project.select_method.github=From GitHub
onboarding.create_project.select_method.gitlab=From GitLab
onboarding.create_project.alm_not_configured=Contact admin to set up global configuration
onboarding.create_project.search_repositories=Search for a repository
onboarding.create_project.select_repositories=Select repositories
onboarding.create_project.select_all_repositories=Select all available repositories
-onboarding.create_project.from_bbs=Create a project from Bitbucket Server
+onboarding.create_project.from_bbs=Bitbucket Server project onboarding
onboarding.create_application.key.description=If specified, this value is used as the key instead of generating it from the name of the Application. Only letters, digits, dashes and underscores can be used.
onboarding.create_project.pat_form.list_repositories=List repositories
onboarding.create_project.select_method=How do you want to create your project?
onboarding.create_project.too_many_alm_instances_X=This method requires exactly one {0} configuration.
-onboarding.create_project.wrong_binding_count=You must have exactly 1 {alm} instance configured in order to use this method, but none were found. Either create the project manually, or contact your system administrator.
-onboarding.create_project.wrong_binding_count.admin=You must have exactly 1 {alm} instance configured in order to use this method. You can configure instances under {url}.
+onboarding.create_project.wrong_binding_count=You must have at least 1 {alm} instance configured in order to use this method, but none were found. Either create the project manually, or contact your system administrator.
+onboarding.create_project.wrong_binding_count.admin=You must have at least 1 {alm} instance configured in order to use this method. You can configure instances under {url}.
onboarding.create_project.azure.no_url.admin=Your Azure DevOps instance configuration is missing a URL. We cannot import projects in the current state. You can configure instances under {url}.
onboarding.create_project.azure.no_url=Your Azure DevOps instance configuration is missing a URL. We cannot import projects in the current state. Please contact your system administrator.
onboarding.create_project.enter_pat=Enter personal access token
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}.
onboarding.create_project.azure.no_results=No repositories match your search query.
-onboarding.create_project.bitbucketcloud.title=Which Bitbucket Cloud repository do you want to set up?
+onboarding.create_project.bitbucketcloud.title=Bitbucket Cloud project onboarding
onboarding.create_project.bitbucketcloud.no_projects=No projects could be fetched from Bitbucket. Contact your system administrator, or {link}.
onboarding.create_project.bitbucketcloud.link=See on Bitbucket
onboarding.create_project.github.title=Github project onboarding