ソースを参照

SONAR-13900 Display BBS repo search results even if project is unknown

tags/8.5.0.37579
Wouter Admiraal 3年前
コミット
1055024bf5

+ 60
- 57
server/sonar-web/src/main/js/apps/create/project/BitbucketProjectAccordion.tsx ファイルの表示

@@ -36,7 +36,7 @@ export interface BitbucketProjectAccordionProps {
onClick?: () => void;
onSelectRepository: (repo: BitbucketRepository) => void;
open: boolean;
project: BitbucketProject;
project?: BitbucketProject;
repositories: BitbucketRepository[];
selectedRepository?: BitbucketRepository;
showingAllRepositories: boolean;
@@ -54,6 +54,8 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi

const repositoryCount = repositories.length;

const title = project?.name ?? translate('search_results');

return (
<BoxedGroupAccordion
className={classNames('big-spacer-bottom', {
@@ -61,7 +63,6 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
'not-clickable': !props.onClick,
'no-hover': !props.onClick
})}
key={project.key}
onClick={
props.onClick
? props.onClick
@@ -70,64 +71,66 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
}
}
open={open}
title={<h3>{project.name}</h3>}>
title={<h3>{title}</h3>}>
{open && (
<div className="display-flex-wrap">
{repositoryCount === 0 && (
<Alert variant="warning">
<FormattedMessage
defaultMessage={translate('onboarding.create_project.no_bbs_repos')}
id="onboarding.create_project.no_bbs_repos"
values={{
link: (
<Link
to={{
pathname: '/projects/create',
query: { mode: CreateProjectModes.BitbucketServer, resetPat: 1 }
}}>
{translate('onboarding.create_project.update_your_token')}
</Link>
)
}}
/>
</Alert>
)}
<>
<div className="display-flex-wrap">
{repositoryCount === 0 && (
<Alert variant="warning">
<FormattedMessage
defaultMessage={translate('onboarding.create_project.no_bbs_repos')}
id="onboarding.create_project.no_bbs_repos"
values={{
link: (
<Link
to={{
pathname: '/projects/create',
query: { mode: CreateProjectModes.BitbucketServer, resetPat: 1 }
}}>
{translate('onboarding.create_project.update_your_token')}
</Link>
)
}}
/>
</Alert>
)}

{repositories.map(repo =>
repo.sqProjectKey ? (
<div
className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
key={repo.id}>
<CheckIcon className="spacer-right" fill={colors.green} size={14} />
<div className="overflow-hidden">
<div className="little-spacer-bottom text-ellipsis">
<strong title={repo.name}>
<Link to={getProjectUrl(repo.sqProjectKey)}>{repo.name}</Link>
</strong>
{repositories.map(repo =>
repo.sqProjectKey ? (
<div
className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
key={repo.id}>
<CheckIcon className="spacer-right" fill={colors.green} size={14} />
<div className="overflow-hidden">
<div className="little-spacer-bottom text-ellipsis">
<strong title={repo.name}>
<Link to={getProjectUrl(repo.sqProjectKey)}>{repo.name}</Link>
</strong>
</div>
<em>{translate('onboarding.create_project.repository_imported')}</em>
</div>
<em>{translate('onboarding.create_project.repository_imported')}</em>
</div>
</div>
) : (
<Radio
checked={selectedRepository?.id === repo.id}
className={classNames(
'display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden',
{
disabled: disableRepositories,
'text-muted': disableRepositories,
'link-no-underline': disableRepositories
}
)}
key={repo.id}
onCheck={() => props.onSelectRepository(repo)}
value={String(repo.id)}>
<strong className="text-ellipsis" title={repo.name}>
{repo.name}
</strong>
</Radio>
)
)}
) : (
<Radio
checked={selectedRepository?.id === repo.id}
className={classNames(
'display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden',
{
disabled: disableRepositories,
'text-muted': disableRepositories,
'link-no-underline': disableRepositories
}
)}
key={repo.id}
onCheck={() => props.onSelectRepository(repo)}
value={String(repo.id)}>
<strong className="text-ellipsis" title={repo.name}>
{repo.name}
</strong>
</Radio>
)
)}
</div>

{!showingAllRepositories && repositoryCount > 0 && (
<Alert variant="warning">
@@ -137,7 +140,7 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
)}
</Alert>
)}
</div>
</>
)}
</BoxedGroupAccordion>
);

