瀏覽代碼

SONAR-19456 New code definition is made part of Bitbucket Server project onboarding

tags/10.1.0.73491
Philippe Perrin 1 年之前
父節點
當前提交
fb56254f6a

+ 19
- 10
server/sonar-web/src/main/js/api/alm-integrations.ts 查看文件

}); });
} }


export function importBitbucketServerProject(
almSetting: string,
projectKey: string,
repositorySlug: string
): Promise<{ project: ProjectBase }> {
return postJSON('/api/alm_integrations/import_bitbucketserver_project', {
almSetting,
projectKey,
repositorySlug,
}).catch(throwGlobalError);
export function setupBitbucketServerProjectCreation(data: {
almSetting: string;
projectKey: string;
repositorySlug: string;
}) {
return (newCodeDefinitionType?: string, newCodeDefinitionValue?: string) =>
importBitbucketServerProject({ ...data, newCodeDefinitionType, newCodeDefinitionValue });
}

export function importBitbucketServerProject(data: {
almSetting: string;
projectKey: string;
repositorySlug: string;
newCodeDefinitionType?: string;
newCodeDefinitionValue?: string;
}): Promise<{ project: ProjectBase }> {
return postJSON('/api/alm_integrations/import_bitbucketserver_project', data).catch(
throwGlobalError
);
} }


export function searchForBitbucketServerRepositories( export function searchForBitbucketServerRepositories(

+ 4
- 0
server/sonar-web/src/main/js/api/mocks/AlmIntegrationsServiceMock.ts 查看文件

searchForBitbucketServerRepositories, searchForBitbucketServerRepositories,
setAlmPersonalAccessToken, setAlmPersonalAccessToken,
setupAzureProjectCreation, setupAzureProjectCreation,
setupBitbucketServerProjectCreation,
} from '../alm-integrations'; } from '../alm-integrations';


export default class AlmIntegrationsServiceMock { export default class AlmIntegrationsServiceMock {
.mocked(getBitbucketServerRepositories) .mocked(getBitbucketServerRepositories)
.mockImplementation(this.getBitbucketServerRepositories); .mockImplementation(this.getBitbucketServerRepositories);
jest.mocked(importBitbucketServerProject).mockImplementation(this.importBitbucketServerProject); jest.mocked(importBitbucketServerProject).mockImplementation(this.importBitbucketServerProject);
jest
.mocked(setupBitbucketServerProjectCreation)
.mockReturnValue(() => this.importBitbucketServerProject());
jest jest
.mocked(searchForBitbucketServerRepositories) .mocked(searchForBitbucketServerRepositories)
.mockImplementation(this.searchForBitbucketServerRepositories); .mockImplementation(this.searchForBitbucketServerRepositories);

+ 0
- 7
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketImportRepositoryForm.tsx 查看文件

BitbucketProjectRepositories, BitbucketProjectRepositories,
BitbucketRepository, BitbucketRepository,
} from '../../../../types/alm-integration'; } from '../../../../types/alm-integration';
import InstanceNewCodeDefinitionComplianceWarning from '../components/InstanceNewCodeDefinitionComplianceWarning';
import { CreateProjectModes } from '../types'; import { CreateProjectModes } from '../types';
import BitbucketRepositories from './BitbucketRepositories'; import BitbucketRepositories from './BitbucketRepositories';
import BitbucketSearchResults from './BitbucketSearchResults'; import BitbucketSearchResults from './BitbucketSearchResults';


