Skip to content

Commit 7a28963

Browse files
authored
fix(bulk-import): updated bulk import ui to show the correct import jobs count (#2315)
[Bulk import]: fix added repositories count
1 parent fef0f60 commit 7a28963

38 files changed

+782
-508
lines changed

.changeset/heavy-mails-doubt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@janus-idp/backstage-plugin-bulk-import": minor
3+
---
4+
5+
update bulk import ui as per the api response

plugins/bulk-import/dev/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { BulkImportPage, bulkImportPlugin } from '../src/plugin';
2727
import {
2828
APITypes,
2929
ImportJobResponse,
30+
ImportJobs,
3031
ImportJobStatus,
3132
OrgAndRepoResponse,
3233
RepositoryStatus,
@@ -78,7 +79,7 @@ class MockBulkImportApi implements BulkImportAPI {
7879
_page: number,
7980
_size: number,
8081
_seachString: string,
81-
): Promise<ImportJobStatus[]> {
82+
): Promise<ImportJobs> {
8283
return mockGetImportJobs;
8384
}
8485

@@ -112,7 +113,7 @@ class MockBulkImportApi implements BulkImportAPI {
112113
repo: string,
113114
_defaultBranch: string,
114115
): Promise<ImportJobStatus | Response> {
115-
return mockGetImportJobs.find(
116+
return mockGetImportJobs.imports.find(
116117
i => i.repository.url === repo,
117118
) as ImportJobStatus;
118119
}

plugins/bulk-import/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@material-ui/lab": "^4.0.0-alpha.61",
5353
"@mui/icons-material": "5.16.4",
5454
"@mui/material": "^5.12.2",
55+
"@tanstack/react-query": "^4.29.21",
5556
"formik": "^2.4.5",
5657
"js-yaml": "^4.1.0",
5758
"lodash": "^4.17.21",

plugins/bulk-import/src/api/BulkImportBackendClient.test.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ const handlers = [
9090
(req, res, ctx) => {
9191
const test = req.headers.get('Content-Type');
9292
if (test === 'application/json') {
93-
return res(ctx.status(200), ctx.json(mockGetImportJobs[1]));
93+
return res(
94+
ctx.status(200),
95+
ctx.json(
96+
mockGetImportJobs.imports.find(i => i.id === 'org/dessert/donut'),
97+
),
98+
);
9499
}
95100
return res(ctx.status(404));
96101
},
@@ -103,7 +108,7 @@ const handlers = [
103108
return res(
104109
ctx.status(200),
105110
ctx.json(
106-
mockGetImportJobs.filter(r =>
111+
mockGetImportJobs.imports.filter(r =>
107112
r.repository.name?.includes(searchParam),
108113
),
109114
),
@@ -138,9 +143,9 @@ const handlers = [
138143
(req, res, ctx) => {
139144
const test = req.headers.get('Content-Type');
140145
if (test === 'application/json') {
141-
return res(ctx.status(200));
146+
return res(ctx.json({ status: 200, ok: true }));
142147
}
143-
return res(ctx.status(404));
148+
return res(ctx.json({ status: 404, ok: false }));
144149
},
145150
),
146151
];
@@ -288,7 +293,9 @@ describe('BulkImportBackendClient', () => {
288293
it('getImportJobs should retrieve the import jobs based on search string', async () => {
289294
const jobs = await bulkImportApi.getImportJobs(1, 2, 'cup');
290295
expect(jobs).toEqual(
291-
mockGetImportJobs.filter(r => r.repository.name?.includes('cup')),
296+
mockGetImportJobs.imports.filter(r =>
297+
r.repository.name?.includes('cup'),
298+
),
292299
);
293300
});
294301

@@ -308,15 +315,19 @@ describe('BulkImportBackendClient', () => {
308315

309316
expect(response.status).toBe(200);
310317
});
318+
});
311319

320+
describe('getImportAction', () => {
312321
it('getImportAction should retrive the status of the repo', async () => {
313322
const response = await bulkImportApi.getImportAction(
314323
'org/dessert/donut',
315324
'master',
316325
);
317326

318327
expect(response.status).toBe(RepositoryStatus.WAIT_PR_APPROVAL);
319-
expect(response).toEqual(mockGetImportJobs[1]);
328+
expect(response).toEqual(
329+
mockGetImportJobs.imports.find(i => i.id === 'org/dessert/donut'),
330+
);
320331
});
321332
});
322333

plugins/bulk-import/src/api/BulkImportBackendClient.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
APITypes,
99
CreateImportJobRepository,
1010
ImportJobResponse,
11+
ImportJobs,
1112
ImportJobStatus,
1213
OrgAndRepoResponse,
1314
} from '../types';
@@ -25,7 +26,7 @@ export type BulkImportAPI = {
2526
page: number,
2627
size: number,
2728
searchString: string,
28-
) => Promise<ImportJobStatus[] | Response>;
29+
) => Promise<ImportJobs | Response>;
2930
createImportJobs: (
3031
importRepositories: CreateImportJobRepository[],
3132
dryRun?: boolean,
@@ -87,18 +88,19 @@ export class BulkImportBackendClient implements BulkImportAPI {
8788
const { token: idToken } = await this.identityApi.getCredentials();
8889
const backendUrl = this.configApi.getString('backend.baseUrl');
8990
const jsonResponse = await fetch(
90-
`${backendUrl}/api/bulk-import/imports?pagePerIntegration=${page}&sizePerIntegration=${size}&search=${searchString}`,
91+
`${backendUrl}/api/bulk-import/imports?page=${page}&size=${size}&search=${searchString}`,
9192
{
9293
headers: {
9394
'Content-Type': 'application/json',
9495
...(idToken && { Authorization: `Bearer ${idToken}` }),
96+
'api-version': 'v2',
9597
},
9698
},
9799
);
98-
if (jsonResponse.status !== 200 && jsonResponse.status !== 204) {
100+
if (!jsonResponse.ok) {
99101
return jsonResponse;
100102
}
101-
return jsonResponse.json();
103+
return jsonResponse.status === 204 ? null : jsonResponse.json();
102104
}
103105

104106
async createImportJobs(
@@ -136,10 +138,11 @@ export class BulkImportBackendClient implements BulkImportAPI {
136138
},
137139
},
138140
);
139-
if (jsonResponse.status !== 200 && jsonResponse.status !== 204) {
140-
return jsonResponse.json();
141+
if (!jsonResponse.ok) {
142+
const errorResponse = await jsonResponse.json();
143+
throw errorResponse.err;
141144
}
142-
return jsonResponse;
145+
return jsonResponse.status === 204 ? null : await jsonResponse.json();
143146
}
144147

145148
async getImportAction(repo: string, defaultBranch: string) {

plugins/bulk-import/src/components/AddRepositories/AddRepositoriesForm.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class MockBulkImportApi {
4949
repo: string,
5050
_defaultBranch: string,
5151
): Promise<ImportJobStatus | Response> {
52-
return mockGetImportJobs.find(
52+
return mockGetImportJobs.imports.find(
5353
i => i.repository.url === repo,
5454
) as ImportJobStatus;
5555
}

plugins/bulk-import/src/components/AddRepositories/AddRepositoriesTable.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class MockBulkImportApi {
4444
repo: string,
4545
_defaultBranch: string,
4646
): Promise<ImportJobStatus | Response> {
47-
return mockGetImportJobs.find(
47+
return mockGetImportJobs.imports.find(
4848
i => i.repository.url === repo,
4949
) as ImportJobStatus;
5050
}

plugins/bulk-import/src/components/AddRepositories/AddRepositoriesTable.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ export const AddRepositoriesTable = ({ title }: { title: string }) => {
1111
const { values } = useFormikContext<AddRepositoriesFormValues>();
1212
const [searchString, setSearchString] = React.useState<string>('');
1313
const [page, setPage] = React.useState<number>(0);
14-
14+
const handleSearch = (str: string) => {
15+
setSearchString(str);
16+
setPage(0);
17+
};
1518
return (
1619
<Box sx={{ width: '100%' }}>
1720
<Paper style={{ width: '100%' }}>
1821
<AddRepositoriesTableToolbar
1922
title={title}
20-
setSearchString={setSearchString}
23+
setSearchString={handleSearch}
2124
onPageChange={setPage}
2225
/>
2326
{values.repositoryType === RepositorySelection.Repository ? (

plugins/bulk-import/src/components/AddRepositories/AddRepositoriesTableToolbar.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
AddRepositoriesFormValues,
1111
RepositorySelection,
1212
} from '../../types';
13-
import { RepositoriesSearchBar } from './AddRepositoriesSearchBar';
13+
import { RepositoriesSearchBar } from './RepositoriesSearchBar';
1414

1515
const useStyles = makeStyles(() => ({
1616
toolbar: {
@@ -73,7 +73,11 @@ export const AddRepositoriesTableToolbar = ({
7373

7474
return (
7575
<Toolbar className={classes.toolbar}>
76-
<Typography sx={{ flex: '1 1 100%' }} variant="h5" id={title}>
76+
<Typography
77+
sx={{ flex: '1 1 100%', fontWeight: 'bold' }}
78+
variant="h5"
79+
id={title}
80+
>
7781
{`${title} (${selectedReposNumber})`}
7882
</Typography>
7983
{!activeOrganization && (

plugins/bulk-import/src/components/AddRepositories/OrganizationsColumnHeader.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ export const OrganizationsColumnHeader: TableColumn[] = [
88
},
99
{ id: 'url', title: 'URL', field: 'organizationUrl' },
1010
{
11-
id: 'selectedRepositories',
11+
id: 'selected-repositories',
1212
title: 'Selected repositories',
1313
field: 'selectedRepositories',
1414
},
1515
{
16-
id: 'catalogInfoYaml',
16+
id: 'cataloginfoyaml',
1717
title: 'catalog-info.yaml',
1818
field: 'catalogInfoYaml.status',
1919
},

plugins/bulk-import/src/components/AddRepositories/ReposSelectDrawerColumnHeader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const ReposSelectDrawerColumnHeader: TableColumn[] = [
1212
field: 'repoUrl',
1313
},
1414
{
15-
id: 'catalogInfoYaml',
15+
id: 'cataloginfoyaml',
1616
title: '',
1717
field: 'catalogInfoYaml.status',
1818
},

plugins/bulk-import/src/components/AddRepositories/RepositoriesColumnHeader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const RepositoriesColumnHeader: TableColumn[] = [
1717
field: 'organizationUrl',
1818
},
1919
{
20-
id: 'catalogInfoYaml',
20+
id: 'cataloginfoyaml',
2121
title: 'catalog-info.yaml',
2222
field: 'catalogInfoYaml.status',
2323
},

plugins/bulk-import/src/components/AddRepositories/RepositoriesHeader.tsx

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from '@material-ui/core';
1010

1111
import { Order } from '../../types';
12+
import { RepositoriesListColumns } from '../Repositories/RepositoriesListColumns';
1213
import { OrganizationsColumnHeader } from './OrganizationsColumnHeader';
1314
import { RepositoriesColumnHeader } from './RepositoriesColumnHeader';
1415
import { ReposSelectDrawerColumnHeader } from './ReposSelectDrawerColumnHeader';
@@ -22,15 +23,17 @@ export const RepositoriesHeader = ({
2223
onRequestSort,
2324
isDataLoading,
2425
showOrganizations,
26+
showImportJobs,
2527
isRepoSelectDrawer = false,
2628
}: {
27-
numSelected: number;
29+
numSelected?: number;
2830
onRequestSort: (event: React.MouseEvent<unknown>, property: any) => void;
2931
order: Order;
3032
orderBy: string | undefined;
31-
rowCount: number;
33+
rowCount?: number;
3234
isDataLoading?: boolean;
3335
showOrganizations?: boolean;
36+
showImportJobs?: boolean;
3437
isRepoSelectDrawer?: boolean;
3538
onSelectAllClick?: (event: React.ChangeEvent<HTMLInputElement>) => void;
3639
}) => {
@@ -43,37 +46,56 @@ export const RepositoriesHeader = ({
4346
if (showOrganizations) {
4447
return OrganizationsColumnHeader;
4548
}
49+
if (showImportJobs) {
50+
return RepositoriesListColumns;
51+
}
4652
if (isRepoSelectDrawer) {
4753
return ReposSelectDrawerColumnHeader;
4854
}
4955
return RepositoriesColumnHeader;
5056
};
5157

58+
const tableCellStyle = () => {
59+
if (showImportJobs) {
60+
return undefined;
61+
}
62+
if (showOrganizations) {
63+
return '15px 16px 15px 24px';
64+
}
65+
return '15px 16px 15px 6px';
66+
};
67+
5268
return (
5369
<TableHead>
5470
<TableRow>
55-
{getColumnHeader().map((headCell, index) => (
71+
{getColumnHeader()?.map((headCell, index) => (
5672
<TableCell
5773
key={headCell.id as string}
5874
align="left"
5975
padding="normal"
6076
style={{
6177
lineHeight: '1.5rem',
6278
fontSize: '0.875rem',
63-
padding: showOrganizations
64-
? '15px 16px 15px 24px'
65-
: '15px 16px 15px 6px',
79+
padding: tableCellStyle(),
6680
fontWeight: '700',
6781
}}
6882
sortDirection={orderBy === headCell.field ? order : 'asc'}
6983
>
70-
{index === 0 && !showOrganizations && (
84+
{index === 0 && !showOrganizations && !showImportJobs && (
7185
<Checkbox
7286
disableRipple
7387
color="primary"
7488
style={{ padding: '0 12px' }}
75-
indeterminate={numSelected > 0 && numSelected < rowCount}
76-
checked={rowCount > 0 && numSelected === rowCount}
89+
indeterminate={
90+
(numSelected &&
91+
rowCount &&
92+
numSelected > 0 &&
93+
numSelected < rowCount) ||
94+
false
95+
}
96+
checked={
97+
((rowCount ?? 0) > 0 && numSelected === rowCount) || false
98+
}
7799
onChange={onSelectAllClick}
78100
inputProps={{
79101
'aria-label': 'select all repositories',
@@ -85,6 +107,7 @@ export const RepositoriesHeader = ({
85107
active={orderBy === headCell.field}
86108
direction={orderBy === headCell.field ? order : 'asc'}
87109
onClick={createSortHandler(headCell.field)}
110+
disabled={headCell.sorting === false}
88111
>
89112
{headCell.title}
90113
</TableSortLabel>

plugins/bulk-import/src/components/AddRepositories/RepositoriesTable.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const RepositoriesTable = ({
3939
drawerOrganization?: string;
4040
updateSelectedReposInDrawer?: (repos: AddedRepositories) => void;
4141
}) => {
42-
const { setFieldValue, values } =
42+
const { setFieldValue, values, setStatus } =
4343
useFormikContext<AddRepositoriesFormValues>();
4444
const [order, setOrder] = React.useState<Order>('asc');
4545
const [orderBy, setOrderBy] = React.useState<string>();
@@ -200,6 +200,7 @@ export const RepositoriesTable = ({
200200

201201
const updateSelection = (newSelected: AddedRepositories) => {
202202
setSelected(newSelected);
203+
setStatus(null);
203204

204205
if (drawerOrganization && updateSelectedReposInDrawer) {
205206
// Update in the context of the drawer

0 commit comments

Comments
 (0)