Skip to content

Commit e03f47f

Browse files
feat(bulk-import): create bulk-import frontend plugin (#1327)
Co-authored-by: debsmita1 <[email protected]>
1 parent 81e79bf commit e03f47f

23 files changed

+467
-2
lines changed

plugins/bulk-import/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);

plugins/bulk-import/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Bulk import frontend plugin for Backstage
2+
3+
This plugin allows bulk import of multiple catalog entities into the catalog.
4+
5+
## Getting started
6+
7+
Coming soon.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
dynamicPlugins:
2+
frontend:
3+
janus-idp.backstage-plugin-bulk-import:
4+
appIcons:
5+
- name: LibraryAddOutlinedIcon
6+
module: BulkImportPlugin
7+
importName: LibraryAddOutlinedIcon
8+
dynamicRoutes:
9+
- path: /bulk-import
10+
importName: BulkImportPage
11+
module: BulkImportPlugin
12+
menuItem:
13+
icon: LibraryAddOutlinedIcon
14+
text: Bulk import

plugins/bulk-import/config.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export interface Config {}

plugins/bulk-import/dev/index.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
3+
import { createDevApp } from '@backstage/dev-utils';
4+
5+
import { BulkImportPage, bulkImportPlugin } from '../src/plugin';
6+
7+
createDevApp()
8+
.registerPlugin(bulkImportPlugin)
9+
.addPage({
10+
element: <BulkImportPage />,
11+
title: 'Bulk import',
12+
path: '/bulk-import',
13+
})
14+
.render();

