@@ -118,16 +118,25 @@ export function getBitbucketServerRepositories( | |||
}); | |||
} | |||
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( |
@@ -58,6 +58,7 @@ import { | |||
searchForBitbucketServerRepositories, | |||
setAlmPersonalAccessToken, | |||
setupAzureProjectCreation, | |||
setupBitbucketServerProjectCreation, | |||
} from '../alm-integrations'; | |||
export default class AlmIntegrationsServiceMock { | |||
@@ -204,6 +205,9 @@ export default class AlmIntegrationsServiceMock { | |||
.mocked(getBitbucketServerRepositories) | |||
.mockImplementation(this.getBitbucketServerRepositories); | |||
jest.mocked(importBitbucketServerProject).mockImplementation(this.importBitbucketServerProject); | |||
jest | |||
.mocked(setupBitbucketServerProjectCreation) | |||
.mockReturnValue(() => this.importBitbucketServerProject()); | |||
jest | |||
.mocked(searchForBitbucketServerRepositories) | |||
.mockImplementation(this.searchForBitbucketServerRepositories); |
@@ -29,13 +29,11 @@ import { | |||
BitbucketProjectRepositories, | |||
BitbucketRepository, | |||
} from '../../../../types/alm-integration'; | |||
import InstanceNewCodeDefinitionComplianceWarning from '../components/InstanceNewCodeDefinitionComplianceWarning'; | |||
import { CreateProjectModes } from '../types'; | |||
import BitbucketRepositories from './BitbucketRepositories'; | |||
import BitbucketSearchResults from './BitbucketSearchResults'; | |||
export interface BitbucketImportRepositoryFormProps { | |||
disableRepositories: boolean; | |||
onSearch: (query: string) => void; | |||
onSelectRepository: (repo: BitbucketRepository) => void; | |||
projects?: BitbucketProject[]; | |||
@@ -47,7 +45,6 @@ export interface BitbucketImportRepositoryFormProps { | |||
export default function BitbucketImportRepositoryForm(props: BitbucketImportRepositoryFormProps) { | |||
const { | |||
disableRepositories, | |||
projects = [], | |||
projectRepositories = {}, | |||
searchResults, | |||
@@ -80,8 +77,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo | |||
return ( | |||
<div className="create-project-import-bbs"> | |||
<InstanceNewCodeDefinitionComplianceWarning /> | |||
<SearchBox | |||
onChange={props.onSearch} | |||
placeholder={translate('onboarding.create_project.search_repositories_by_name')} | |||
@@ -89,7 +84,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo | |||
{searching || searchResults ? ( | |||
<BitbucketSearchResults | |||
disableRepositories={disableRepositories} | |||
onSelectRepository={props.onSelectRepository} | |||
projects={projects} | |||
searchResults={searchResults} | |||
@@ -98,7 +92,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo | |||
/> | |||
) : ( | |||
<BitbucketRepositories | |||
disableRepositories={disableRepositories} | |||
onSelectRepository={props.onSelectRepository} | |||
projectRepositories={projectRepositories} | |||
projects={projects} |
@@ -32,7 +32,6 @@ import { BitbucketProject, BitbucketRepository } from '../../../../types/alm-int | |||
import { CreateProjectModes } from '../types'; | |||
export interface BitbucketProjectAccordionProps { | |||
disableRepositories: boolean; | |||
onClick?: () => void; | |||
onSelectRepository: (repo: BitbucketRepository) => void; | |||
open: boolean; | |||
@@ -43,14 +42,7 @@ export interface 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; | |||
@@ -119,12 +111,7 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi | |||
) : ( | |||
<Radio | |||
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} | |||
onCheck={() => props.onSelectRepository(repo)} | |||
value={String(repo.id)} |
@@ -21,8 +21,8 @@ import * as React from 'react'; | |||
import { | |||
getBitbucketServerProjects, | |||
getBitbucketServerRepositories, | |||
importBitbucketServerProject, | |||
searchForBitbucketServerRepositories, | |||
setupBitbucketServerProjectCreation, | |||
} from '../../../../api/alm-integrations'; | |||
import { Location, Router } from '../../../../components/hoc/withRouter'; | |||
import { | |||
@@ -32,20 +32,20 @@ import { | |||
} from '../../../../types/alm-integration'; | |||
import { AlmSettingsInstance } from '../../../../types/alm-settings'; | |||
import { DEFAULT_BBS_PAGE_SIZE } from '../constants'; | |||
import { CreateProjectApiCallback } from '../types'; | |||
import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer'; | |||
interface Props { | |||
canAdmin: boolean; | |||
almInstances: AlmSettingsInstance[]; | |||
loadingBindings: boolean; | |||
onProjectCreate: (projectKey: string) => void; | |||
location: Location; | |||
router: Router; | |||
onProjectSetupDone: (createProject: CreateProjectApiCallback) => void; | |||
} | |||
interface State { | |||
selectedAlmInstance?: AlmSettingsInstance; | |||
importing: boolean; | |||
loading: boolean; | |||
projects?: BitbucketProject[]; | |||
projectRepositories?: BitbucketProjectRepositories; | |||
@@ -61,10 +61,7 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S | |||
constructor(props: Props) { | |||
super(props); | |||
this.state = { | |||
// For now, we only handle a single instance. So we always use the first | |||
// one from the list. | |||
selectedAlmInstance: props.almInstances[0], | |||
importing: false, | |||
loading: false, | |||
searching: false, | |||
showPersonalAccessTokenForm: true, | |||
@@ -187,27 +184,15 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S | |||
handleImportRepository = () => { | |||
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) => { | |||
@@ -253,7 +238,6 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S | |||
const { canAdmin, loadingBindings, location, almInstances } = this.props; | |||
const { | |||
selectedAlmInstance, | |||
importing, | |||
loading, | |||
projectRepositories, | |||
projects, | |||
@@ -268,7 +252,6 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S | |||
selectedAlmInstance={selectedAlmInstance} | |||
almInstances={almInstances} | |||
canAdmin={canAdmin} | |||
importing={importing} | |||
loading={loading || loadingBindings} | |||
onImportRepository={this.handleImportRepository} | |||
onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated} |
@@ -19,7 +19,6 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import { Button } from '../../../../components/controls/buttons'; | |||
import DeferredSpinner from '../../../../components/ui/DeferredSpinner'; | |||
import { translate } from '../../../../helpers/l10n'; | |||
import { getBaseUrl } from '../../../../helpers/system'; | |||
import { | |||
@@ -38,7 +37,6 @@ export interface BitbucketProjectCreateRendererProps { | |||
selectedAlmInstance?: AlmSettingsInstance; | |||
almInstances: AlmSettingsInstance[]; | |||
canAdmin?: boolean; | |||
importing: boolean; | |||
loading: boolean; | |||
onImportRepository: () => void; | |||
onSearch: (query: string) => void; | |||
@@ -59,7 +57,6 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr | |||
almInstances, | |||
selectedAlmInstance, | |||
canAdmin, | |||
importing, | |||
loading, | |||
projects, | |||
projectRepositories, | |||
@@ -76,10 +73,9 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr | |||
additionalActions={ | |||
!showPersonalAccessTokenForm && ( | |||
<div className="display-flex-center pull-right"> | |||
<DeferredSpinner className="spacer-right" loading={importing} /> | |||
<Button | |||
className="button-large button-primary" | |||
disabled={!selectedRepository || importing} | |||
disabled={!selectedRepository} | |||
onClick={props.onImportRepository} | |||
> | |||
{translate('onboarding.create_project.import_selected_repo')} | |||
@@ -123,7 +119,6 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr | |||
/> | |||
) : ( | |||
<BitbucketImportRepositoryForm | |||
disableRepositories={importing} | |||
onSearch={props.onSearch} | |||
onSelectRepository={props.onSelectRepository} | |||
projectRepositories={projectRepositories} |
@@ -29,7 +29,6 @@ import { | |||
import BitbucketProjectAccordion from './BitbucketProjectAccordion'; | |||
export interface BitbucketRepositoriesProps { | |||
disableRepositories: boolean; | |||
onSelectRepository: (repo: BitbucketRepository) => void; | |||
projects: BitbucketProject[]; | |||
projectRepositories: BitbucketProjectRepositories; | |||
@@ -37,7 +36,7 @@ export interface BitbucketRepositoriesProps { | |||
} | |||
export default function BitbucketRepositories(props: BitbucketRepositoriesProps) { | |||
const { disableRepositories, projects, projectRepositories, selectedRepository } = props; | |||
const { projects, projectRepositories, selectedRepository } = props; | |||
const [openProjectKeys, setOpenProjectKeys] = React.useState( | |||
projects.length > 0 ? [projects[0].key] : [] | |||
@@ -68,7 +67,6 @@ export default function BitbucketRepositories(props: BitbucketRepositoriesProps) | |||
return ( | |||
<BitbucketProjectAccordion | |||
disableRepositories={disableRepositories} | |||
key={project.key} | |||
onClick={() => handleClick(isOpen, project.key)} | |||
onSelectRepository={props.onSelectRepository} |
@@ -25,7 +25,6 @@ import { BitbucketProject, BitbucketRepository } from '../../../../types/alm-int | |||
import BitbucketProjectAccordion from './BitbucketProjectAccordion'; | |||
export interface BitbucketSearchResultsProps { | |||
disableRepositories: boolean; | |||
onSelectRepository: (repo: BitbucketRepository) => void; | |||
projects: BitbucketProject[]; | |||
searching: boolean; | |||
@@ -34,13 +33,7 @@ export interface 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) { | |||
return ( | |||
@@ -63,7 +56,6 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp | |||
<DeferredSpinner loading={searching}> | |||
{filteredSearchResults.length > 0 && ( | |||
<BitbucketProjectAccordion | |||
disableRepositories={disableRepositories} | |||
onSelectRepository={props.onSelectRepository} | |||
open | |||
repositories={filteredSearchResults} | |||
@@ -77,7 +69,6 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp | |||
return ( | |||
<BitbucketProjectAccordion | |||
disableRepositories={disableRepositories} | |||
key={project.key} | |||
onSelectRepository={props.onSelectRepository} | |||
open |
@@ -220,8 +220,8 @@ export class CreateProjectPage extends React.PureComponent<CreateProjectPageProp | |||
almInstances={bitbucketSettings} | |||
loadingBindings={loading} | |||
location={location} | |||
onProjectCreate={this.handleProjectCreate} | |||
router={router} | |||
onProjectSetupDone={this.handleProjectSetupDone} | |||
/> | |||
); | |||
} |
@@ -125,6 +125,18 @@ it('should show import project feature when PAT is already set', async () => { | |||
await user.click(radioButton); | |||
expect(importButton).toBeEnabled(); | |||
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(); | |||
}); | |||