+ 26
- 5
server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx ファイルの表示

@@ -34,6 +34,7 @@ import {
} from '../../../types/alm-integration';
import { AlmSettingsInstance } from '../../../types/alm-settings';
import BitbucketCreateProjectRenderer from './BitbucketProjectCreateRenderer';
import { DEFAULT_BBS_PAGE_SIZE } from './constants';

interface Props extends Pick<WithRouterProps, 'location'> {
canAdmin: boolean;
@@ -146,11 +147,31 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S
return Promise.all(
projects.map(p => {
return getBitbucketServerRepositories(bitbucketSetting.key, p.name).then(
({ isLastPage, repositories }) => ({
isLastPage,
repositories,
projectKey: p.key
})
({ 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
// example, p.name = "A1" would find repositories for projects "A1",
// "A10", "A11", etc. This is a limitation of BBS. To make sure we
// don't display incorrect information, filter on the project key.
const filteredRepositories = repositories.filter(r => r.projectKey === p.key);

// And because of the above, the "isLastPage" cannot be relied upon
// either. This one is impossible to get 100% for now. We can only
// make some assumptions: by default, the page size for BBS is 25
// (this is not part of the payload, so we don't know the actual
// number; but changing this implies changing some advanced config,
// so it's not likely). If the filtered repos is larger than this
// number AND isLastPage is false, we'll keep it at false.
// Otherwise, we assume it's true.
const realIsLastPage =
isLastPage || filteredRepositories.length < DEFAULT_BBS_PAGE_SIZE;

return {
repositories: filteredRepositories,
isLastPage: realIsLastPage,
projectKey: p.key
};
}
);
})
).then(results => {

+ 20
- 5
server/sonar-web/src/main/js/apps/create/project/BitbucketSearchResults.tsx ファイルの表示

@@ -44,17 +44,32 @@ export default function BitbucketSearchResults(props: BitbucketSearchResultsProp
selectedRepository
} = props;

if (searchResults.length === 0 && !searching) {
return (
<Alert className="big-spacer-top" variant="warning">
{translate('onboarding.create_project.no_bbs_repos.filter')}
</Alert>
);
}

const filteredProjects = uniq(
searchResults.map(r => projects.find(p => p.key === r.projectKey)).filter(isDefined)
);

return filteredProjects.length === 0 && !searching ? (
<Alert className="big-spacer-top" variant="warning">
{translate('onboarding.create_project.no_bbs_repos.filter')}
</Alert>
) : (
return (
<div className="big-spacer-top">
<DeferredSpinner loading={searching}>
{filteredProjects.length === 0 && searchResults.length > 0 && (
<BitbucketProjectAccordion
disableRepositories={disableRepositories}
onSelectRepository={props.onSelectRepository}
open={true}
repositories={searchResults}
selectedRepository={selectedRepository}
showingAllRepositories={true}
/>
)}

{filteredProjects.map(project => {
const repositories = searchResults.filter(r => r.projectKey === project.key);


+ 1
- 0
server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectAccordion-test.tsx ファイルの表示

@@ -39,6 +39,7 @@ it('should render correctly', () => {
'selected repo'
);
expect(shallowRender({ showingAllRepositories: false })).toMatchSnapshot('not showing all repos');
expect(shallowRender({ project: undefined })).toMatchSnapshot('no project info');
});

it('should correctly handle selecting repos', () => {

+ 3
- 0
server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketSearchResults-test.tsx ファイルの表示

@@ -32,6 +32,9 @@ it('should render correctly', () => {
shallowRender({ searching: true, projects: undefined, searchResults: undefined })
).toMatchSnapshot('searching');
expect(shallowRender({ searchResults: undefined })).toMatchSnapshot('no results');
expect(
shallowRender({ searchResults: [mockBitbucketRepository({ projectKey: 'unknown' })] })
).toMatchSnapshot('unknown project in search results');
});

function shallowRender(props: Partial<BitbucketSearchResultsProps> = {}) {

+ 77
- 12
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectAccordion-test.tsx.snap ファイルの表示

@@ -3,7 +3,6 @@
exports[`should render correctly: closed 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom"
key="project"
onClick={[MockFunction]}
open={false}
title={
@@ -17,7 +16,6 @@ exports[`should render correctly: closed 1`] = `
exports[`should render correctly: default 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open"
key="project"
onClick={[MockFunction]}
open={true}
title={
@@ -90,7 +88,6 @@ exports[`should render correctly: default 1`] = `
exports[`should render correctly: disable options 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open"
key="project"
onClick={[MockFunction]}
open={true}
title={
@@ -163,7 +160,6 @@ exports[`should render correctly: disable options 1`] = `
exports[`should render correctly: no click handler 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open not-clickable no-hover"
key="project"
onClick={[Function]}
open={true}
title={
@@ -233,10 +229,81 @@ exports[`should render correctly: no click handler 1`] = `
</BoxedGroupAccordion>
`;

exports[`should render correctly: no project info 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open"
onClick={[MockFunction]}
open={true}
title={
<h3>
search_results
</h3>
}
>
<div
className="display-flex-wrap"
>
<Radio
checked={false}
className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo overflow-hidden"
key="1"
onCheck={[Function]}
value="1"
>
<strong
className="text-ellipsis"
title="Repo"
>
Repo
</strong>
</Radio>
<div
className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
key="2"
>
<CheckIcon
className="spacer-right"
fill="#00aa00"
size={14}
/>
<div
className="overflow-hidden"
>
<div
className="little-spacer-bottom text-ellipsis"
>
<strong
title="Bar"
>
<Link
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/dashboard",
"query": Object {
"branch": undefined,
"id": "bar",
},
}
}
>
Bar
</Link>
</strong>
</div>
<em>
onboarding.create_project.repository_imported
</em>
</div>
</div>
</div>
</BoxedGroupAccordion>
`;

exports[`should render correctly: no repos 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open"
key="project"
onClick={[MockFunction]}
open={true}
title={
@@ -282,7 +349,6 @@ exports[`should render correctly: no repos 1`] = `
exports[`should render correctly: not showing all repos 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open"
key="project"
onClick={[MockFunction]}
open={true}
title={
@@ -348,19 +414,18 @@ exports[`should render correctly: not showing all repos 1`] = `
</em>
</div>
</div>
<Alert
variant="warning"
>
onboarding.create_project.only_showing_X_first_repos.2
</Alert>
</div>
<Alert
variant="warning"
>
onboarding.create_project.only_showing_X_first_repos.2
</Alert>
</BoxedGroupAccordion>
`;

exports[`should render correctly: selected repo 1`] = `
<BoxedGroupAccordion
className="big-spacer-bottom open"
key="project"
onClick={[MockFunction]}
open={true}
title={

+ 27
- 0
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketSearchResults-test.tsx.snap ファイルの表示

@@ -53,3 +53,30 @@ exports[`should render correctly: searching 1`] = `
/>
</div>
`;

exports[`should render correctly: unknown project in search results 1`] = `
<div
className="big-spacer-top"
>
<DeferredSpinner
loading={false}
>
<BitbucketProjectAccordion
disableRepositories={false}
onSelectRepository={[MockFunction]}
open={true}
repositories={
Array [
Object {
"id": 1,
"name": "Repo",
"projectKey": "unknown",
"slug": "project__repo",
},
]
}
showingAllRepositories={true}
/>
</DeferredSpinner>
</div>
`;

+ 2
- 0
server/sonar-web/src/main/js/apps/create/project/constants.ts ファイルの表示

@@ -19,3 +19,5 @@
*/

export const PROJECT_NAME_MAX_LEN = 255;

export const DEFAULT_BBS_PAGE_SIZE = 25;

+ 1
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties ファイルの表示

@@ -168,6 +168,7 @@ review=Review
rule=Rule
rules=Rules
save=Save
search_results=Search results
search_verb=Search
see_all=See all
select_verb=Select

読み込み中…
キャンセル
保存