From 4168c01dac574b047fd9f72951b46ed3b55e71ec Mon Sep 17 00:00:00 2001 From: Debsmita Santra Date: Fri, 17 Jul 2026 21:56:03 +0530 Subject: [PATCH] chore(bulk-import): optimize bulk-import --- .../.changeset/smart-crews-wish.md | 5 +++ .../packages/app/src/modules/nav/Sidebar.tsx | 45 ++++++++----------- .../plugins/bulk-import/report-alpha.api.md | 26 +---------- .../plugins/bulk-import/src/alpha.tsx | 37 ++------------- .../src/api/BulkImportBackendClient.ts | 2 +- .../src/components/BulkImportIcon.tsx | 39 ++++++++++++++++ .../src/components/BulkImportSidebarItem.tsx | 24 +--------- .../plugins/bulk-import/src/index.ts | 2 +- .../plugins/bulk-import/src/utils/getApi.ts | 43 ++++++++++++++++++ .../src/utils/repository-utils.tsx | 36 +++------------ 10 files changed, 122 insertions(+), 137 deletions(-) create mode 100644 workspaces/bulk-import/.changeset/smart-crews-wish.md create mode 100644 workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportIcon.tsx create mode 100644 workspaces/bulk-import/plugins/bulk-import/src/utils/getApi.ts diff --git a/workspaces/bulk-import/.changeset/smart-crews-wish.md b/workspaces/bulk-import/.changeset/smart-crews-wish.md new file mode 100644 index 00000000000..127298d0ca9 --- /dev/null +++ b/workspaces/bulk-import/.changeset/smart-crews-wish.md @@ -0,0 +1,5 @@ +--- +'@red-hat-developer-hub/backstage-plugin-bulk-import': patch +--- + +optimize bulk-import diff --git a/workspaces/bulk-import/packages/app/src/modules/nav/Sidebar.tsx b/workspaces/bulk-import/packages/app/src/modules/nav/Sidebar.tsx index 4d0943964a9..7d7058e33b9 100644 --- a/workspaces/bulk-import/packages/app/src/modules/nav/Sidebar.tsx +++ b/workspaces/bulk-import/packages/app/src/modules/nav/Sidebar.tsx @@ -20,38 +20,31 @@ import { SidebarGroup, SidebarItem, SidebarScrollWrapper, - SidebarSpace, } from '@backstage/core-components'; import { NavContentBlueprint } from '@backstage/plugin-app-react'; -import { - Settings as SidebarSettings, - UserSettingsSignInAvatar, -} from '@backstage/plugin-user-settings'; import MenuIcon from '@mui/icons-material/Menu'; import { SidebarLogo } from './SidebarLogo'; export const SidebarContent = NavContentBlueprint.make({ params: { - component: ({ items }) => ( - - - - }> - {items.map((item, index) => ( - - ))} - - - - - } - to="/settings" - > - - - - ), + component: ({ navItems }) => { + return ( + + + + }> + {navItems.rest().map(item => ( + item.icon} + /> + ))} + + + + ); + }, }, }); diff --git a/workspaces/bulk-import/plugins/bulk-import/report-alpha.api.md b/workspaces/bulk-import/plugins/bulk-import/report-alpha.api.md index 6c6dbcd5fc9..4580edaa67e 100644 --- a/workspaces/bulk-import/plugins/bulk-import/report-alpha.api.md +++ b/workspaces/bulk-import/plugins/bulk-import/report-alpha.api.md @@ -11,7 +11,6 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { FrontendModule } from '@backstage/frontend-plugin-api'; -import { IconComponent } from '@backstage/frontend-plugin-api'; import { IconElement } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -116,13 +115,13 @@ export const bulkImportTranslationRef: TranslationRef< readonly 'steps.generateCatalogInfoItems': string; readonly 'steps.editPullRequest': string; readonly 'steps.trackStatus': string; - readonly 'addRepositories.generateCatalogInfo': string; - readonly 'addRepositories.editPullRequest': string; readonly 'addRepositories.approvalTool.title': string; readonly 'addRepositories.approvalTool.tooltip': string; readonly 'addRepositories.approvalTool.github': string; readonly 'addRepositories.approvalTool.gitlab': string; readonly 'addRepositories.approvalTool.description': string; + readonly 'addRepositories.generateCatalogInfo': string; + readonly 'addRepositories.editPullRequest': string; readonly 'addRepositories.repositoryType.title': string; readonly 'addRepositories.repositoryType.group': string; readonly 'addRepositories.repositoryType.repository': string; @@ -254,27 +253,6 @@ const _default: OverridableFrontendPlugin< params: ApiFactory, ) => ExtensionBlueprintParams; }>; - 'nav-item:bulk-import': OverridableExtensionDefinition<{ - kind: 'nav-item'; - name: undefined; - config: {}; - configInput: {}; - output: ExtensionDataRef< - { - title: string; - icon: IconComponent; - routeRef: RouteRef; - }, - 'core.nav-item.target', - {} - >; - inputs: {}; - params: { - title: string; - icon: IconComponent; - routeRef: RouteRef; - }; - }>; 'page:bulk-import': OverridableExtensionDefinition<{ kind: 'page'; name: undefined; diff --git a/workspaces/bulk-import/plugins/bulk-import/src/alpha.tsx b/workspaces/bulk-import/plugins/bulk-import/src/alpha.tsx index 4640b4702e0..61822c3d83b 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/alpha.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/alpha.tsx @@ -23,7 +23,6 @@ import { createRouteRef, createSubRouteRef, identityApiRef, - NavItemBlueprint, PageBlueprint, } from '@backstage/frontend-plugin-api'; import { TranslationBlueprint } from '@backstage/plugin-app-react'; @@ -32,7 +31,7 @@ import { bulkImportApiRef, BulkImportBackendClient, } from './api/BulkImportBackendClient'; -import BulkImportIcon from './components/BulkImportSidebarItem'; +import BulkImportIcon from './components/BulkImportIcon'; import { bulkImportTranslations } from './translations'; // NFS Route References - created using @backstage/frontend-plugin-api @@ -73,41 +72,13 @@ const bulkImportPage = PageBlueprint.make({ params: { path: '/bulk-import', routeRef: rootRouteRef, + title: 'Bulk import', + icon: , noHeader: true, loader: () => import('./components').then(({ Router }) => ), }, }); -/** - * Nav Item Extension - * - * NOTE: This nav item is always visible in the sidebar. Unlike the legacy - * BulkImportSidebarItem component, NavItemBlueprint does not support runtime - * permission checking because: - * - * 1. Extension factories run at app startup, before user authentication - * 2. NavItemBlueprint only accepts static data (title, icon, routeRef) - * 3. React hooks like usePermission cannot be used in extension factories - * - * Permission checking is handled at the PAGE level instead. Users without - * the required permissions will see a permission error when accessing the page. - * - * For apps requiring permission-gated nav visibility, use the legacy - * BulkImportSidebarItem component from the main package export: - * - * @example - * ```tsx - * import { BulkImportSidebarItem } from '@red-hat-developer-hub/backstage-plugin-bulk-import'; - * ``` - */ -const bulkImportNavItem = NavItemBlueprint.make({ - params: { - title: 'Bulk import', - routeRef: rootRouteRef, - icon: BulkImportIcon, - }, -}); - /** * The Bulk Import frontend plugin for the new Backstage frontend system. * @@ -115,7 +86,7 @@ const bulkImportNavItem = NavItemBlueprint.make({ */ export default createFrontendPlugin({ pluginId: 'bulk-import', - extensions: [bulkImportApi, bulkImportPage, bulkImportNavItem], + extensions: [bulkImportApi, bulkImportPage], routes: { root: rootRouteRef, tasks: importHistoryRouteRef, diff --git a/workspaces/bulk-import/plugins/bulk-import/src/api/BulkImportBackendClient.ts b/workspaces/bulk-import/plugins/bulk-import/src/api/BulkImportBackendClient.ts index 2fe6ab707be..8f847d50377 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/api/BulkImportBackendClient.ts +++ b/workspaces/bulk-import/plugins/bulk-import/src/api/BulkImportBackendClient.ts @@ -32,7 +32,7 @@ import { OrgAndRepoResponse, SortingOrderEnum, } from '../types'; -import { getApi } from '../utils/repository-utils'; +import { getApi } from '../utils/getApi'; import { IBulkImportRESTPathProvider } from './BulkImportBackendClientBase'; import { OrchestratorBulkImportBackendClientPathProvider } from './OrchestratorBulkImportBackendClientPathProvider'; import { PRBulkImportBackendClientPathProvider } from './PRBulkImportBackendClientPathProvider'; diff --git a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportIcon.tsx b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportIcon.tsx new file mode 100644 index 00000000000..b7e7d594c9a --- /dev/null +++ b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportIcon.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useTheme } from '@mui/material/styles'; + +import bulkImportBlackImg from '../images/BulkImportIcon-Black.svg'; +import bulkImportWhiteImg from '../images/BulkImportIcon-White.svg'; + +/** + * @public + * Bulk Import Icon (kept free of sidebar/permission deps for NFS sync). + */ +export const BulkImportIcon = () => { + const theme = useTheme(); + const isDarkTheme = theme.palette.mode === 'dark'; + + return ( + bulk import icon + ); +}; + +export default BulkImportIcon; diff --git a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx index 68b0ace81fb..f357c787395 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx @@ -18,32 +18,12 @@ import { SidebarItem } from '@backstage/core-components'; import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { usePermission } from '@backstage/plugin-permission-react'; -import { useTheme } from '@mui/material/styles'; - import { bulkImportPermission } from '@red-hat-developer-hub/backstage-plugin-bulk-import-common'; import { useTranslation } from '../hooks/useTranslation'; -import { getImageForIconClass } from '../utils/icons'; - -/** - * @public - * Bulk Import Icon - */ -export const BulkImportIcon = () => { - const theme = useTheme(); - const isDarkTheme = theme.palette.mode === 'dark'; - const iconClass = isDarkTheme - ? 'icon-bulk-import-white' - : 'icon-bulk-import-black'; +import { BulkImportIcon } from './BulkImportIcon'; - return ( - bulk import icon - ); -}; +export { BulkImportIcon } from './BulkImportIcon'; export const BulkImportSidebarItem = () => { const { t } = useTranslation(); diff --git a/workspaces/bulk-import/plugins/bulk-import/src/index.ts b/workspaces/bulk-import/plugins/bulk-import/src/index.ts index ee7761593d0..93d27b78172 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/index.ts +++ b/workspaces/bulk-import/plugins/bulk-import/src/index.ts @@ -28,4 +28,4 @@ export { BulkImportSidebarItem, } from './plugin'; -export { default as BulkImportIcon } from './components/BulkImportSidebarItem'; +export { default as BulkImportIcon } from './components/BulkImportIcon'; diff --git a/workspaces/bulk-import/plugins/bulk-import/src/utils/getApi.ts b/workspaces/bulk-import/plugins/bulk-import/src/utils/getApi.ts new file mode 100644 index 00000000000..73b75af94ca --- /dev/null +++ b/workspaces/bulk-import/plugins/bulk-import/src/utils/getApi.ts @@ -0,0 +1,43 @@ +/* + * Copyright Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { APITypes } from '../types'; + +/** Builds bulk-import backend list URLs (kept out of repository-utils UI graph). */ +export const getApi = ( + backendUrl: string, + page: number, + size: number, + searchString: string, + approvalTool: string, + options?: APITypes, +) => { + const params = new URLSearchParams({ + pagePerIntegration: String(page), + sizePerIntegration: String(size), + search: searchString, + approvalTool, + }); + + if (options?.fetchOrganizations) { + return `${backendUrl}/api/bulk-import/organizations?${params.toString()}`; + } + if (options?.orgName) { + const orgName = encodeURIComponent(options?.orgName); + return `${backendUrl}/api/bulk-import/organizations/${orgName}/repositories?${params.toString()}`; + } + return `${backendUrl}/api/bulk-import/repositories?${params.toString()}`; +}; diff --git a/workspaces/bulk-import/plugins/bulk-import/src/utils/repository-utils.tsx b/workspaces/bulk-import/plugins/bulk-import/src/utils/repository-utils.tsx index 80a2080df4b..a8b49bc7c47 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/utils/repository-utils.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/utils/repository-utils.tsx @@ -19,16 +19,15 @@ import { Link, StatusOK } from '@backstage/core-components'; import { configApiRef, useApi } from '@backstage/core-plugin-api'; import Typography from '@mui/material/Typography'; -import * as jsyaml from 'js-yaml'; +import { loadAll } from 'js-yaml'; import { get } from 'lodash'; -import * as yaml from 'yaml'; +import { stringify } from 'yaml'; import * as yup from 'yup'; import { WaitingForPR } from '../components/WaitingForPR'; import { AddedRepositories, AddRepositoryData, - APITypes, ApprovalTool, CreateImportJobRepository, ErrorType, @@ -50,6 +49,8 @@ import { import { getWorkflowStatusInfo } from './orchestratorStatus'; import { getTaskStatusInfo } from './task-status'; +export { getApi } from './getApi'; + export const TaskLink = ({ taskId, t, @@ -521,7 +522,7 @@ export const prepareDataForSubmission = ( organization: repo.orgName || '', defaultBranch: repo.defaultBranch || '', }, - catalogInfoContent: yaml.stringify( + catalogInfoContent: stringify( repo.catalogInfoYaml?.prTemplate?.yaml, null, 2, @@ -540,31 +541,6 @@ export const prepareDataForSubmission = ( [], ); -export const getApi = ( - backendUrl: string, - page: number, - size: number, - searchString: string, - approvalTool: string, - options?: APITypes, -) => { - const params = new URLSearchParams({ - pagePerIntegration: String(page), - sizePerIntegration: String(size), - search: searchString, - approvalTool, - }); - - if (options?.fetchOrganizations) { - return `${backendUrl}/api/bulk-import/organizations?${params.toString()}`; - } - if (options?.orgName) { - const orgName = encodeURIComponent(options?.orgName); - return `${backendUrl}/api/bulk-import/organizations/${orgName}/repositories?${params.toString()}`; - } - return `${backendUrl}/api/bulk-import/repositories?${params.toString()}`; -}; - export const getCustomisedErrorMessage = ( status: (RepositoryStatus | string)[] | undefined, t: (key: string) => string, @@ -636,7 +612,7 @@ export const evaluatePRTemplate = ( ): { pullReqPreview: PullRequestPreview; isInvalidEntity: boolean } => { const gitProvider = isGithubJob(repositoryStatus) ? 'github' : 'gitlab'; try { - const entity = jsyaml.loadAll( + const entity = loadAll( repositoryStatus[gitProvider]?.pullRequest.catalogInfoContent ?? '', )[0] as Entity; const isInvalid =