plugins/bulk-import/package.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{
2+
"name": "@janus-idp/backstage-plugin-bulk-import",
3+
"version": "0.1.0",
4+
"main": "src/index.ts",
5+
"types": "src/index.ts",
6+
"license": "Apache-2.0",
7+
"publishConfig": {
8+
"access": "public",
9+
"main": "dist/index.esm.js",
10+
"types": "dist/index.d.ts"
11+
},
12+
"backstage": {
13+
"role": "frontend-plugin"
14+
},
15+
"scripts": {
16+
"build": "backstage-cli package build",
17+
"clean": "backstage-cli package clean",
18+
"export-dynamic": "janus-cli package export-dynamic-plugin",
19+
"lint": "backstage-cli package lint",
20+
"postpack": "backstage-cli package postpack",
21+
"postversion": "yarn run export-dynamic",
22+
"prepack": "backstage-cli package prepack",
23+
"start": "backstage-cli package start",
24+
"test": "backstage-cli package test --passWithNoTests --coverage",
25+
"tsc": "tsc",
26+
"ui-test": "yarn playwright test"
27+
},
28+
"dependencies": {
29+
"@backstage/core-components": "^0.14.0",
30+
"@backstage/core-plugin-api": "^1.9.0",
31+
"@backstage/theme": "^0.5.1",
32+
"@material-ui/core": "^4.9.13",
33+
"@material-ui/icons": "^4.9.1",
34+
"@material-ui/lab": "^4.0.0-alpha.61",
35+
"@mui/icons-material": "5.14.11",
36+
"react-use": "^17.2.4"
37+
},
38+
"peerDependencies": {
39+
"react": "16.13.1 || ^17.0.0 || ^18.0.0",
40+
"react-router-dom": "^6.20.0"
41+
},
42+
"devDependencies": {
43+
"@backstage/cli": "0.25.2",
44+
"@backstage/core-app-api": "1.12.0",
45+
"@backstage/dev-utils": "1.0.27",
46+
"@backstage/test-utils": "1.5.0",
47+
"@janus-idp/cli": "1.7.2",
48+
"@testing-library/jest-dom": "6.0.0",
49+
"@testing-library/react": "14.0.0",
50+
"@testing-library/user-event": "14.0.0",
51+
"@playwright/test": "1.41.2",
52+
"msw": "1.0.0"
53+
},
54+
"scalprum": {
55+
"name": "janus-idp.backstage-plugin-rbac",
56+
"exposedModules": {
57+
"RbacPlugin": "./src/index.ts"
58+
}
59+
},
60+
"files": [
61+
"dist",
62+
"dist-scalprum",
63+
"app-config.janus-idp.yaml"
64+
],
65+
"repository": "github:janus-idp/backstage-plugins",
66+
"keywords": [
67+
"backstage",
68+
"plugin"
69+
],
70+
"homepage": "https://janus-idp.io/",
71+
"bugs": "https://github.com/janus-idp/backstage-plugins/issues"
72+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
/**
4+
* See https://playwright.dev/docs/test-configuration.
5+
*/
6+
export default defineConfig({
7+
testDir: './tests',
8+
/* Run tests in files in parallel */
9+
fullyParallel: true,
10+
/* Fail the build on CI if you accidentally left test.only in the source code. */
11+
forbidOnly: !!process.env.CI,
12+
/* Retry on CI only */
13+
retries: process.env.CI ? 2 : 0,
14+
/* Run tests in sequence. */
15+
workers: 1,
16+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
17+
reporter: 'html',
18+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
19+
use: {
20+
baseURL: process.env.PLUGIN_BASE_URL || 'http://localhost:3000',
21+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
22+
trace: 'on-first-retry',
23+
screenshot: 'only-on-failure',
24+
video: 'retain-on-failure',
25+
},
26+
27+
/* Configure projects for major browsers */
28+
projects: [
29+
{
30+
name: 'chromium',
31+
use: { ...devices['Desktop Chrome'] },
32+
},
33+
],
34+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
3+
import { SidebarItem } from '@backstage/core-components';
4+
import { IconComponent } from '@backstage/core-plugin-api';
5+
6+
import LibraryAddOutlinedIcon from '@mui/icons-material/LibraryAddOutlined';
7+
8+
export const BulkImportIcon = () => {
9+
// permission logic
10+
11+
return (
12+
<SidebarItem
13+
text="Bulk import"
14+
to="bulk-import/repositories"
15+
icon={LibraryAddOutlinedIcon as IconComponent}
16+
/>
17+
);
18+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
3+
import { Header, Page, TabbedLayout } from '@backstage/core-components';
4+
5+
import { RepositoriesList } from './Repositories/RepositoriesList';
6+
7+
export const BulkImportPage = () => (
8+
<Page themeId="tool">
9+
<Header title="Bulk import" />
10+
<TabbedLayout>
11+
<TabbedLayout.Route path="/repositories" title="Repositories">
12+
<RepositoriesList />
13+
</TabbedLayout.Route>
14+
</TabbedLayout>
15+
</Page>
16+
);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
3+
import { Table } from '@backstage/core-components';
4+
5+
import { makeStyles } from '@material-ui/core';
6+
7+
import { RepositoriesData } from '../../types';
8+
import { columns } from './RepositoriesListColumns';
9+
10+
const useStyles = makeStyles(theme => ({
11+
empty: {
12+
padding: theme.spacing(2),
13+
display: 'flex',
14+
justifyContent: 'center',
15+
},
16+
}));
17+
18+
export const RepositoriesList = () => {
19+
const classes = useStyles();
20+
const data: RepositoriesData[] = [];
21+
22+
return (
23+
<Table
24+
title="Added repositories (0)"
25+
options={{ padding: 'default', search: true, paging: true }}
26+
data={data}
27+
isLoading={false}
28+
columns={columns}
29+
emptyContent={
30+
<div data-testid="repositories-table-empty" className={classes.empty}>
31+
No records found
32+
</div>
33+
}
34+
/>
35+
);
36+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
3+
import { Link, TableColumn } from '@backstage/core-components';
4+
5+
import { RepositoriesData } from '../../types';
6+
7+
export const columns: TableColumn<RepositoriesData>[] = [
8+
{
9+
title: 'Name',
10+
field: 'name',
11+
type: 'string',
12+
},
13+
{
14+
title: 'Repo URL',
15+
field: 'repoURL',
16+
type: 'string',
17+
align: 'left',
18+
render: (props: RepositoriesData) => {
19+
return <Link to={props.repoURL}>{props.repoURL}</Link>;
20+
},
21+
},
22+
{
23+
title: 'Organization',
24+
field: 'organization',
25+
type: 'string',
26+
align: 'left',
27+
render: (props: RepositoriesData) => {
28+
return <Link to={props.organization}>{props.organization}</Link>;
29+
},
30+
},
31+
{
32+
title: 'Status',
33+
field: 'status',
34+
type: 'string',
35+
align: 'left',
36+
},
37+
{
38+
title: 'Last updated',
39+
field: 'lastUpdated',
40+
type: 'string',
41+
align: 'left',
42+
},
43+
];
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { Route, Routes } from 'react-router-dom';
3+
4+
import { BulkImportPage } from '../plugin';
5+
6+
/**
7+
*
8+
* @public
9+
*/
10+
export const Router = () => (
11+
<Routes>
12+
<Route path="/repositories" element={<BulkImportPage />} />
13+
</Routes>
14+
);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { BulkImportPage } from './BulkImportPage';
2+
export { BulkImportIcon } from './BulkImportIcon';
3+
export { Router } from './Router';

plugins/bulk-import/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { bulkImportPlugin, BulkImportPage, BulkImportIcon } from './plugin';
2+
3+
export { default as LibraryAddOutlinedIcon } from '@mui/icons-material/LibraryAddOutlined';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { bulkImportPlugin } from './plugin';
2+
3+
describe('bulk-import', () => {
4+
it('should export plugin', () => {
5+
expect(bulkImportPlugin).toBeDefined();
6+
});
7+
});

plugins/bulk-import/src/plugin.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {
2+
createComponentExtension,
3+
createPlugin,
4+
createRoutableExtension,
5+
} from '@backstage/core-plugin-api';
6+
7+
import { rootRouteRef } from './routes';
8+
9+
export const bulkImportPlugin = createPlugin({
10+
id: 'bulk-import',
11+
routes: {
12+
root: rootRouteRef,
13+
},
14+
});
15+
16+
export const BulkImportPage = bulkImportPlugin.provide(
17+
createRoutableExtension({
18+
name: 'BulkImportPage',
19+
component: () => import('./components').then(m => m.BulkImportPage),
20+
mountPoint: rootRouteRef,
21+
}),
22+
);
23+
24+
export const BulkImportIcon = bulkImportPlugin.provide(
25+
createComponentExtension({
26+
name: 'BulkImportIcon',
27+
component: {
28+
lazy: () => import('./components').then(m => m.BulkImportIcon),
29+
},
30+
}),
31+
);

plugins/bulk-import/src/routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createRouteRef } from '@backstage/core-plugin-api';
2+
3+
export const rootRouteRef = createRouteRef({
4+
id: 'bulk-import',
5+
});

plugins/bulk-import/src/setupTests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@testing-library/jest-dom';

plugins/bulk-import/src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type RepositoriesData = {
2+
name: string;
3+
repoURL: string;
4+
organization: string;
5+
status: string;
6+
lastUpdated: string;
7+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { expect, Page, test } from '@playwright/test';
2+
3+
test.describe('Bulk import plugin', () => {
4+
let page: Page;
5+
6+
test.beforeAll(async ({ browser }) => {
7+
const context = await browser.newContext();
8+
page = await context.newPage();
9+
await page.goto('/');
10+
await expect(page.getByRole('link', { name: 'Bulk import' })).toBeEnabled({
11+
timeout: 20000,
12+
});
13+
});
14+
15+
test.afterAll(async ({ browser }) => {
16+
await browser.close();
17+
});
18+
19+
test('Repositories tab is shown', async () => {
20+
await expect(page.getByText('Added repositories (0)')).toBeVisible();
21+
const columns = [
22+
'Name',
23+
'Repo URL',
24+
'Organization',
25+
'Status',
26+
'Last Updated',
27+
];
28+
const thead = page.locator('thead');
29+
30+
for (const col of columns) {
31+
await expect(thead.getByText(col)).toBeVisible();
32+
}
33+
});
34+
});

plugins/bulk-import/tsconfig.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "@backstage/cli/config/tsconfig.json",
3+
"include": ["src", "dev", "migrations"],
4+
"exclude": ["node_modules"],
5+
"compilerOptions": {
6+
"outDir": "../../dist-types/plugins/bulk-import",
7+
"rootDir": "."
8+
}
9+
}

0 commit comments

Comments
 (0)