export interface BitbucketImportRepositoryFormProps { export interface BitbucketImportRepositoryFormProps {
disableRepositories: boolean;
onSearch: (query: string) => void; onSearch: (query: string) => void;
onSelectRepository: (repo: BitbucketRepository) => void; onSelectRepository: (repo: BitbucketRepository) => void;
projects?: BitbucketProject[]; projects?: BitbucketProject[];


export default function BitbucketImportRepositoryForm(props: BitbucketImportRepositoryFormProps) { export default function BitbucketImportRepositoryForm(props: BitbucketImportRepositoryFormProps) {
const { const {
disableRepositories,
projects = [], projects = [],
projectRepositories = {}, projectRepositories = {},
searchResults, searchResults,


return ( return (
<div className="create-project-import-bbs"> <div className="create-project-import-bbs">
<InstanceNewCodeDefinitionComplianceWarning />

<SearchBox <SearchBox
onChange={props.onSearch} onChange={props.onSearch}
placeholder={translate('onboarding.create_project.search_repositories_by_name')} placeholder={translate('onboarding.create_project.search_repositories_by_name')}


{searching || searchResults ? ( {searching || searchResults ? (
<BitbucketSearchResults <BitbucketSearchResults
disableRepositories={disableRepositories}
onSelectRepository={props.onSelectRepository} onSelectRepository={props.onSelectRepository}
projects={projects} projects={projects}
searchResults={searchResults} searchResults={searchResults}
/> />
) : ( ) : (
<BitbucketRepositories <BitbucketRepositories
disableRepositories={disableRepositories}
onSelectRepository={props.onSelectRepository} onSelectRepository={props.onSelectRepository}
projectRepositories={projectRepositories} projectRepositories={projectRepositories}
projects={projects} projects={projects}

+ 2
- 15
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectAccordion.tsx 查看文件

import { CreateProjectModes } from '../types'; import { CreateProjectModes } from '../types';


export interface BitbucketProjectAccordionProps { export interface BitbucketProjectAccordionProps {
disableRepositories: boolean;
onClick?: () => void; onClick?: () => void;
onSelectRepository: (repo: BitbucketRepository) => void; onSelectRepository: (repo: BitbucketRepository) => void;
open: boolean; open: boolean;
} }


export default function BitbucketProjectAccordion(props: BitbucketProjectAccordionProps) { export default function BitbucketProjectAccordion(props: BitbucketProjectAccordionProps) {
const {
disableRepositories,
open,
project,
repositories,
selectedRepository,
showingAllRepositories,
} = props;
const { open, project, repositories, selectedRepository, showingAllRepositories } = props;


const repositoryCount = repositories.length; const repositoryCount = repositories.length;


) : ( ) : (
<Radio <Radio
checked={selectedRepository?.id === repo.id} checked={selectedRepository?.id === repo.id}
className={classNames(
'display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden',
{
disabled: disableRepositories,
}
)}
className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden"
key={repo.id} key={repo.id}
onCheck={() => props.onSelectRepository(repo)} onCheck={() => props.onSelectRepository(repo)}
value={String(repo.id)} value={String(repo.id)}

+ 11
- 28
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreate.tsx 查看文件

import { import {
getBitbucketServerProjects, getBitbucketServerProjects,
getBitbucketServerRepositories, getBitbucketServerRepositories,
importBitbucketServerProject,
searchForBitbucketServerRepositories, searchForBitbucketServerRepositories,
setupBitbucketServerProjectCreation,
} from '../../../../api/alm-integrations'; } from '../../../../api/alm-integrations';
import { Location, Router } from '../../../../components/hoc/withRouter'; import { Location, Router } from '../../../../components/hoc/withRouter';
import { import {
} from '../../../../types/alm-integration'; } from '../../../../types/alm-integration';
import { AlmSettingsInstance } from '../../../../types/alm-settings'; import { AlmSettingsInstance } from '../../../../types/alm-settings';
import { DEFAULT_BBS_PAGE_SIZE } from '../constants'; import { DEFAULT_BBS_PAGE_SIZE } from '../constants';
import { CreateProjectApiCallback } from '../types';
import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer'; import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer';


interface Props { interface Props {
canAdmin: boolean; canAdmin: boolean;
almInstances: AlmSettingsInstance[]; almInstances: AlmSettingsInstance[];
loadingBindings: boolean; loadingBindings: boolean;
onProjectCreate: (projectKey: string) => void;
location: Location; location: Location;
router: Router; router: Router;
onProjectSetupDone: (createProject: CreateProjectApiCallback) => void;
} }


interface State { interface State {
selectedAlmInstance?: AlmSettingsInstance; selectedAlmInstance?: AlmSettingsInstance;
importing: boolean;
loading: boolean; loading: boolean;
projects?: BitbucketProject[]; projects?: BitbucketProject[];
projectRepositories?: BitbucketProjectRepositories; projectRepositories?: BitbucketProjectRepositories;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
// For now, we only handle a single instance. So we always use the first
// one from the list.
selectedAlmInstance: props.almInstances[0], selectedAlmInstance: props.almInstances[0],
importing: false,
loading: false, loading: false,
searching: false, searching: false,
showPersonalAccessTokenForm: true, showPersonalAccessTokenForm: true,
handleImportRepository = () => { handleImportRepository = () => {
const { selectedAlmInstance, selectedRepository } = this.state; const { selectedAlmInstance, selectedRepository } = this.state;


if (!selectedAlmInstance || !selectedRepository) {
return;
if (selectedAlmInstance && selectedRepository) {
this.props.onProjectSetupDone(
setupBitbucketServerProjectCreation({
almSetting: selectedAlmInstance.key,
projectKey: selectedRepository.projectKey,
repositorySlug: selectedRepository.slug,
})
);
} }

this.setState({ importing: true });
importBitbucketServerProject(
selectedAlmInstance.key,
selectedRepository.projectKey,
selectedRepository.slug
)
.then(({ project: { key } }) => {
if (this.mounted) {
this.setState({ importing: false });
this.props.onProjectCreate(key);
}
})
.catch(() => {
if (this.mounted) {
this.setState({ importing: false });
}
});
}; };


handleSearch = (query: string) => { handleSearch = (query: string) => {
const { canAdmin, loadingBindings, location, almInstances } = this.props; const { canAdmin, loadingBindings, location, almInstances } = this.props;
const { const {
selectedAlmInstance, selectedAlmInstance,
importing,
loading, loading,
projectRepositories, projectRepositories,
projects, projects,
selectedAlmInstance={selectedAlmInstance} selectedAlmInstance={selectedAlmInstance}
almInstances={almInstances} almInstances={almInstances}
canAdmin={canAdmin} canAdmin={canAdmin}
importing={importing}
loading={loading || loadingBindings} loading={loading || loadingBindings}
onImportRepository={this.handleImportRepository} onImportRepository={this.handleImportRepository}
onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated} onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated}

+ 1
- 6
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketProjectCreateRenderer.tsx 查看文件

*/ */
import * as React from 'react'; import * as React from 'react';
import { Button } from '../../../../components/controls/buttons'; import { Button } from '../../../../components/controls/buttons';
import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
import { translate } from '../../../../helpers/l10n'; import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system'; import { getBaseUrl } from '../../../../helpers/system';
import { import {
selectedAlmInstance?: AlmSettingsInstance; selectedAlmInstance?: AlmSettingsInstance;
almInstances: AlmSettingsInstance[]; almInstances: AlmSettingsInstance[];
canAdmin?: boolean; canAdmin?: boolean;
importing: boolean;
loading: boolean; loading: boolean;
onImportRepository: () => void; onImportRepository: () => void;
onSearch: (query: string) => void; onSearch: (query: string) => void;
almInstances, almInstances,
selectedAlmInstance, selectedAlmInstance,
canAdmin, canAdmin,
importing,
loading, loading,
projects, projects,
projectRepositories, projectRepositories,
additionalActions={ additionalActions={
!showPersonalAccessTokenForm && ( !showPersonalAccessTokenForm && (
<div className="display-flex-center pull-right"> <div className="display-flex-center pull-right">
<DeferredSpinner className="spacer-right" loading={importing} />
<Button <Button
className="button-large button-primary" className="button-large button-primary"
disabled={!selectedRepository || importing}
disabled={!selectedRepository}
onClick={props.onImportRepository} onClick={props.onImportRepository}
> >
{translate('onboarding.create_project.import_selected_repo')} {translate('onboarding.create_project.import_selected_repo')}
/> />
) : ( ) : (
<BitbucketImportRepositoryForm <BitbucketImportRepositoryForm
disableRepositories={importing}
onSearch={props.onSearch} onSearch={props.onSearch}
onSelectRepository={props.onSelectRepository} onSelectRepository={props.onSelectRepository}
projectRepositories={projectRepositories} projectRepositories={projectRepositories}

+ 1
- 3
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketRepositories.tsx 查看文件

import BitbucketProjectAccordion from './BitbucketProjectAccordion'; import BitbucketProjectAccordion from './BitbucketProjectAccordion';


export interface BitbucketRepositoriesProps { export interface BitbucketRepositoriesProps {
disableRepositories: boolean;
onSelectRepository: (repo: BitbucketRepository) => void; onSelectRepository: (repo: BitbucketRepository) => void;
projects: BitbucketProject[]; projects: BitbucketProject[];
projectRepositories: BitbucketProjectRepositories; projectRepositories: BitbucketProjectRepositories;
} }


export default function BitbucketRepositories(props: BitbucketRepositoriesProps) { export default function BitbucketRepositories(props: BitbucketRepositoriesProps) {
const { disableRepositories, projects, projectRepositories, selectedRepository } = props;
const { projects, projectRepositories, selectedRepository } = props;


const [openProjectKeys, setOpenProjectKeys] = React.useState( const [openProjectKeys, setOpenProjectKeys] = React.useState(
projects.length > 0 ? [projects[0].key] : [] projects.length > 0 ? [projects[0].key] : []


return ( return (
<BitbucketProjectAccordion <BitbucketProjectAccordion
disableRepositories={disableRepositories}
key={project.key} key={project.key}
onClick={() => handleClick(isOpen, project.key)} onClick={() => handleClick(isOpen, project.key)}
onSelectRepository={props.onSelectRepository} onSelectRepository={props.onSelectRepository}

+ 1
- 10
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketSearchResults.tsx 查看文件

import BitbucketProjectAccordion from './BitbucketProjectAccordion'; import BitbucketProjectAccordion from './BitbucketProjectAccordion';


export interface BitbucketSearchResultsProps { export interface BitbucketSearchResultsProps {
disableRepositories: boolean;
onSelectRepository: (repo: BitbucketRepository) => void; onSelectRepository: (repo: BitbucketRepository) => void;
projects: BitbucketProject[]; projects: BitbucketProject[];
searching: boolean; searching: boolean;
} }


export default function BitbucketSearchResults(props: BitbucketSearchResultsProps) { export default function BitbucketSearchResults(props: BitbucketSearchResultsProps) {
const {
disableRepositories,
projects,
searching,
searchResults = [],
selectedRepository,
} = props;
const { projects, searching, searchResults = [], selectedRepository } = props;


if (searchResults.length === 0 && !searching) { if (searchResults.length === 0 && !searching) {
return ( return (
<DeferredSpinner loading={searching}> <DeferredSpinner loading={searching}>
{filteredSearchResults.length > 0 && ( {filteredSearchResults.length > 0 && (
<BitbucketProjectAccordion <BitbucketProjectAccordion
disableRepositories={disableRepositories}
onSelectRepository={props.onSelectRepository} onSelectRepository={props.onSelectRepository}
open open
repositories={filteredSearchResults} repositories={filteredSearchResults}


return ( return (
<BitbucketProjectAccordion <BitbucketProjectAccordion
disableRepositories={disableRepositories}
key={project.key} key={project.key}
onSelectRepository={props.onSelectRepository} onSelectRepository={props.onSelectRepository}
open open

+ 1
- 1
server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx 查看文件

almInstances={bitbucketSettings} almInstances={bitbucketSettings}
loadingBindings={loading} loadingBindings={loading}
location={location} location={location}
onProjectCreate={this.handleProjectCreate}
router={router} router={router}
onProjectSetupDone={this.handleProjectSetupDone}
/> />
); );
} }

+ 12
- 0
server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx 查看文件

await user.click(radioButton); await user.click(radioButton);
expect(importButton).toBeEnabled(); expect(importButton).toBeEnabled();
await user.click(importButton); await user.click(importButton);

expect(
screen.getByRole('heading', { name: 'onboarding.create_project.new_code_definition.title' })
).toBeInTheDocument();

await user.click(screen.getByRole('radio', { name: 'new_code_definition.global_setting' }));
await user.click(
screen.getByRole('button', {
name: 'onboarding.create_project.new_code_definition.create_project',
})
);

expect(await screen.findByText('/dashboard?id=key')).toBeInTheDocument(); expect(await screen.findByText('/dashboard?id=key')).toBeInTheDocument();
}); });



Loading…
取消
儲存