diff --git a/.changeset/config.json b/.changeset/config.json index d1cf79be4..978497a0d 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -27,7 +27,8 @@ "@openchoreo/backstage-plugin-openchoreo-workflows-backend", "@openchoreo/backstage-plugin-thunder-idp-client-node", "@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users", - "@openchoreo/cell-diagram" + "@openchoreo/cell-diagram", + "@openchoreo/backstage-portal-backend" ] ], "access": "restricted", diff --git a/.changeset/portal-backend-package.md b/.changeset/portal-backend-package.md new file mode 100644 index 000000000..cf1c502b8 --- /dev/null +++ b/.changeset/portal-backend-package.md @@ -0,0 +1,11 @@ +--- +'@openchoreo/backstage-portal-backend': minor +--- + +Add `@openchoreo/backstage-portal-backend` — the stock portal's backend +composition (feature bundle + IDP root-router middleware) as a reusable +published package. The monorepo backend now consumes it; runtime behavior is +unchanged. The app shell also moves into the private +`@openchoreo/backstage-portal-app` package (`createPortalApp()`), leaving +`packages/app` as a thin consumer — the shared base for the new-frontend-system +migration and the custom-portal scaffold. diff --git a/packages/app/package.json b/packages/app/package.json index b80511932..49a2f31fb 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -19,86 +19,18 @@ "lint": "backstage-cli package lint" }, "dependencies": { - "@backstage-community/plugin-github-actions": "^0.20.0", - "@backstage-community/plugin-jenkins": "^0.28.0", - "@backstage/app-defaults": "^1.7.8", - "@backstage/catalog-client": "^1.15.1", - "@backstage/catalog-model": "^1.9.0", "@backstage/cli": "^0.36.2", - "@backstage/config": "^1.3.8", - "@backstage/core-app-api": "^1.20.1", - "@backstage/core-compat-api": "^0.5.11", - "@backstage/core-components": "^0.18.10", - "@backstage/core-plugin-api": "^1.12.6", - "@backstage/frontend-app-api": "^0.16.3", - "@backstage/frontend-defaults": "^0.5.2", - "@backstage/frontend-plugin-api": "^0.17.0", - "@backstage/integration-react": "^1.2.18", - "@backstage/plugin-api-docs": "^0.14.1", - "@backstage/plugin-app": "^0.4.6", - "@backstage/plugin-app-react": "^0.2.3", - "@backstage/plugin-catalog": "^2.0.5", - "@backstage/plugin-catalog-common": "^1.1.10", - "@backstage/plugin-catalog-graph": "^0.6.4", - "@backstage/plugin-catalog-import": "^0.13.13", - "@backstage/plugin-catalog-react": "^3.0.0", - "@backstage/plugin-home": "^0.9.6", - "@backstage/plugin-kubernetes": "^0.12.19", - "@backstage/plugin-org": "^0.7.4", - "@backstage/plugin-permission-common": "^0.9.9", - "@backstage/plugin-permission-react": "^0.5.1", - "@backstage/plugin-scaffolder": "^1.37.0", - "@backstage/plugin-scaffolder-common": "^2.2.0", - "@backstage/plugin-scaffolder-react": "^2.0.0", - "@backstage/plugin-search": "^1.7.4", - "@backstage/plugin-search-react": "^1.11.4", - "@backstage/plugin-techdocs": "^1.17.6", - "@backstage/plugin-techdocs-common": "^0.1.1", - "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.36", - "@backstage/plugin-techdocs-react": "^1.3.11", - "@backstage/plugin-user-settings": "^0.9.3", - "@backstage/theme": "^0.7.3", "@backstage/ui": "^0.15.0", - "@gitbeaker/rest": "^40.6.0", - "@immobiliarelabs/backstage-plugin-gitlab": "^6.13.0", - "@material-ui/core": "4.12.4", - "@material-ui/icons": "4.11.3", - "@material-ui/lab": "4.0.0-alpha.61", - "@openchoreo/backstage-design-system": "workspace:^", - "@openchoreo/backstage-plugin": "workspace:^", - "@openchoreo/backstage-plugin-common": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-ci": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-observability": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-portal-assistant": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-workflows": "workspace:^", - "@openchoreo/backstage-plugin-platform-engineer-core": "workspace:^", - "@openchoreo/backstage-plugin-react": "workspace:^", - "@rjsf/core": "5.24.13", - "@rjsf/material-ui": "5.24.13", - "@rjsf/utils": "5.24.13", - "@rjsf/validator-ajv8": "5.24.13", - "@tanstack/react-query": "^5.101.2", - "clsx": "^2.1.1", - "d3-selection": "^3.0.0", - "d3-zoom": "^3.0.0", - "devicons-react": "^1.5.0", - "json-schema": "0.4.0", + "@openchoreo/backstage-portal-app": "workspace:^", "react": "18.3.1", - "react-dom": "18.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", - "react-use": "17.6.0", - "yaml": "^2.8.1" + "react-dom": "18.3.1" }, "devDependencies": { "@axe-core/playwright": "^4.11.3", - "@backstage/frontend-test-utils": "^0.6.0", - "@backstage/test-utils": "^1.7.18", "@playwright/test": "1.56.0", "@testing-library/dom": "9.3.4", "@testing-library/jest-dom": "6.9.1", "@testing-library/react": "14.3.1", - "@testing-library/user-event": "14.6.1", "@types/react-dom": "*", "cross-env": "7.0.3" }, diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index a719e52a9..fb3ccdc28 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -1,211 +1,3 @@ -import { Route } from 'react-router-dom'; -import { catalogPlugin } from '@backstage/plugin-catalog'; -import { catalogImportPlugin } from '@backstage/plugin-catalog-import'; -import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; -import { SearchPage } from '@backstage/plugin-search'; -import { - TechDocsIndexPage, - techdocsPlugin, - TechDocsReaderPage, -} from '@backstage/plugin-techdocs'; -import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; -import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; -import { apis } from './apis'; -import { CustomApiExplorerPage } from './components/catalog/CustomApiExplorerPage'; -import { CustomCatalogImportPage } from './components/catalog/CustomCatalogImportPage'; -import { searchPage } from './components/search/SearchPage'; -import { Root } from './components/Root'; -import { HomePage } from './components/Home'; -import { CustomGraphNode } from '@openchoreo/backstage-plugin-react'; -import { PageLoader } from '@openchoreo/backstage-design-system'; -import { PlatformOverviewPage } from './components/platformOverview'; +import { createPortalApp } from '@openchoreo/backstage-portal-app'; -import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; -import { createApp } from '@backstage/frontend-defaults'; -import { - convertLegacyAppOptions, - convertLegacyAppRoot, -} from '@backstage/core-compat-api'; -import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; - -// NFS plugin features (created in Step 2 — each plugin's `/alpha` exports a -// `createFrontendPlugin` instance). These replace the API factory entries -// that previously lived in `apis.ts`. -import openchoreoPluginAlpha from '@openchoreo/backstage-plugin/alpha'; -import openchoreoCiPluginAlpha from '@openchoreo/backstage-plugin-openchoreo-ci/alpha'; -import openchoreoObservabilityPluginAlpha from '@openchoreo/backstage-plugin-openchoreo-observability/alpha'; -import openchoreoWorkflowsPluginAlpha from '@openchoreo/backstage-plugin-openchoreo-workflows/alpha'; -import platformEngineerCorePluginAlpha from '@openchoreo/backstage-plugin-platform-engineer-core/alpha'; - -// Upstream NFS plugin features with our overrides: -// - catalog graph default API replaced to include OpenChoreo custom relations -// - catalog entity-presentation default API replaced to add custom kind icons -// - scaffolder `page:scaffolder` disabled (our legacy wins) -// and form-decorators API replaced to inject the openChoreoTokenDecorator -// - customTranslationsModule reinstates the catalog-import header overrides -// that previously rode via createApp.__experimentalTranslations -import { - catalogGraphPluginAlpha, - catalogPluginAlpha, - customAppModule, - scaffolderPluginAlpha as upstreamScaffolderPluginAlpha, -} from './apis/customOverrides'; - -// catalog-import NFS plugin — registered so the `/catalog-import` route ref -// resolves under NFS. Our legacy `` -// mount in `` provides the actual page rendering. -import catalogImportPluginAlpha from '@backstage/plugin-catalog-import/alpha'; -// api-docs and kubernetes NFS plugins — registered so that `apiDocsConfigRef`, -// `kubernetesApiRef`, etc. are present in the api holder. The host owns the -// `/api-docs` route (CustomApiExplorerPage) and the Kubernetes entity tab -// reuses upstream `EntityKubernetesContent`; without these features the apis -// they depend on are absent and the tabs throw `NotImplementedError`. -import apiDocsPluginAlpha from '@backstage/plugin-api-docs/alpha'; -import kubernetesPluginAlpha from '@backstage/plugin-kubernetes/alpha'; -import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; -import { RequirePermission } from '@backstage/plugin-permission-react'; -import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; -import { appThemes } from './themes'; -import { LEGACY_KIND_ICONS } from './kindIcons'; -import { - AccessControlContent, - SecretsContent, - ExecTerminalWindowPage, -} from '@openchoreo/backstage-plugin'; -import { - UserSettingsPage, - SettingsLayout, - UserSettingsGeneral, -} from '@backstage/plugin-user-settings'; -import { VisitListener } from '@backstage/plugin-home'; - -const legacyAppOptions = convertLegacyAppOptions({ - apis, - icons: LEGACY_KIND_ICONS, - themes: appThemes, -}); - -const routes = ( - - } /> - {/* - `/catalog` is owned by the NFS `page:catalog` extension and - `/catalog/:namespace/:kind/:name` by `page:catalog/entity` — see - customOverrides.tsx, which overrides each loader to render the - host's `` and the legacy `entityPage` JSX - respectively. The legacy `` mount used to - live here but double-rendered the catalog header under the NFS - compat shim. - */} - } /> - } - > - - - - - {/* - `/create` is owned by the NFS `page:scaffolder` extension — see - customOverrides.tsx, which overrides its loader to render - `` (the host's `` with - the 27 field-extension children and `CustomTemplateListPage` / - `CustomReviewStep` components). The legacy `` - mount used to live here but double-rendered the scaffolder header - under the NFS compat shim. - */} - } /> - - - - } - /> - }> - {searchPage} - - }> - - - - - - - - - - - - - } - /> - } /> - {/* - Standalone full-window exec terminal, opened in a new browser tab from the - resource drawer. The page renders a fixed viewport overlay over the app - chrome, so the terminal uses the entire window. - */} - } /> - -); - -const legacyRoot = convertLegacyAppRoot( - <> - - - - - {routes} - - , -); - -const app = createApp({ - features: [ - // `...legacyRoot` re-emits each legacy plugin's `apis: [...]` array as - // ApiBlueprint extensions under the legacy plugin's own pluginId - // (collectLegacyRoutes). The NFS api-factory registry resolves - // same-pluginId factories last-write-wins, so the override features - // below MUST come after `...legacyRoot` to win the contest. Otherwise - // our custom catalog-graph relations, entity-presentation kind icons, - // and scaffolder form-decorator override get silently overwritten by - // upstream defaults at startup. - legacyAppOptions, - ...legacyRoot, - customAppModule, - upstreamScaffolderPluginAlpha, - catalogGraphPluginAlpha, - catalogPluginAlpha, - catalogImportPluginAlpha, - apiDocsPluginAlpha, - kubernetesPluginAlpha, - openchoreoPluginAlpha, - openchoreoCiPluginAlpha, - openchoreoObservabilityPluginAlpha, - openchoreoWorkflowsPluginAlpha, - platformEngineerCorePluginAlpha, - ], - bindRoutes({ bind }) { - bind(catalogPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - viewTechDoc: techdocsPlugin.routes.docRoot, - createFromTemplate: scaffolderPlugin.routes.selectedTemplate, - }); - bind(scaffolderPlugin.externalRoutes, { - registerComponent: catalogImportPlugin.routes.importPage, - viewTechDoc: techdocsPlugin.routes.docRoot, - }); - }, - advanced: { - // App-boot loader (waiting for config/features) — use our centered loader - // instead of the default Backstage progress bar. - loadingElement: , - }, -}); - -export default app.createRoot(); +export default createPortalApp().createRoot(); diff --git a/packages/backend/package.json b/packages/backend/package.json index bd41c8809..b11ffb968 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -21,46 +21,18 @@ "build-image": "docker buildx build ../.. -f Dockerfile --tag openchoreo-ui:local --load" }, "dependencies": { - "@backstage-community/plugin-jenkins-backend": "^0.24.1", "@backstage/backend-defaults": "^0.17.1", "@backstage/backend-plugin-api": "^1.9.1", "@backstage/config": "^1.3.8", - "@backstage/plugin-app-backend": "^0.5.14", - "@backstage/plugin-auth-backend": "^0.29.0", - "@backstage/plugin-auth-backend-module-github-provider": "^0.5.3", - "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.19", "@backstage/plugin-auth-node": "^0.7.1", - "@backstage/plugin-catalog-backend": "^3.7.0", - "@backstage/plugin-catalog-backend-module-logs": "^0.1.22", - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.20", - "@backstage/plugin-events-backend": "^0.6.2", "@backstage/plugin-kubernetes-backend": "^0.21.4", - "@backstage/plugin-permission-backend": "^0.7.12", "@backstage/plugin-permission-common": "^0.9.9", "@backstage/plugin-permission-node": "^0.11.0", - "@backstage/plugin-proxy-backend": "^0.6.13", - "@backstage/plugin-scaffolder-backend": "^4.0.0", - "@backstage/plugin-scaffolder-backend-module-github": "^0.9.9", - "@backstage/plugin-search-backend": "^2.1.2", - "@backstage/plugin-search-backend-module-catalog": "^0.3.15", - "@backstage/plugin-search-backend-module-pg": "^0.5.55", "@backstage/plugin-search-backend-module-techdocs": "^0.4.14", "@backstage/plugin-search-backend-node": "^1.4.4", - "@backstage/plugin-techdocs-backend": "^2.2.0", - "@backstage/plugin-user-settings-backend": "^0.4.3", "@immobiliarelabs/backstage-plugin-gitlab-backend": "^6.13.0", - "@openchoreo/backstage-plugin-auth-backend-module-openchoreo-auth": "workspace:^", - "@openchoreo/backstage-plugin-backend": "workspace:^", - "@openchoreo/backstage-plugin-catalog-backend-module": "workspace:^", - "@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-ci-backend": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-observability-backend": "workspace:^", "@openchoreo/backstage-plugin-openchoreo-portal-assistant-backend": "workspace:^", - "@openchoreo/backstage-plugin-openchoreo-workflows-backend": "workspace:^", - "@openchoreo/backstage-plugin-permission-backend-module-openchoreo-policy": "workspace:^", - "@openchoreo/backstage-plugin-platform-engineer-core-backend": "workspace:^", - "@openchoreo/backstage-plugin-scaffolder-backend-module": "workspace:^", - "@openchoreo/openchoreo-auth": "workspace:^", + "@openchoreo/backstage-portal-backend": "workspace:^", "app": "workspace:^", "better-sqlite3": "9.6.0", "cookie-parser": "1.4.7", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index eb6e2d7c9..22133b5e3 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -7,125 +7,35 @@ */ import { createBackend } from '@backstage/backend-defaults'; -import { OpenChoreoAuthModule } from '@openchoreo/backstage-plugin-auth-backend-module-openchoreo-auth'; -import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter'; import { - immediateCatalogServiceFactory, - annotationStoreFactory, -} from '@openchoreo/backstage-plugin-catalog-backend-module'; -import { createIdpTokenHeaderMiddleware } from '@openchoreo/openchoreo-auth'; + portalBackendFeatures, + portalRootHttpRouterServiceFactory, +} from '@openchoreo/backstage-portal-backend'; const backend = createBackend(); -// Configure root HTTP router with IDP token header middleware -// This middleware reads the IDP token from headers and makes it available -// to ALL routes via AsyncLocalStorage, which is critical for the permission -// system to access the user's IDP token when making authorization decisions. -backend.add( - rootHttpRouterServiceFactory({ - configure: ({ app, applyDefaults }) => { - // IDP token middleware — reads the IDP token from the x-openchoreo-token - // header and establishes the AsyncLocalStorage context so - // getUserTokenFromContext() works in the permission policy and elsewhere. - // Registered before applyDefaults() so it wraps all route handlers. - app.use(createIdpTokenHeaderMiddleware()); - - // applyDefaults() applies the standard middleware stack ONCE: - // helmet, cors, compression, logging, rateLimit, then routes + error - // handling. Do NOT re-apply any of these manually here. - applyDefaults(); - }, - }), -); -backend.add(immediateCatalogServiceFactory); -backend.add(annotationStoreFactory); - -backend.add(import('@backstage/plugin-app-backend')); -backend.add(import('@backstage/plugin-proxy-backend')); -backend.add(import('@backstage/plugin-scaffolder-backend')); -backend.add(import('@backstage/plugin-scaffolder-backend-module-github')); -backend.add(import('@backstage/plugin-techdocs-backend')); - -// auth plugin -backend.add(import('@backstage/plugin-auth-backend')); -// See https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin - -// Auth providers - both registered, but each checks config to determine if it should activate -// OpenChoreo Auth provider - works with any OIDC-compliant IDP (active when openchoreo.features.auth.enabled = true) -backend.add(OpenChoreoAuthModule); -// Guest provider for development/demo mode (active when openchoreo.features.auth.enabled = false) -backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); -// Github provider -backend.add(import('@backstage/plugin-auth-backend-module-github-provider')); -// events plugin — receives webhook POSTs and publishes to EventsService -backend.add(import('@backstage/plugin-events-backend')); - -// catalog plugin -backend.add(import('@backstage/plugin-catalog-backend')); -backend.add( - import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'), -); - -// See https://backstage.io/docs/features/software-catalog/configuration#subscribing-to-catalog-errors -backend.add(import('@backstage/plugin-catalog-backend-module-logs')); - -// permission plugin -backend.add(import('@backstage/plugin-permission-backend')); -// OpenChoreo permission policy - handles openchoreo.* permissions via /authz/profile API -// Falls back to ALLOW for non-OpenChoreo permissions (composable with other policies) -backend.add( - import( - '@openchoreo/backstage-plugin-permission-backend-module-openchoreo-policy' - ), -); +// Root HTTP router with the IDP token header middleware — reads the IDP token +// from headers and makes it available to ALL routes via AsyncLocalStorage, +// which is critical for the permission system to access the user's IDP token +// when making authorization decisions. +backend.add(portalRootHttpRouterServiceFactory); -// search plugin -backend.add(import('@backstage/plugin-search-backend')); - -// search engine -// See https://backstage.io/docs/features/search/search-engines -backend.add(import('@backstage/plugin-search-backend-module-pg')); - -// search collators -backend.add(import('@backstage/plugin-search-backend-module-catalog')); - -// user settings plugin - enables centralized storage for starred entities and user preferences -backend.add(import('@backstage/plugin-user-settings-backend')); - -// IMPORTANT: catalog-backend-module MUST be registered before openchoreo-backend -// because openchoreo-backend depends on the AnnotationStore which is initialized -// by the catalog module. -backend.add(import('@openchoreo/backstage-plugin-catalog-backend-module')); -backend.add(import('@openchoreo/backstage-plugin-backend')); -backend.add(import('@openchoreo/backstage-plugin-scaffolder-backend-module')); -backend.add( - import( - '@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users' - ), -); -backend.add( - import('@openchoreo/backstage-plugin-platform-engineer-core-backend'), -); -// backend.add(import('@openchoreo/backstage-plugin-home-backend')); -backend.add( - import('@openchoreo/backstage-plugin-openchoreo-observability-backend'), -); -backend.add(import('@openchoreo/backstage-plugin-openchoreo-ci-backend')); +// The full portal backend composition: Backstage core plugins, the Jenkins CI +// integration, and all OpenChoreo backend plugins, modules, and service +// factories. +backend.add(portalBackendFeatures); // External CI Platform Integrations -// Jenkins: Handles missing config gracefully (API calls fail, not startup) -backend.add(import('@backstage-community/plugin-jenkins-backend')); // GitLab: Requires integrations.gitlab config at startup. Uncomment after configuring in app-config.local.yaml // For production, config is in app-config.production.yaml with Helm-injected env vars // backend.add(import('@immobiliarelabs/backstage-plugin-gitlab-backend')); -backend.add( - import('@openchoreo/backstage-plugin-openchoreo-workflows-backend'), -); // Portal Assistant backend — forwards Portal Assistant frontend traffic to the // portal-assistant service in the OpenChoreo control plane. Plugin // self-disables when openchoreo.portalAssistantUrl is not set. +// (Private package — deliberately not part of the published portal bundle.) backend.add( import('@openchoreo/backstage-plugin-openchoreo-portal-assistant-backend'), ); + backend.start(); diff --git a/packages/portal-app/.eslintrc.js b/packages/portal-app/.eslintrc.js new file mode 100644 index 000000000..e2a53a6ad --- /dev/null +++ b/packages/portal-app/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/portal-app/README.md b/packages/portal-app/README.md new file mode 100644 index 000000000..4127cddb4 --- /dev/null +++ b/packages/portal-app/README.md @@ -0,0 +1,29 @@ +# @openchoreo/backstage-portal-app + +The OpenChoreo Portal's frontend shell: app assembly, sign-in, navigation, +custom catalog/entity/scaffolder pages, and scaffolder field extensions. The +stock portal's `app` package is a thin consumer: + +```tsx +import { createPortalApp } from '@openchoreo/backstage-portal-app'; + +export default createPortalApp().createRoot(); +``` + +A custom portal adds its own plugins via options: + +```tsx +export default createPortalApp({ + features: [myPluginAlpha], +}).createRoot(); +``` + +## Status + +This package is **private for now**. It is the landing place for app-shell +pieces as they are migrated to the new frontend system — parts of the shell +still run through `@backstage/core-compat-api` (legacy bridge) and are being +migrated piece by piece. It becomes publishable once the migration removes the +legacy-bridged internals (and the dependency on the private portal-assistant +plugin is decoupled); that PR flips `private` and adds the package to the +changeset linked group. diff --git a/packages/portal-app/package.json b/packages/portal-app/package.json new file mode 100644 index 000000000..1551bd185 --- /dev/null +++ b/packages/portal-app/package.json @@ -0,0 +1,118 @@ +{ + "name": "@openchoreo/backstage-portal-app", + "version": "0.1.0", + "description": "The OpenChoreo Portal's frontend shell as composable building blocks (app assembly, sign-in, navigation, pages, scaffolder fields)", + "license": "Apache-2.0", + "private": true, + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts", + "registry": "https://npm.pkg.github.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/openchoreo/backstage-plugins.git", + "directory": "packages/portal-app" + }, + "backstage": { + "role": "web-library" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage-community/plugin-github-actions": "^0.20.0", + "@backstage-community/plugin-jenkins": "^0.28.0", + "@backstage/catalog-client": "^1.15.1", + "@backstage/catalog-model": "^1.9.0", + "@backstage/config": "^1.3.8", + "@backstage/core-app-api": "^1.20.1", + "@backstage/core-compat-api": "^0.5.11", + "@backstage/core-components": "^0.18.10", + "@backstage/core-plugin-api": "^1.12.6", + "@backstage/frontend-app-api": "^0.16.3", + "@backstage/frontend-defaults": "^0.5.2", + "@backstage/frontend-plugin-api": "^0.17.0", + "@backstage/integration-react": "^1.2.18", + "@backstage/plugin-api-docs": "^0.14.1", + "@backstage/plugin-app": "^0.4.6", + "@backstage/plugin-app-react": "^0.2.3", + "@backstage/plugin-catalog": "^2.0.5", + "@backstage/plugin-catalog-common": "^1.1.10", + "@backstage/plugin-catalog-graph": "^0.6.4", + "@backstage/plugin-catalog-import": "^0.13.13", + "@backstage/plugin-catalog-react": "^3.0.0", + "@backstage/plugin-home": "^0.9.6", + "@backstage/plugin-kubernetes": "^0.12.19", + "@backstage/plugin-org": "^0.7.4", + "@backstage/plugin-permission-common": "^0.9.9", + "@backstage/plugin-permission-react": "^0.5.1", + "@backstage/plugin-scaffolder": "^1.37.0", + "@backstage/plugin-scaffolder-common": "^2.2.0", + "@backstage/plugin-scaffolder-react": "^2.0.0", + "@backstage/plugin-search": "^1.7.4", + "@backstage/plugin-search-react": "^1.11.4", + "@backstage/plugin-techdocs": "^1.17.6", + "@backstage/plugin-techdocs-common": "^0.1.1", + "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.36", + "@backstage/plugin-techdocs-react": "^1.3.11", + "@backstage/plugin-user-settings": "^0.9.3", + "@backstage/theme": "^0.7.3", + "@gitbeaker/rest": "^40.6.0", + "@immobiliarelabs/backstage-plugin-gitlab": "^6.13.0", + "@material-ui/core": "4.12.4", + "@material-ui/icons": "4.11.3", + "@material-ui/lab": "4.0.0-alpha.61", + "@openchoreo/backstage-design-system": "workspace:^", + "@openchoreo/backstage-plugin": "workspace:^", + "@openchoreo/backstage-plugin-common": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-ci": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-observability": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-portal-assistant": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-workflows": "workspace:^", + "@openchoreo/backstage-plugin-platform-engineer-core": "workspace:^", + "@openchoreo/backstage-plugin-react": "workspace:^", + "@rjsf/core": "5.24.13", + "@rjsf/material-ui": "5.24.13", + "@rjsf/utils": "5.24.13", + "@rjsf/validator-ajv8": "5.24.13", + "@tanstack/react-query": "^5.101.2", + "clsx": "^2.1.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "devicons-react": "^1.5.0", + "json-schema": "0.4.0", + "react-use": "17.6.0", + "yaml": "^2.8.1" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-router-dom": "^6.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.36.2", + "@backstage/frontend-test-utils": "^0.6.0", + "@backstage/test-utils": "^1.7.18", + "@testing-library/dom": "9.3.4", + "@testing-library/jest-dom": "6.9.1", + "@testing-library/react": "14.3.1", + "@testing-library/user-event": "14.6.1", + "@types/react-dom": "*", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-router-dom": "6.30.1" + }, + "files": [ + "dist" + ] +} diff --git a/packages/app/src/apis.test.ts b/packages/portal-app/src/apis.test.ts similarity index 100% rename from packages/app/src/apis.test.ts rename to packages/portal-app/src/apis.test.ts diff --git a/packages/app/src/apis.ts b/packages/portal-app/src/apis.ts similarity index 100% rename from packages/app/src/apis.ts rename to packages/portal-app/src/apis.ts diff --git a/packages/app/src/apis/OpenChoreoFetchApi.test.ts b/packages/portal-app/src/apis/OpenChoreoFetchApi.test.ts similarity index 100% rename from packages/app/src/apis/OpenChoreoFetchApi.test.ts rename to packages/portal-app/src/apis/OpenChoreoFetchApi.test.ts diff --git a/packages/app/src/apis/OpenChoreoFetchApi.ts b/packages/portal-app/src/apis/OpenChoreoFetchApi.ts similarity index 100% rename from packages/app/src/apis/OpenChoreoFetchApi.ts rename to packages/portal-app/src/apis/OpenChoreoFetchApi.ts diff --git a/packages/app/src/apis/OpenChoreoPermissionApi.test.ts b/packages/portal-app/src/apis/OpenChoreoPermissionApi.test.ts similarity index 100% rename from packages/app/src/apis/OpenChoreoPermissionApi.test.ts rename to packages/portal-app/src/apis/OpenChoreoPermissionApi.test.ts diff --git a/packages/app/src/apis/OpenChoreoPermissionApi.ts b/packages/portal-app/src/apis/OpenChoreoPermissionApi.ts similarity index 100% rename from packages/app/src/apis/OpenChoreoPermissionApi.ts rename to packages/portal-app/src/apis/OpenChoreoPermissionApi.ts diff --git a/packages/app/src/apis/authRefs.ts b/packages/portal-app/src/apis/authRefs.ts similarity index 100% rename from packages/app/src/apis/authRefs.ts rename to packages/portal-app/src/apis/authRefs.ts diff --git a/packages/app/src/apis/customOverrides.test.tsx b/packages/portal-app/src/apis/customOverrides.test.tsx similarity index 100% rename from packages/app/src/apis/customOverrides.test.tsx rename to packages/portal-app/src/apis/customOverrides.test.tsx diff --git a/packages/app/src/apis/customOverrides.tsx b/packages/portal-app/src/apis/customOverrides.tsx similarity index 100% rename from packages/app/src/apis/customOverrides.tsx rename to packages/portal-app/src/apis/customOverrides.tsx diff --git a/packages/app/src/components/DynamicSignInPage.tsx b/packages/portal-app/src/components/DynamicSignInPage.tsx similarity index 100% rename from packages/app/src/components/DynamicSignInPage.tsx rename to packages/portal-app/src/components/DynamicSignInPage.tsx diff --git a/packages/app/src/components/Home/HomePage.tsx b/packages/portal-app/src/components/Home/HomePage.tsx similarity index 100% rename from packages/app/src/components/Home/HomePage.tsx rename to packages/portal-app/src/components/Home/HomePage.tsx diff --git a/packages/app/src/components/Home/RecentlyVisitedCard.tsx b/packages/portal-app/src/components/Home/RecentlyVisitedCard.tsx similarity index 100% rename from packages/app/src/components/Home/RecentlyVisitedCard.tsx rename to packages/portal-app/src/components/Home/RecentlyVisitedCard.tsx diff --git a/packages/app/src/components/Home/index.ts b/packages/portal-app/src/components/Home/index.ts similarity index 100% rename from packages/app/src/components/Home/index.ts rename to packages/portal-app/src/components/Home/index.ts diff --git a/packages/app/src/components/Home/styles.ts b/packages/portal-app/src/components/Home/styles.ts similarity index 100% rename from packages/app/src/components/Home/styles.ts rename to packages/portal-app/src/components/Home/styles.ts diff --git a/packages/app/src/components/Root/LogoFull.tsx b/packages/portal-app/src/components/Root/LogoFull.tsx similarity index 100% rename from packages/app/src/components/Root/LogoFull.tsx rename to packages/portal-app/src/components/Root/LogoFull.tsx diff --git a/packages/app/src/components/Root/LogoIcon.tsx b/packages/portal-app/src/components/Root/LogoIcon.tsx similarity index 100% rename from packages/app/src/components/Root/LogoIcon.tsx rename to packages/portal-app/src/components/Root/LogoIcon.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/portal-app/src/components/Root/Root.tsx similarity index 100% rename from packages/app/src/components/Root/Root.tsx rename to packages/portal-app/src/components/Root/Root.tsx diff --git a/packages/app/src/components/Root/index.ts b/packages/portal-app/src/components/Root/index.ts similarity index 100% rename from packages/app/src/components/Root/index.ts rename to packages/portal-app/src/components/Root/index.ts diff --git a/packages/app/src/components/catalog/ApiDefinitionTabs.tsx b/packages/portal-app/src/components/catalog/ApiDefinitionTabs.tsx similarity index 100% rename from packages/app/src/components/catalog/ApiDefinitionTabs.tsx rename to packages/portal-app/src/components/catalog/ApiDefinitionTabs.tsx diff --git a/packages/app/src/components/catalog/CatalogCardList.tsx b/packages/portal-app/src/components/catalog/CatalogCardList.tsx similarity index 100% rename from packages/app/src/components/catalog/CatalogCardList.tsx rename to packages/portal-app/src/components/catalog/CatalogCardList.tsx diff --git a/packages/app/src/components/catalog/ChoreoEntityKindPicker.tsx b/packages/portal-app/src/components/catalog/ChoreoEntityKindPicker.tsx similarity index 100% rename from packages/app/src/components/catalog/ChoreoEntityKindPicker.tsx rename to packages/portal-app/src/components/catalog/ChoreoEntityKindPicker.tsx diff --git a/packages/app/src/components/catalog/ContainedCatalogGraphCard.tsx b/packages/portal-app/src/components/catalog/ContainedCatalogGraphCard.tsx similarity index 100% rename from packages/app/src/components/catalog/ContainedCatalogGraphCard.tsx rename to packages/portal-app/src/components/catalog/ContainedCatalogGraphCard.tsx diff --git a/packages/app/src/components/catalog/ContextAwareCreateButton.tsx b/packages/portal-app/src/components/catalog/ContextAwareCreateButton.tsx similarity index 100% rename from packages/app/src/components/catalog/ContextAwareCreateButton.tsx rename to packages/portal-app/src/components/catalog/ContextAwareCreateButton.tsx diff --git a/packages/app/src/components/catalog/CustomApiExplorerPage.tsx b/packages/portal-app/src/components/catalog/CustomApiExplorerPage.tsx similarity index 100% rename from packages/app/src/components/catalog/CustomApiExplorerPage.tsx rename to packages/portal-app/src/components/catalog/CustomApiExplorerPage.tsx diff --git a/packages/app/src/components/catalog/CustomCatalogImportPage.tsx b/packages/portal-app/src/components/catalog/CustomCatalogImportPage.tsx similarity index 100% rename from packages/app/src/components/catalog/CustomCatalogImportPage.tsx rename to packages/portal-app/src/components/catalog/CustomCatalogImportPage.tsx diff --git a/packages/app/src/components/catalog/CustomCatalogPage.tsx b/packages/portal-app/src/components/catalog/CustomCatalogPage.tsx similarity index 100% rename from packages/app/src/components/catalog/CustomCatalogPage.tsx rename to packages/portal-app/src/components/catalog/CustomCatalogPage.tsx diff --git a/packages/app/src/components/catalog/CustomPersonalFilters.tsx b/packages/portal-app/src/components/catalog/CustomPersonalFilters.tsx similarity index 100% rename from packages/app/src/components/catalog/CustomPersonalFilters.tsx rename to packages/portal-app/src/components/catalog/CustomPersonalFilters.tsx diff --git a/packages/app/src/components/catalog/EntityLayoutWithDelete.tsx b/packages/portal-app/src/components/catalog/EntityLayoutWithDelete.tsx similarity index 100% rename from packages/app/src/components/catalog/EntityLayoutWithDelete.tsx rename to packages/portal-app/src/components/catalog/EntityLayoutWithDelete.tsx diff --git a/packages/app/src/components/catalog/EntityPage.test.tsx b/packages/portal-app/src/components/catalog/EntityPage.test.tsx similarity index 100% rename from packages/app/src/components/catalog/EntityPage.test.tsx rename to packages/portal-app/src/components/catalog/EntityPage.test.tsx diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/portal-app/src/components/catalog/EntityPage.tsx similarity index 100% rename from packages/app/src/components/catalog/EntityPage.tsx rename to packages/portal-app/src/components/catalog/EntityPage.tsx diff --git a/packages/app/src/components/catalog/EntityRelationWarning.tsx b/packages/portal-app/src/components/catalog/EntityRelationWarning.tsx similarity index 100% rename from packages/app/src/components/catalog/EntityRelationWarning.tsx rename to packages/portal-app/src/components/catalog/EntityRelationWarning.tsx diff --git a/packages/app/src/components/catalog/OpenChoreoAboutCard.tsx b/packages/portal-app/src/components/catalog/OpenChoreoAboutCard.tsx similarity index 100% rename from packages/app/src/components/catalog/OpenChoreoAboutCard.tsx rename to packages/portal-app/src/components/catalog/OpenChoreoAboutCard.tsx diff --git a/packages/app/src/components/catalog/OpenChoreoCatalogEntityPage.tsx b/packages/portal-app/src/components/catalog/OpenChoreoCatalogEntityPage.tsx similarity index 100% rename from packages/app/src/components/catalog/OpenChoreoCatalogEntityPage.tsx rename to packages/portal-app/src/components/catalog/OpenChoreoCatalogEntityPage.tsx diff --git a/packages/app/src/components/catalog/SelectedKindContext.test.tsx b/packages/portal-app/src/components/catalog/SelectedKindContext.test.tsx similarity index 100% rename from packages/app/src/components/catalog/SelectedKindContext.test.tsx rename to packages/portal-app/src/components/catalog/SelectedKindContext.test.tsx diff --git a/packages/app/src/components/catalog/SelectedKindContext.tsx b/packages/portal-app/src/components/catalog/SelectedKindContext.tsx similarity index 100% rename from packages/app/src/components/catalog/SelectedKindContext.tsx rename to packages/portal-app/src/components/catalog/SelectedKindContext.tsx diff --git a/packages/app/src/components/catalog/WorkflowsOrExternalCICard.tsx b/packages/portal-app/src/components/catalog/WorkflowsOrExternalCICard.tsx similarity index 100% rename from packages/app/src/components/catalog/WorkflowsOrExternalCICard.tsx rename to packages/portal-app/src/components/catalog/WorkflowsOrExternalCICard.tsx diff --git a/packages/app/src/components/catalog/catalogLoadState.test.ts b/packages/portal-app/src/components/catalog/catalogLoadState.test.ts similarity index 100% rename from packages/app/src/components/catalog/catalogLoadState.test.ts rename to packages/portal-app/src/components/catalog/catalogLoadState.test.ts diff --git a/packages/app/src/components/catalog/catalogLoadState.ts b/packages/portal-app/src/components/catalog/catalogLoadState.ts similarity index 100% rename from packages/app/src/components/catalog/catalogLoadState.ts rename to packages/portal-app/src/components/catalog/catalogLoadState.ts diff --git a/packages/app/src/components/catalog/catalogSeed.test.ts b/packages/portal-app/src/components/catalog/catalogSeed.test.ts similarity index 100% rename from packages/app/src/components/catalog/catalogSeed.test.ts rename to packages/portal-app/src/components/catalog/catalogSeed.test.ts diff --git a/packages/app/src/components/catalog/catalogSeed.ts b/packages/portal-app/src/components/catalog/catalogSeed.ts similarity index 100% rename from packages/app/src/components/catalog/catalogSeed.ts rename to packages/portal-app/src/components/catalog/catalogSeed.ts diff --git a/packages/app/src/components/catalog/styles.ts b/packages/portal-app/src/components/catalog/styles.ts similarity index 100% rename from packages/app/src/components/catalog/styles.ts rename to packages/portal-app/src/components/catalog/styles.ts diff --git a/packages/app/src/components/catalog/useKindCreateConfig.ts b/packages/portal-app/src/components/catalog/useKindCreateConfig.ts similarity index 100% rename from packages/app/src/components/catalog/useKindCreateConfig.ts rename to packages/portal-app/src/components/catalog/useKindCreateConfig.ts diff --git a/packages/app/src/components/graph/DependencyGraphZoomOverrides.tsx b/packages/portal-app/src/components/graph/DependencyGraphZoomOverrides.tsx similarity index 100% rename from packages/app/src/components/graph/DependencyGraphZoomOverrides.tsx rename to packages/portal-app/src/components/graph/DependencyGraphZoomOverrides.tsx diff --git a/packages/app/src/components/platformOverview/PlatformOverviewPage.test.tsx b/packages/portal-app/src/components/platformOverview/PlatformOverviewPage.test.tsx similarity index 100% rename from packages/app/src/components/platformOverview/PlatformOverviewPage.test.tsx rename to packages/portal-app/src/components/platformOverview/PlatformOverviewPage.test.tsx diff --git a/packages/app/src/components/platformOverview/PlatformOverviewPage.tsx b/packages/portal-app/src/components/platformOverview/PlatformOverviewPage.tsx similarity index 100% rename from packages/app/src/components/platformOverview/PlatformOverviewPage.tsx rename to packages/portal-app/src/components/platformOverview/PlatformOverviewPage.tsx diff --git a/packages/app/src/components/platformOverview/index.ts b/packages/portal-app/src/components/platformOverview/index.ts similarity index 100% rename from packages/app/src/components/platformOverview/index.ts rename to packages/portal-app/src/components/platformOverview/index.ts diff --git a/packages/app/src/components/scaffolder/CustomTemplateCard.tsx b/packages/portal-app/src/components/scaffolder/CustomTemplateCard.tsx similarity index 100% rename from packages/app/src/components/scaffolder/CustomTemplateCard.tsx rename to packages/portal-app/src/components/scaffolder/CustomTemplateCard.tsx diff --git a/packages/app/src/components/scaffolder/CustomTemplateListPage.tsx b/packages/portal-app/src/components/scaffolder/CustomTemplateListPage.tsx similarity index 100% rename from packages/app/src/components/scaffolder/CustomTemplateListPage.tsx rename to packages/portal-app/src/components/scaffolder/CustomTemplateListPage.tsx diff --git a/packages/app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx b/packages/portal-app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx similarity index 100% rename from packages/app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx rename to packages/portal-app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx diff --git a/packages/app/src/components/scaffolder/OpenChoreoTemplateOutputs.tsx b/packages/portal-app/src/components/scaffolder/OpenChoreoTemplateOutputs.tsx similarity index 100% rename from packages/app/src/components/scaffolder/OpenChoreoTemplateOutputs.tsx rename to packages/portal-app/src/components/scaffolder/OpenChoreoTemplateOutputs.tsx diff --git a/packages/app/src/components/scaffolder/ScaffolderCategoryPicker.tsx b/packages/portal-app/src/components/scaffolder/ScaffolderCategoryPicker.tsx similarity index 100% rename from packages/app/src/components/scaffolder/ScaffolderCategoryPicker.tsx rename to packages/portal-app/src/components/scaffolder/ScaffolderCategoryPicker.tsx diff --git a/packages/app/src/components/scaffolder/ScaffolderNamespacePicker.tsx b/packages/portal-app/src/components/scaffolder/ScaffolderNamespacePicker.tsx similarity index 100% rename from packages/app/src/components/scaffolder/ScaffolderNamespacePicker.tsx rename to packages/portal-app/src/components/scaffolder/ScaffolderNamespacePicker.tsx diff --git a/packages/app/src/components/scaffolder/ScaffolderSearchBar.tsx b/packages/portal-app/src/components/scaffolder/ScaffolderSearchBar.tsx similarity index 100% rename from packages/app/src/components/scaffolder/ScaffolderSearchBar.tsx rename to packages/portal-app/src/components/scaffolder/ScaffolderSearchBar.tsx diff --git a/packages/app/src/components/scaffolder/ScaffolderStarredFilter.tsx b/packages/portal-app/src/components/scaffolder/ScaffolderStarredFilter.tsx similarity index 100% rename from packages/app/src/components/scaffolder/ScaffolderStarredFilter.tsx rename to packages/portal-app/src/components/scaffolder/ScaffolderStarredFilter.tsx diff --git a/packages/app/src/components/scaffolder/ScaffolderTagPicker.tsx b/packages/portal-app/src/components/scaffolder/ScaffolderTagPicker.tsx similarity index 100% rename from packages/app/src/components/scaffolder/ScaffolderTagPicker.tsx rename to packages/portal-app/src/components/scaffolder/ScaffolderTagPicker.tsx diff --git a/packages/app/src/components/scaffolder/TemplateCardSkeleton.tsx b/packages/portal-app/src/components/scaffolder/TemplateCardSkeleton.tsx similarity index 100% rename from packages/app/src/components/scaffolder/TemplateCardSkeleton.tsx rename to packages/portal-app/src/components/scaffolder/TemplateCardSkeleton.tsx diff --git a/packages/app/src/components/scaffolder/filterPickerStyles.ts b/packages/portal-app/src/components/scaffolder/filterPickerStyles.ts similarity index 100% rename from packages/app/src/components/scaffolder/filterPickerStyles.ts rename to packages/portal-app/src/components/scaffolder/filterPickerStyles.ts diff --git a/packages/app/src/components/scaffolder/styles.ts b/packages/portal-app/src/components/scaffolder/styles.ts similarity index 100% rename from packages/app/src/components/scaffolder/styles.ts rename to packages/portal-app/src/components/scaffolder/styles.ts diff --git a/packages/app/src/components/search/CustomSearchModal.test.tsx b/packages/portal-app/src/components/search/CustomSearchModal.test.tsx similarity index 100% rename from packages/app/src/components/search/CustomSearchModal.test.tsx rename to packages/portal-app/src/components/search/CustomSearchModal.test.tsx diff --git a/packages/app/src/components/search/CustomSearchModal.tsx b/packages/portal-app/src/components/search/CustomSearchModal.tsx similarity index 100% rename from packages/app/src/components/search/CustomSearchModal.tsx rename to packages/portal-app/src/components/search/CustomSearchModal.tsx diff --git a/packages/app/src/components/search/SearchKindDropdown.tsx b/packages/portal-app/src/components/search/SearchKindDropdown.tsx similarity index 100% rename from packages/app/src/components/search/SearchKindDropdown.tsx rename to packages/portal-app/src/components/search/SearchKindDropdown.tsx diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/portal-app/src/components/search/SearchPage.tsx similarity index 100% rename from packages/app/src/components/search/SearchPage.tsx rename to packages/portal-app/src/components/search/SearchPage.tsx diff --git a/packages/app/src/components/search/SearchResultItem.tsx b/packages/portal-app/src/components/search/SearchResultItem.tsx similarity index 100% rename from packages/app/src/components/search/SearchResultItem.tsx rename to packages/portal-app/src/components/search/SearchResultItem.tsx diff --git a/packages/portal-app/src/createPortalApp.test.tsx b/packages/portal-app/src/createPortalApp.test.tsx new file mode 100644 index 000000000..4288df25a --- /dev/null +++ b/packages/portal-app/src/createPortalApp.test.tsx @@ -0,0 +1,46 @@ +import { render, waitFor } from '@testing-library/react'; +import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import { createPortalApp } from './createPortalApp'; + +describe('createPortalApp', () => { + beforeEach(() => { + process.env = { + NODE_ENV: 'test', + APP_CONFIG: [ + { + data: { + app: { title: 'Test' }, + backend: { baseUrl: 'http://localhost:7007' }, + techdocs: { + storageUrl: 'http://localhost:7007/api/techdocs/static/docs', + }, + }, + context: 'test', + }, + ] as any, + }; + }); + + it('renders the portal app', async () => { + const rendered = render(createPortalApp().createRoot()); + + await waitFor(() => { + expect(rendered.baseElement).toBeInTheDocument(); + }); + }); + + it('accepts additional features', async () => { + const extraModule = createFrontendModule({ + pluginId: 'app', + extensions: [], + }); + + const rendered = render( + createPortalApp({ features: [extraModule] }).createRoot(), + ); + + await waitFor(() => { + expect(rendered.baseElement).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/portal-app/src/createPortalApp.tsx b/packages/portal-app/src/createPortalApp.tsx new file mode 100644 index 000000000..bbf981eb9 --- /dev/null +++ b/packages/portal-app/src/createPortalApp.tsx @@ -0,0 +1,226 @@ +import { Route } from 'react-router-dom'; +import { catalogPlugin } from '@backstage/plugin-catalog'; +import { catalogImportPlugin } from '@backstage/plugin-catalog-import'; +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { SearchPage } from '@backstage/plugin-search'; +import { + TechDocsIndexPage, + techdocsPlugin, + TechDocsReaderPage, +} from '@backstage/plugin-techdocs'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; +import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; +import { apis } from './apis'; +import { CustomApiExplorerPage } from './components/catalog/CustomApiExplorerPage'; +import { CustomCatalogImportPage } from './components/catalog/CustomCatalogImportPage'; +import { searchPage } from './components/search/SearchPage'; +import { Root } from './components/Root'; +import { HomePage } from './components/Home'; +import { CustomGraphNode } from '@openchoreo/backstage-plugin-react'; +import { PageLoader } from '@openchoreo/backstage-design-system'; +import { PlatformOverviewPage } from './components/platformOverview'; + +import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; +import { createApp } from '@backstage/frontend-defaults'; +import type { FrontendFeature } from '@backstage/frontend-plugin-api'; +import { + convertLegacyAppOptions, + convertLegacyAppRoot, +} from '@backstage/core-compat-api'; +import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; + +// NFS plugin features (created in Step 2 — each plugin's `/alpha` exports a +// `createFrontendPlugin` instance). These replace the API factory entries +// that previously lived in `apis.ts`. +import openchoreoPluginAlpha from '@openchoreo/backstage-plugin/alpha'; +import openchoreoCiPluginAlpha from '@openchoreo/backstage-plugin-openchoreo-ci/alpha'; +import openchoreoObservabilityPluginAlpha from '@openchoreo/backstage-plugin-openchoreo-observability/alpha'; +import openchoreoWorkflowsPluginAlpha from '@openchoreo/backstage-plugin-openchoreo-workflows/alpha'; +import platformEngineerCorePluginAlpha from '@openchoreo/backstage-plugin-platform-engineer-core/alpha'; + +// Upstream NFS plugin features with our overrides: +// - catalog graph default API replaced to include OpenChoreo custom relations +// - catalog entity-presentation default API replaced to add custom kind icons +// - scaffolder `page:scaffolder` disabled (our legacy wins) +// and form-decorators API replaced to inject the openChoreoTokenDecorator +// - customTranslationsModule reinstates the catalog-import header overrides +// that previously rode via createApp.__experimentalTranslations +import { + catalogGraphPluginAlpha, + catalogPluginAlpha, + customAppModule, + scaffolderPluginAlpha as upstreamScaffolderPluginAlpha, +} from './apis/customOverrides'; + +// catalog-import NFS plugin — registered so the `/catalog-import` route ref +// resolves under NFS. Our legacy `` +// mount in `` provides the actual page rendering. +import catalogImportPluginAlpha from '@backstage/plugin-catalog-import/alpha'; +// api-docs and kubernetes NFS plugins — registered so that `apiDocsConfigRef`, +// `kubernetesApiRef`, etc. are present in the api holder. The host owns the +// `/api-docs` route (CustomApiExplorerPage) and the Kubernetes entity tab +// reuses upstream `EntityKubernetesContent`; without these features the apis +// they depend on are absent and the tabs throw `NotImplementedError`. +import apiDocsPluginAlpha from '@backstage/plugin-api-docs/alpha'; +import kubernetesPluginAlpha from '@backstage/plugin-kubernetes/alpha'; +import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; +import { RequirePermission } from '@backstage/plugin-permission-react'; +import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; +import { appThemes } from './themes'; +import { LEGACY_KIND_ICONS } from './kindIcons'; +import { + AccessControlContent, + SecretsContent, + ExecTerminalWindowPage, +} from '@openchoreo/backstage-plugin'; +import { + UserSettingsPage, + SettingsLayout, + UserSettingsGeneral, +} from '@backstage/plugin-user-settings'; +import { VisitListener } from '@backstage/plugin-home'; + +const legacyAppOptions = convertLegacyAppOptions({ + apis, + icons: LEGACY_KIND_ICONS, + themes: appThemes, +}); + +const routes = ( + + } /> + {/* + `/catalog` is owned by the NFS `page:catalog` extension and + `/catalog/:namespace/:kind/:name` by `page:catalog/entity` — see + customOverrides.tsx, which overrides each loader to render the + host's `` and the legacy `entityPage` JSX + respectively. The legacy `` mount used to + live here but double-rendered the catalog header under the NFS + compat shim. + */} + } /> + } + > + + + + + {/* + `/create` is owned by the NFS `page:scaffolder` extension — see + customOverrides.tsx, which overrides its loader to render + `` (the host's `` with + the 27 field-extension children and `CustomTemplateListPage` / + `CustomReviewStep` components). The legacy `` + mount used to live here but double-rendered the scaffolder header + under the NFS compat shim. + */} + } /> + + + + } + /> + }> + {searchPage} + + }> + + + + + + + + + + + + + } + /> + } /> + {/* + Standalone full-window exec terminal, opened in a new browser tab from the + resource drawer. The page renders a fixed viewport overlay over the app + chrome, so the terminal uses the entire window. + */} + } /> + +); + +const legacyRoot = convertLegacyAppRoot( + <> + + + + + {routes} + + , +); + +/** Options for {@link createPortalApp}. */ +export interface PortalAppOptions { + /** + * Additional frontend features (plugins/modules) appended after the + * portal's own. Use this to add your plugins to a custom portal. + */ + features?: FrontendFeature[]; +} + +/** + * Assembles the OpenChoreo Portal frontend app. The stock portal renders + * `createPortalApp().createRoot()`; a custom portal can pass extra features. + */ +export function createPortalApp(options?: PortalAppOptions) { + return createApp({ + features: [ + // `...legacyRoot` re-emits each legacy plugin's `apis: [...]` array as + // ApiBlueprint extensions under the legacy plugin's own pluginId + // (collectLegacyRoutes). The NFS api-factory registry resolves + // same-pluginId factories last-write-wins, so the override features + // below MUST come after `...legacyRoot` to win the contest. Otherwise + // our custom catalog-graph relations, entity-presentation kind icons, + // and scaffolder form-decorator override get silently overwritten by + // upstream defaults at startup. + legacyAppOptions, + ...legacyRoot, + customAppModule, + upstreamScaffolderPluginAlpha, + catalogGraphPluginAlpha, + catalogPluginAlpha, + catalogImportPluginAlpha, + apiDocsPluginAlpha, + kubernetesPluginAlpha, + openchoreoPluginAlpha, + openchoreoCiPluginAlpha, + openchoreoObservabilityPluginAlpha, + openchoreoWorkflowsPluginAlpha, + platformEngineerCorePluginAlpha, + ...(options?.features ?? []), + ], + bindRoutes({ bind }) { + bind(catalogPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + viewTechDoc: techdocsPlugin.routes.docRoot, + createFromTemplate: scaffolderPlugin.routes.selectedTemplate, + }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + viewTechDoc: techdocsPlugin.routes.docRoot, + }); + }, + advanced: { + // App-boot loader (waiting for config/features) — use our centered loader + // instead of the default Backstage progress bar. + loadingElement: , + }, + }); +} diff --git a/packages/app/src/hooks/index.ts b/packages/portal-app/src/hooks/index.ts similarity index 100% rename from packages/app/src/hooks/index.ts rename to packages/portal-app/src/hooks/index.ts diff --git a/packages/app/src/hooks/useAllKinds.ts b/packages/portal-app/src/hooks/useAllKinds.ts similarity index 100% rename from packages/app/src/hooks/useAllKinds.ts rename to packages/portal-app/src/hooks/useAllKinds.ts diff --git a/packages/app/src/hooks/useUserInfo.ts b/packages/portal-app/src/hooks/useUserInfo.ts similarity index 100% rename from packages/app/src/hooks/useUserInfo.ts rename to packages/portal-app/src/hooks/useUserInfo.ts diff --git a/packages/portal-app/src/index.ts b/packages/portal-app/src/index.ts new file mode 100644 index 000000000..a8b13f506 --- /dev/null +++ b/packages/portal-app/src/index.ts @@ -0,0 +1,18 @@ +/** + * The OpenChoreo Portal's frontend shell as composable building blocks. + * + * This package is the agreed landing place for app-shell pieces as they are + * migrated to the new frontend system: sign-in, navigation/layout, custom + * catalog/entity/scaffolder pages, and scaffolder field extensions. Migrated + * code lives here (not in packages/app) so the custom-portal scaffold can + * consume it — see the portal composition proposal. + * + * The package stays private until the migration cleans up the legacy-bridged + * pieces; the PR that makes it publishable flips `private` and adds it to the + * changeset linked group. + * + * @packageDocumentation + */ + +export { createPortalApp } from './createPortalApp'; +export type { PortalAppOptions } from './createPortalApp'; diff --git a/packages/app/src/kindIcons.ts b/packages/portal-app/src/kindIcons.ts similarity index 100% rename from packages/app/src/kindIcons.ts rename to packages/portal-app/src/kindIcons.ts diff --git a/packages/app/src/scaffolder/AdvancedConfigurationField/AdvancedConfigurationField.tsx b/packages/portal-app/src/scaffolder/AdvancedConfigurationField/AdvancedConfigurationField.tsx similarity index 100% rename from packages/app/src/scaffolder/AdvancedConfigurationField/AdvancedConfigurationField.tsx rename to packages/portal-app/src/scaffolder/AdvancedConfigurationField/AdvancedConfigurationField.tsx diff --git a/packages/app/src/scaffolder/AdvancedConfigurationField/extensions.ts b/packages/portal-app/src/scaffolder/AdvancedConfigurationField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/AdvancedConfigurationField/extensions.ts rename to packages/portal-app/src/scaffolder/AdvancedConfigurationField/extensions.ts diff --git a/packages/app/src/scaffolder/AdvancedConfigurationField/index.ts b/packages/portal-app/src/scaffolder/AdvancedConfigurationField/index.ts similarity index 100% rename from packages/app/src/scaffolder/AdvancedConfigurationField/index.ts rename to packages/portal-app/src/scaffolder/AdvancedConfigurationField/index.ts diff --git a/packages/app/src/scaffolder/BuildAndDeployField/BuildAndDeployField.tsx b/packages/portal-app/src/scaffolder/BuildAndDeployField/BuildAndDeployField.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildAndDeployField/BuildAndDeployField.tsx rename to packages/portal-app/src/scaffolder/BuildAndDeployField/BuildAndDeployField.tsx diff --git a/packages/app/src/scaffolder/BuildAndDeployField/extensions.ts b/packages/portal-app/src/scaffolder/BuildAndDeployField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/BuildAndDeployField/extensions.ts rename to packages/portal-app/src/scaffolder/BuildAndDeployField/extensions.ts diff --git a/packages/app/src/scaffolder/BuildAndDeployField/index.ts b/packages/portal-app/src/scaffolder/BuildAndDeployField/index.ts similarity index 100% rename from packages/app/src/scaffolder/BuildAndDeployField/index.ts rename to packages/portal-app/src/scaffolder/BuildAndDeployField/index.ts diff --git a/packages/app/src/scaffolder/BuildTemplateParameters/BuildTemplateParametersExtension.tsx b/packages/portal-app/src/scaffolder/BuildTemplateParameters/BuildTemplateParametersExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildTemplateParameters/BuildTemplateParametersExtension.tsx rename to packages/portal-app/src/scaffolder/BuildTemplateParameters/BuildTemplateParametersExtension.tsx diff --git a/packages/app/src/scaffolder/BuildTemplateParameters/extensions.ts b/packages/portal-app/src/scaffolder/BuildTemplateParameters/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/BuildTemplateParameters/extensions.ts rename to packages/portal-app/src/scaffolder/BuildTemplateParameters/extensions.ts diff --git a/packages/app/src/scaffolder/BuildTemplateParameters/index.ts b/packages/portal-app/src/scaffolder/BuildTemplateParameters/index.ts similarity index 100% rename from packages/app/src/scaffolder/BuildTemplateParameters/index.ts rename to packages/portal-app/src/scaffolder/BuildTemplateParameters/index.ts diff --git a/packages/app/src/scaffolder/BuildTemplatePicker/BuildTemplatePickerExtension.tsx b/packages/portal-app/src/scaffolder/BuildTemplatePicker/BuildTemplatePickerExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildTemplatePicker/BuildTemplatePickerExtension.tsx rename to packages/portal-app/src/scaffolder/BuildTemplatePicker/BuildTemplatePickerExtension.tsx diff --git a/packages/app/src/scaffolder/BuildTemplatePicker/extensions.ts b/packages/portal-app/src/scaffolder/BuildTemplatePicker/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/BuildTemplatePicker/extensions.ts rename to packages/portal-app/src/scaffolder/BuildTemplatePicker/extensions.ts diff --git a/packages/app/src/scaffolder/BuildTemplatePicker/index.ts b/packages/portal-app/src/scaffolder/BuildTemplatePicker/index.ts similarity index 100% rename from packages/app/src/scaffolder/BuildTemplatePicker/index.ts rename to packages/portal-app/src/scaffolder/BuildTemplatePicker/index.ts diff --git a/packages/app/src/scaffolder/BuildWorkflowParameters/BuildWorkflowParametersExtension.tsx b/packages/portal-app/src/scaffolder/BuildWorkflowParameters/BuildWorkflowParametersExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildWorkflowParameters/BuildWorkflowParametersExtension.tsx rename to packages/portal-app/src/scaffolder/BuildWorkflowParameters/BuildWorkflowParametersExtension.tsx diff --git a/packages/app/src/scaffolder/BuildWorkflowParameters/index.tsx b/packages/portal-app/src/scaffolder/BuildWorkflowParameters/index.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildWorkflowParameters/index.tsx rename to packages/portal-app/src/scaffolder/BuildWorkflowParameters/index.tsx diff --git a/packages/app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.test.tsx b/packages/portal-app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.test.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.test.tsx rename to packages/portal-app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.test.tsx diff --git a/packages/app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.tsx b/packages/portal-app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.tsx rename to packages/portal-app/src/scaffolder/BuildWorkflowPicker/BuildWorkflowPickerExtension.tsx diff --git a/packages/app/src/scaffolder/BuildWorkflowPicker/index.tsx b/packages/portal-app/src/scaffolder/BuildWorkflowPicker/index.tsx similarity index 100% rename from packages/app/src/scaffolder/BuildWorkflowPicker/index.tsx rename to packages/portal-app/src/scaffolder/BuildWorkflowPicker/index.tsx diff --git a/packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/ClusterComponentTypeYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/ClusterComponentTypeYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/ClusterComponentTypeYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/ClusterComponentTypeYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterComponentTypeYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ClusterComponentTypeYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.test.tsx b/packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.test.tsx similarity index 100% rename from packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.test.tsx rename to packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.test.tsx diff --git a/packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/ClusterProjectTypeYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterProjectTypeYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ClusterProjectTypeYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/ClusterResourceTypeYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/ClusterResourceTypeYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/ClusterResourceTypeYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/ClusterResourceTypeYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterResourceTypeYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ClusterResourceTypeYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ClusterTraitYamlEditor/ClusterTraitYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/ClusterTraitYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ClusterTraitYamlEditor/ClusterTraitYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/ClusterTraitYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ClusterTraitYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterTraitYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ClusterTraitYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterTraitYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ClusterTraitYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterTraitYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ClusterTraitYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ClusterWorkflowYamlEditor/ClusterWorkflowYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/ClusterWorkflowYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ClusterWorkflowYamlEditor/ClusterWorkflowYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/ClusterWorkflowYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ClusterWorkflowYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterWorkflowYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ClusterWorkflowYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterWorkflowYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ClusterWorkflowYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ClusterWorkflowYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ClusterWorkflowYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ComponentNamePicker/ComponentNamePickerExtension.tsx b/packages/portal-app/src/scaffolder/ComponentNamePicker/ComponentNamePickerExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ComponentNamePicker/ComponentNamePickerExtension.tsx rename to packages/portal-app/src/scaffolder/ComponentNamePicker/ComponentNamePickerExtension.tsx diff --git a/packages/app/src/scaffolder/ComponentNamePicker/extensions.ts b/packages/portal-app/src/scaffolder/ComponentNamePicker/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentNamePicker/extensions.ts rename to packages/portal-app/src/scaffolder/ComponentNamePicker/extensions.ts diff --git a/packages/app/src/scaffolder/ComponentNamePicker/index.ts b/packages/portal-app/src/scaffolder/ComponentNamePicker/index.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentNamePicker/index.ts rename to packages/portal-app/src/scaffolder/ComponentNamePicker/index.ts diff --git a/packages/app/src/scaffolder/ComponentTypeYamlEditor/ComponentTypeYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/ComponentTypeYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ComponentTypeYamlEditor/ComponentTypeYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/ComponentTypeYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ComponentTypeYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentTypeYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ComponentTypeYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentTypeYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ComponentTypeYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentTypeYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ComponentTypeYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ComponentWorkflowYamlEditor/ComponentWorkflowYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/ComponentWorkflowYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ComponentWorkflowYamlEditor/ComponentWorkflowYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/ComponentWorkflowYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ComponentWorkflowYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentWorkflowYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ComponentWorkflowYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentWorkflowYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ComponentWorkflowYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ComponentWorkflowYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ComponentWorkflowYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ContainerImageField/ContainerImageField.tsx b/packages/portal-app/src/scaffolder/ContainerImageField/ContainerImageField.tsx similarity index 100% rename from packages/app/src/scaffolder/ContainerImageField/ContainerImageField.tsx rename to packages/portal-app/src/scaffolder/ContainerImageField/ContainerImageField.tsx diff --git a/packages/app/src/scaffolder/ContainerImageField/extensions.ts b/packages/portal-app/src/scaffolder/ContainerImageField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ContainerImageField/extensions.ts rename to packages/portal-app/src/scaffolder/ContainerImageField/extensions.ts diff --git a/packages/app/src/scaffolder/ContainerImageField/index.ts b/packages/portal-app/src/scaffolder/ContainerImageField/index.ts similarity index 100% rename from packages/app/src/scaffolder/ContainerImageField/index.ts rename to packages/portal-app/src/scaffolder/ContainerImageField/index.ts diff --git a/packages/app/src/scaffolder/CustomReviewState/CustomReviewStep.test.tsx b/packages/portal-app/src/scaffolder/CustomReviewState/CustomReviewStep.test.tsx similarity index 100% rename from packages/app/src/scaffolder/CustomReviewState/CustomReviewStep.test.tsx rename to packages/portal-app/src/scaffolder/CustomReviewState/CustomReviewStep.test.tsx diff --git a/packages/app/src/scaffolder/CustomReviewState/CustomReviewStep.tsx b/packages/portal-app/src/scaffolder/CustomReviewState/CustomReviewStep.tsx similarity index 100% rename from packages/app/src/scaffolder/CustomReviewState/CustomReviewStep.tsx rename to packages/portal-app/src/scaffolder/CustomReviewState/CustomReviewStep.tsx diff --git a/packages/app/src/scaffolder/CustomReviewState/index.ts b/packages/portal-app/src/scaffolder/CustomReviewState/index.ts similarity index 100% rename from packages/app/src/scaffolder/CustomReviewState/index.ts rename to packages/portal-app/src/scaffolder/CustomReviewState/index.ts diff --git a/packages/app/src/scaffolder/CustomReviewState/styles.ts b/packages/portal-app/src/scaffolder/CustomReviewState/styles.ts similarity index 100% rename from packages/app/src/scaffolder/CustomReviewState/styles.ts rename to packages/portal-app/src/scaffolder/CustomReviewState/styles.ts diff --git a/packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/DeploymentPipelineFormWithYamlExtension.tsx b/packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/DeploymentPipelineFormWithYamlExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/DeploymentPipelineFormWithYamlExtension.tsx rename to packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/DeploymentPipelineFormWithYamlExtension.tsx diff --git a/packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/extensions.ts b/packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/extensions.ts rename to packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/extensions.ts diff --git a/packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/index.ts b/packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/index.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/index.ts rename to packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/index.ts diff --git a/packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/styles.ts b/packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/styles.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelineFormWithYaml/styles.ts rename to packages/portal-app/src/scaffolder/DeploymentPipelineFormWithYaml/styles.ts diff --git a/packages/app/src/scaffolder/DeploymentPipelinePicker/DeploymentPipelinePickerExtension.tsx b/packages/portal-app/src/scaffolder/DeploymentPipelinePicker/DeploymentPipelinePickerExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelinePicker/DeploymentPipelinePickerExtension.tsx rename to packages/portal-app/src/scaffolder/DeploymentPipelinePicker/DeploymentPipelinePickerExtension.tsx diff --git a/packages/app/src/scaffolder/DeploymentPipelinePicker/extensions.ts b/packages/portal-app/src/scaffolder/DeploymentPipelinePicker/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelinePicker/extensions.ts rename to packages/portal-app/src/scaffolder/DeploymentPipelinePicker/extensions.ts diff --git a/packages/app/src/scaffolder/DeploymentPipelinePicker/index.ts b/packages/portal-app/src/scaffolder/DeploymentPipelinePicker/index.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentPipelinePicker/index.ts rename to packages/portal-app/src/scaffolder/DeploymentPipelinePicker/index.ts diff --git a/packages/app/src/scaffolder/DeploymentSourcePicker/DeploymentSourcePicker.tsx b/packages/portal-app/src/scaffolder/DeploymentSourcePicker/DeploymentSourcePicker.tsx similarity index 100% rename from packages/app/src/scaffolder/DeploymentSourcePicker/DeploymentSourcePicker.tsx rename to packages/portal-app/src/scaffolder/DeploymentSourcePicker/DeploymentSourcePicker.tsx diff --git a/packages/app/src/scaffolder/DeploymentSourcePicker/extensions.ts b/packages/portal-app/src/scaffolder/DeploymentSourcePicker/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentSourcePicker/extensions.ts rename to packages/portal-app/src/scaffolder/DeploymentSourcePicker/extensions.ts diff --git a/packages/app/src/scaffolder/DeploymentSourcePicker/index.ts b/packages/portal-app/src/scaffolder/DeploymentSourcePicker/index.ts similarity index 100% rename from packages/app/src/scaffolder/DeploymentSourcePicker/index.ts rename to packages/portal-app/src/scaffolder/DeploymentSourcePicker/index.ts diff --git a/packages/app/src/scaffolder/EnvironmentFormWithYaml/EnvironmentFormWithYamlExtension.tsx b/packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/EnvironmentFormWithYamlExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/EnvironmentFormWithYaml/EnvironmentFormWithYamlExtension.tsx rename to packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/EnvironmentFormWithYamlExtension.tsx diff --git a/packages/app/src/scaffolder/EnvironmentFormWithYaml/extensions.ts b/packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/EnvironmentFormWithYaml/extensions.ts rename to packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/extensions.ts diff --git a/packages/app/src/scaffolder/EnvironmentFormWithYaml/index.ts b/packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/index.ts similarity index 100% rename from packages/app/src/scaffolder/EnvironmentFormWithYaml/index.ts rename to packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/index.ts diff --git a/packages/app/src/scaffolder/EnvironmentFormWithYaml/styles.ts b/packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/styles.ts similarity index 100% rename from packages/app/src/scaffolder/EnvironmentFormWithYaml/styles.ts rename to packages/portal-app/src/scaffolder/EnvironmentFormWithYaml/styles.ts diff --git a/packages/app/src/scaffolder/GitSecretField/GitSecretDialog.tsx b/packages/portal-app/src/scaffolder/GitSecretField/GitSecretDialog.tsx similarity index 100% rename from packages/app/src/scaffolder/GitSecretField/GitSecretDialog.tsx rename to packages/portal-app/src/scaffolder/GitSecretField/GitSecretDialog.tsx diff --git a/packages/app/src/scaffolder/GitSourceField/GitSourceField.tsx b/packages/portal-app/src/scaffolder/GitSourceField/GitSourceField.tsx similarity index 100% rename from packages/app/src/scaffolder/GitSourceField/GitSourceField.tsx rename to packages/portal-app/src/scaffolder/GitSourceField/GitSourceField.tsx diff --git a/packages/app/src/scaffolder/GitSourceField/extensions.ts b/packages/portal-app/src/scaffolder/GitSourceField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/GitSourceField/extensions.ts rename to packages/portal-app/src/scaffolder/GitSourceField/extensions.ts diff --git a/packages/app/src/scaffolder/GitSourceField/index.ts b/packages/portal-app/src/scaffolder/GitSourceField/index.ts similarity index 100% rename from packages/app/src/scaffolder/GitSourceField/index.ts rename to packages/portal-app/src/scaffolder/GitSourceField/index.ts diff --git a/packages/app/src/scaffolder/NamespaceEntityPicker/NamespaceEntityPicker.tsx b/packages/portal-app/src/scaffolder/NamespaceEntityPicker/NamespaceEntityPicker.tsx similarity index 100% rename from packages/app/src/scaffolder/NamespaceEntityPicker/NamespaceEntityPicker.tsx rename to packages/portal-app/src/scaffolder/NamespaceEntityPicker/NamespaceEntityPicker.tsx diff --git a/packages/app/src/scaffolder/NamespaceEntityPicker/NamespaceSelectField.tsx b/packages/portal-app/src/scaffolder/NamespaceEntityPicker/NamespaceSelectField.tsx similarity index 100% rename from packages/app/src/scaffolder/NamespaceEntityPicker/NamespaceSelectField.tsx rename to packages/portal-app/src/scaffolder/NamespaceEntityPicker/NamespaceSelectField.tsx diff --git a/packages/app/src/scaffolder/NamespaceEntityPicker/extensions.ts b/packages/portal-app/src/scaffolder/NamespaceEntityPicker/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/NamespaceEntityPicker/extensions.ts rename to packages/portal-app/src/scaffolder/NamespaceEntityPicker/extensions.ts diff --git a/packages/app/src/scaffolder/NamespaceEntityPicker/index.ts b/packages/portal-app/src/scaffolder/NamespaceEntityPicker/index.ts similarity index 100% rename from packages/app/src/scaffolder/NamespaceEntityPicker/index.ts rename to packages/portal-app/src/scaffolder/NamespaceEntityPicker/index.ts diff --git a/packages/app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.test.tsx b/packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.test.tsx similarity index 100% rename from packages/app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.test.tsx rename to packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.test.tsx diff --git a/packages/app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.tsx b/packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.tsx rename to packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/NotificationChannelFormWithYamlExtension.tsx diff --git a/packages/app/src/scaffolder/NotificationChannelFormWithYaml/extensions.ts b/packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/NotificationChannelFormWithYaml/extensions.ts rename to packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/extensions.ts diff --git a/packages/app/src/scaffolder/NotificationChannelFormWithYaml/index.ts b/packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/index.ts similarity index 100% rename from packages/app/src/scaffolder/NotificationChannelFormWithYaml/index.ts rename to packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/index.ts diff --git a/packages/app/src/scaffolder/NotificationChannelFormWithYaml/styles.ts b/packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/styles.ts similarity index 100% rename from packages/app/src/scaffolder/NotificationChannelFormWithYaml/styles.ts rename to packages/portal-app/src/scaffolder/NotificationChannelFormWithYaml/styles.ts diff --git a/packages/app/src/scaffolder/ProjectNamespaceField/ProjectNamespaceField.tsx b/packages/portal-app/src/scaffolder/ProjectNamespaceField/ProjectNamespaceField.tsx similarity index 100% rename from packages/app/src/scaffolder/ProjectNamespaceField/ProjectNamespaceField.tsx rename to packages/portal-app/src/scaffolder/ProjectNamespaceField/ProjectNamespaceField.tsx diff --git a/packages/app/src/scaffolder/ProjectNamespaceField/extensions.ts b/packages/portal-app/src/scaffolder/ProjectNamespaceField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectNamespaceField/extensions.ts rename to packages/portal-app/src/scaffolder/ProjectNamespaceField/extensions.ts diff --git a/packages/app/src/scaffolder/ProjectNamespaceField/index.ts b/packages/portal-app/src/scaffolder/ProjectNamespaceField/index.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectNamespaceField/index.ts rename to packages/portal-app/src/scaffolder/ProjectNamespaceField/index.ts diff --git a/packages/app/src/scaffolder/ProjectNamespaceField/projectNamespaceFieldValidation.test.ts b/packages/portal-app/src/scaffolder/ProjectNamespaceField/projectNamespaceFieldValidation.test.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectNamespaceField/projectNamespaceFieldValidation.test.ts rename to packages/portal-app/src/scaffolder/ProjectNamespaceField/projectNamespaceFieldValidation.test.ts diff --git a/packages/app/src/scaffolder/ProjectParametersField/ProjectParametersField.test.tsx b/packages/portal-app/src/scaffolder/ProjectParametersField/ProjectParametersField.test.tsx similarity index 100% rename from packages/app/src/scaffolder/ProjectParametersField/ProjectParametersField.test.tsx rename to packages/portal-app/src/scaffolder/ProjectParametersField/ProjectParametersField.test.tsx diff --git a/packages/app/src/scaffolder/ProjectParametersField/ProjectParametersField.tsx b/packages/portal-app/src/scaffolder/ProjectParametersField/ProjectParametersField.tsx similarity index 100% rename from packages/app/src/scaffolder/ProjectParametersField/ProjectParametersField.tsx rename to packages/portal-app/src/scaffolder/ProjectParametersField/ProjectParametersField.tsx diff --git a/packages/app/src/scaffolder/ProjectParametersField/extensions.ts b/packages/portal-app/src/scaffolder/ProjectParametersField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectParametersField/extensions.ts rename to packages/portal-app/src/scaffolder/ProjectParametersField/extensions.ts diff --git a/packages/app/src/scaffolder/ProjectParametersField/index.ts b/packages/portal-app/src/scaffolder/ProjectParametersField/index.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectParametersField/index.ts rename to packages/portal-app/src/scaffolder/ProjectParametersField/index.ts diff --git a/packages/app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.test.tsx b/packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.test.tsx similarity index 100% rename from packages/app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.test.tsx rename to packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.test.tsx diff --git a/packages/app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/ProjectTypeYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ProjectTypeYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectTypeYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ProjectTypeYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectTypeYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ProjectTypeYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ProjectTypeYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ProjectTypeYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ResourceNamePicker/ResourceNamePickerExtension.tsx b/packages/portal-app/src/scaffolder/ResourceNamePicker/ResourceNamePickerExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ResourceNamePicker/ResourceNamePickerExtension.tsx rename to packages/portal-app/src/scaffolder/ResourceNamePicker/ResourceNamePickerExtension.tsx diff --git a/packages/app/src/scaffolder/ResourceNamePicker/extensions.ts b/packages/portal-app/src/scaffolder/ResourceNamePicker/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceNamePicker/extensions.ts rename to packages/portal-app/src/scaffolder/ResourceNamePicker/extensions.ts diff --git a/packages/app/src/scaffolder/ResourceNamePicker/index.ts b/packages/portal-app/src/scaffolder/ResourceNamePicker/index.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceNamePicker/index.ts rename to packages/portal-app/src/scaffolder/ResourceNamePicker/index.ts diff --git a/packages/app/src/scaffolder/ResourceParametersField/ResourceParametersField.tsx b/packages/portal-app/src/scaffolder/ResourceParametersField/ResourceParametersField.tsx similarity index 100% rename from packages/app/src/scaffolder/ResourceParametersField/ResourceParametersField.tsx rename to packages/portal-app/src/scaffolder/ResourceParametersField/ResourceParametersField.tsx diff --git a/packages/app/src/scaffolder/ResourceParametersField/extensions.ts b/packages/portal-app/src/scaffolder/ResourceParametersField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceParametersField/extensions.ts rename to packages/portal-app/src/scaffolder/ResourceParametersField/extensions.ts diff --git a/packages/app/src/scaffolder/ResourceParametersField/index.ts b/packages/portal-app/src/scaffolder/ResourceParametersField/index.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceParametersField/index.ts rename to packages/portal-app/src/scaffolder/ResourceParametersField/index.ts diff --git a/packages/app/src/scaffolder/ResourceTypeYamlEditor/ResourceTypeYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/ResourceTypeYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/ResourceTypeYamlEditor/ResourceTypeYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/ResourceTypeYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/ResourceTypeYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceTypeYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/ResourceTypeYamlEditor/index.ts b/packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceTypeYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/ResourceTypeYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/ResourceTypeYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/ResourceTypeYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/ScaffolderLayout.tsx b/packages/portal-app/src/scaffolder/ScaffolderLayout.tsx similarity index 100% rename from packages/app/src/scaffolder/ScaffolderLayout.tsx rename to packages/portal-app/src/scaffolder/ScaffolderLayout.tsx diff --git a/packages/app/src/scaffolder/ScaffolderPreselectionContext.tsx b/packages/portal-app/src/scaffolder/ScaffolderPreselectionContext.tsx similarity index 100% rename from packages/app/src/scaffolder/ScaffolderPreselectionContext.tsx rename to packages/portal-app/src/scaffolder/ScaffolderPreselectionContext.tsx diff --git a/packages/app/src/scaffolder/SwitchField/SwitchFieldExtension.test.tsx b/packages/portal-app/src/scaffolder/SwitchField/SwitchFieldExtension.test.tsx similarity index 100% rename from packages/app/src/scaffolder/SwitchField/SwitchFieldExtension.test.tsx rename to packages/portal-app/src/scaffolder/SwitchField/SwitchFieldExtension.test.tsx diff --git a/packages/app/src/scaffolder/SwitchField/SwitchFieldExtension.tsx b/packages/portal-app/src/scaffolder/SwitchField/SwitchFieldExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/SwitchField/SwitchFieldExtension.tsx rename to packages/portal-app/src/scaffolder/SwitchField/SwitchFieldExtension.tsx diff --git a/packages/app/src/scaffolder/SwitchField/extensions.ts b/packages/portal-app/src/scaffolder/SwitchField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/SwitchField/extensions.ts rename to packages/portal-app/src/scaffolder/SwitchField/extensions.ts diff --git a/packages/app/src/scaffolder/SwitchField/index.ts b/packages/portal-app/src/scaffolder/SwitchField/index.ts similarity index 100% rename from packages/app/src/scaffolder/SwitchField/index.ts rename to packages/portal-app/src/scaffolder/SwitchField/index.ts diff --git a/packages/app/src/scaffolder/TraitYamlEditor/TraitYamlEditorExtension.tsx b/packages/portal-app/src/scaffolder/TraitYamlEditor/TraitYamlEditorExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/TraitYamlEditor/TraitYamlEditorExtension.tsx rename to packages/portal-app/src/scaffolder/TraitYamlEditor/TraitYamlEditorExtension.tsx diff --git a/packages/app/src/scaffolder/TraitYamlEditor/extensions.ts b/packages/portal-app/src/scaffolder/TraitYamlEditor/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/TraitYamlEditor/extensions.ts rename to packages/portal-app/src/scaffolder/TraitYamlEditor/extensions.ts diff --git a/packages/app/src/scaffolder/TraitYamlEditor/index.ts b/packages/portal-app/src/scaffolder/TraitYamlEditor/index.ts similarity index 100% rename from packages/app/src/scaffolder/TraitYamlEditor/index.ts rename to packages/portal-app/src/scaffolder/TraitYamlEditor/index.ts diff --git a/packages/app/src/scaffolder/TraitYamlEditor/styles.ts b/packages/portal-app/src/scaffolder/TraitYamlEditor/styles.ts similarity index 100% rename from packages/app/src/scaffolder/TraitYamlEditor/styles.ts rename to packages/portal-app/src/scaffolder/TraitYamlEditor/styles.ts diff --git a/packages/app/src/scaffolder/TraitsField/NoTraitsAvailableMessage.tsx b/packages/portal-app/src/scaffolder/TraitsField/NoTraitsAvailableMessage.tsx similarity index 100% rename from packages/app/src/scaffolder/TraitsField/NoTraitsAvailableMessage.tsx rename to packages/portal-app/src/scaffolder/TraitsField/NoTraitsAvailableMessage.tsx diff --git a/packages/app/src/scaffolder/TraitsField/TraitCard.tsx b/packages/portal-app/src/scaffolder/TraitsField/TraitCard.tsx similarity index 100% rename from packages/app/src/scaffolder/TraitsField/TraitCard.tsx rename to packages/portal-app/src/scaffolder/TraitsField/TraitCard.tsx diff --git a/packages/app/src/scaffolder/TraitsField/TraitPicker.tsx b/packages/portal-app/src/scaffolder/TraitsField/TraitPicker.tsx similarity index 100% rename from packages/app/src/scaffolder/TraitsField/TraitPicker.tsx rename to packages/portal-app/src/scaffolder/TraitsField/TraitPicker.tsx diff --git a/packages/app/src/scaffolder/TraitsField/TraitsFieldExtension.tsx b/packages/portal-app/src/scaffolder/TraitsField/TraitsFieldExtension.tsx similarity index 100% rename from packages/app/src/scaffolder/TraitsField/TraitsFieldExtension.tsx rename to packages/portal-app/src/scaffolder/TraitsField/TraitsFieldExtension.tsx diff --git a/packages/app/src/scaffolder/TraitsField/extensions.ts b/packages/portal-app/src/scaffolder/TraitsField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/TraitsField/extensions.ts rename to packages/portal-app/src/scaffolder/TraitsField/extensions.ts diff --git a/packages/app/src/scaffolder/TraitsField/index.ts b/packages/portal-app/src/scaffolder/TraitsField/index.ts similarity index 100% rename from packages/app/src/scaffolder/TraitsField/index.ts rename to packages/portal-app/src/scaffolder/TraitsField/index.ts diff --git a/packages/app/src/scaffolder/WorkloadDetailsField/WorkloadDetailsField.tsx b/packages/portal-app/src/scaffolder/WorkloadDetailsField/WorkloadDetailsField.tsx similarity index 100% rename from packages/app/src/scaffolder/WorkloadDetailsField/WorkloadDetailsField.tsx rename to packages/portal-app/src/scaffolder/WorkloadDetailsField/WorkloadDetailsField.tsx diff --git a/packages/app/src/scaffolder/WorkloadDetailsField/extensions.ts b/packages/portal-app/src/scaffolder/WorkloadDetailsField/extensions.ts similarity index 100% rename from packages/app/src/scaffolder/WorkloadDetailsField/extensions.ts rename to packages/portal-app/src/scaffolder/WorkloadDetailsField/extensions.ts diff --git a/packages/app/src/scaffolder/WorkloadDetailsField/index.ts b/packages/portal-app/src/scaffolder/WorkloadDetailsField/index.ts similarity index 100% rename from packages/app/src/scaffolder/WorkloadDetailsField/index.ts rename to packages/portal-app/src/scaffolder/WorkloadDetailsField/index.ts diff --git a/packages/app/src/scaffolder/openChoreoTokenDecorator.ts b/packages/portal-app/src/scaffolder/openChoreoTokenDecorator.ts similarity index 100% rename from packages/app/src/scaffolder/openChoreoTokenDecorator.ts rename to packages/portal-app/src/scaffolder/openChoreoTokenDecorator.ts diff --git a/packages/app/src/scaffolder/types.ts b/packages/portal-app/src/scaffolder/types.ts similarity index 100% rename from packages/app/src/scaffolder/types.ts rename to packages/portal-app/src/scaffolder/types.ts diff --git a/packages/app/src/scaffolder/utils/rjsfUtils.test.ts b/packages/portal-app/src/scaffolder/utils/rjsfUtils.test.ts similarity index 100% rename from packages/app/src/scaffolder/utils/rjsfUtils.test.ts rename to packages/portal-app/src/scaffolder/utils/rjsfUtils.test.ts diff --git a/packages/app/src/scaffolder/utils/rjsfUtils.tsx b/packages/portal-app/src/scaffolder/utils/rjsfUtils.tsx similarity index 100% rename from packages/app/src/scaffolder/utils/rjsfUtils.tsx rename to packages/portal-app/src/scaffolder/utils/rjsfUtils.tsx diff --git a/packages/portal-app/src/setupTests.ts b/packages/portal-app/src/setupTests.ts new file mode 100644 index 000000000..7b0828bfa --- /dev/null +++ b/packages/portal-app/src/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/packages/app/src/themes.tsx b/packages/portal-app/src/themes.tsx similarity index 100% rename from packages/app/src/themes.tsx rename to packages/portal-app/src/themes.tsx diff --git a/packages/app/src/utils/kindUtils.ts b/packages/portal-app/src/utils/kindUtils.ts similarity index 100% rename from packages/app/src/utils/kindUtils.ts rename to packages/portal-app/src/utils/kindUtils.ts diff --git a/packages/portal-backend/.eslintrc.js b/packages/portal-backend/.eslintrc.js new file mode 100644 index 000000000..e2a53a6ad --- /dev/null +++ b/packages/portal-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/portal-backend/README.md b/packages/portal-backend/README.md new file mode 100644 index 000000000..722e819b3 --- /dev/null +++ b/packages/portal-backend/README.md @@ -0,0 +1,33 @@ +# @openchoreo/backstage-portal-backend + +The OpenChoreo Portal's backend composition as a reusable package. It bundles +every backend plugin, module, and service factory the stock portal runs, so a +custom portal backend is a few lines: + +```ts +import { createBackend } from '@backstage/backend-defaults'; +import { + portalBackendFeatures, + portalRootHttpRouterServiceFactory, +} from '@openchoreo/backstage-portal-backend'; + +const backend = createBackend(); + +backend.add(portalRootHttpRouterServiceFactory); +backend.add(portalBackendFeatures); + +// Add your own plugins alongside: +// backend.add(import('@internal/my-plugin-backend')); + +backend.start(); +``` + +## Exports + +- `portalBackendFeatures` — a feature loader bundling the Backstage core + plugins (app, auth, catalog, scaffolder, search, techdocs, permission, + events, proxy, user-settings), the Jenkins CI integration, and all + OpenChoreo backend plugins and modules, in the required registration order. +- `portalRootHttpRouterServiceFactory` — the root HTTP router pre-configured + with the OpenChoreo IDP token header middleware. Kept separate from the + bundle so hosts can substitute their own root-router configuration. diff --git a/packages/portal-backend/package.json b/packages/portal-backend/package.json new file mode 100644 index 000000000..ae8f5f627 --- /dev/null +++ b/packages/portal-backend/package.json @@ -0,0 +1,70 @@ +{ + "name": "@openchoreo/backstage-portal-backend", + "version": "1.2.0", + "description": "The OpenChoreo Portal's backend composition as a reusable bundle: all portal backend plugins, modules, and service factories plus the IDP token root-router middleware", + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts", + "registry": "https://npm.pkg.github.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/openchoreo/backstage-plugins.git", + "directory": "packages/portal-backend" + }, + "backstage": { + "role": "node-library" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage-community/plugin-jenkins-backend": "^0.24.1", + "@backstage/backend-defaults": "^0.17.1", + "@backstage/backend-plugin-api": "^1.9.1", + "@backstage/plugin-app-backend": "^0.5.14", + "@backstage/plugin-auth-backend": "^0.29.0", + "@backstage/plugin-auth-backend-module-github-provider": "^0.5.3", + "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.19", + "@backstage/plugin-catalog-backend": "^3.7.0", + "@backstage/plugin-catalog-backend-module-logs": "^0.1.22", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.2.20", + "@backstage/plugin-events-backend": "^0.6.2", + "@backstage/plugin-permission-backend": "^0.7.12", + "@backstage/plugin-proxy-backend": "^0.6.13", + "@backstage/plugin-scaffolder-backend": "^4.0.0", + "@backstage/plugin-scaffolder-backend-module-github": "^0.9.9", + "@backstage/plugin-search-backend": "^2.1.2", + "@backstage/plugin-search-backend-module-catalog": "^0.3.15", + "@backstage/plugin-search-backend-module-pg": "^0.5.55", + "@backstage/plugin-techdocs-backend": "^2.2.0", + "@backstage/plugin-user-settings-backend": "^0.4.3", + "@openchoreo/backstage-plugin-auth-backend-module-openchoreo-auth": "workspace:^", + "@openchoreo/backstage-plugin-backend": "workspace:^", + "@openchoreo/backstage-plugin-catalog-backend-module": "workspace:^", + "@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-ci-backend": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-observability-backend": "workspace:^", + "@openchoreo/backstage-plugin-openchoreo-workflows-backend": "workspace:^", + "@openchoreo/backstage-plugin-permission-backend-module-openchoreo-policy": "workspace:^", + "@openchoreo/backstage-plugin-platform-engineer-core-backend": "workspace:^", + "@openchoreo/backstage-plugin-scaffolder-backend-module": "workspace:^", + "@openchoreo/openchoreo-auth": "workspace:^" + }, + "devDependencies": { + "@backstage/cli": "^0.36.2" + }, + "files": [ + "dist" + ] +} diff --git a/packages/portal-backend/src/features.test.ts b/packages/portal-backend/src/features.test.ts new file mode 100644 index 000000000..210d8203f --- /dev/null +++ b/packages/portal-backend/src/features.test.ts @@ -0,0 +1,36 @@ +import { + immediateCatalogServiceFactory, + annotationStoreFactory, +} from '@openchoreo/backstage-plugin-catalog-backend-module'; +import { + portalBackendFeatures, + portalFeatureLoaders, + portalServiceFactories, +} from './features'; + +describe('portalBackendFeatures', () => { + it('is a backend feature loader', () => { + expect((portalBackendFeatures as any).$$type).toBe( + '@backstage/BackendFeature', + ); + expect((portalBackendFeatures as any).featureType).toBe('loader'); + }); + + it('registers the OpenChoreo service factories ahead of the plugins', () => { + // openchoreo-backend depends on the AnnotationStore initialized by the + // catalog module, so the factories must be registered before any plugin. + expect(portalServiceFactories).toEqual([ + immediateCatalogServiceFactory, + annotationStoreFactory, + ]); + }); + + it('composes the full portal plugin set', () => { + // If you add or remove a feature in features.ts, update this count — + // the composition is this package's contract. + expect(portalFeatureLoaders).toHaveLength(28); + for (const load of portalFeatureLoaders) { + expect(typeof load).toBe('function'); + } + }); +}); diff --git a/packages/portal-backend/src/features.ts b/packages/portal-backend/src/features.ts new file mode 100644 index 000000000..af4253cb6 --- /dev/null +++ b/packages/portal-backend/src/features.ts @@ -0,0 +1,119 @@ +import { createBackendFeatureLoader } from '@backstage/backend-plugin-api'; +import { OpenChoreoAuthModule } from '@openchoreo/backstage-plugin-auth-backend-module-openchoreo-auth'; +import { + immediateCatalogServiceFactory, + annotationStoreFactory, +} from '@openchoreo/backstage-plugin-catalog-backend-module'; + +/** + * OpenChoreo service factories, registered ahead of every plugin: the catalog + * module and openchoreo-backend depend on these (the AnnotationStore is + * initialized by the catalog module). Exported for tests — consume + * {@link portalBackendFeatures} instead. + * + * @internal + */ +export const portalServiceFactories = [ + immediateCatalogServiceFactory, + annotationStoreFactory, +]; + +/** + * The portal's plugin/module composition, in registration order, as lazy + * import thunks. Exported for tests — consume {@link portalBackendFeatures} + * instead. + * + * @internal + */ +export const portalFeatureLoaders = [ + () => import('@backstage/plugin-app-backend'), + () => import('@backstage/plugin-proxy-backend'), + () => import('@backstage/plugin-scaffolder-backend'), + () => import('@backstage/plugin-scaffolder-backend-module-github'), + () => import('@backstage/plugin-techdocs-backend'), + + // auth plugin + // See https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin + () => import('@backstage/plugin-auth-backend'), + // Auth providers - both registered, but each checks config to determine if it should activate + // OpenChoreo Auth provider - works with any OIDC-compliant IDP (active when openchoreo.features.auth.enabled = true) + () => OpenChoreoAuthModule, + // Guest provider for development/demo mode (active when openchoreo.features.auth.enabled = false) + () => import('@backstage/plugin-auth-backend-module-guest-provider'), + // Github provider + () => import('@backstage/plugin-auth-backend-module-github-provider'), + // events plugin — receives webhook POSTs and publishes to EventsService + () => import('@backstage/plugin-events-backend'), + + // catalog plugin + () => import('@backstage/plugin-catalog-backend'), + () => + import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'), + // See https://backstage.io/docs/features/software-catalog/configuration#subscribing-to-catalog-errors + () => import('@backstage/plugin-catalog-backend-module-logs'), + + // permission plugin + () => import('@backstage/plugin-permission-backend'), + // OpenChoreo permission policy - handles openchoreo.* permissions via /authz/profile API + // Falls back to ALLOW for non-OpenChoreo permissions (composable with other policies) + () => + import( + '@openchoreo/backstage-plugin-permission-backend-module-openchoreo-policy' + ), + + // search plugin, engine and collators + // See https://backstage.io/docs/features/search/search-engines + () => import('@backstage/plugin-search-backend'), + () => import('@backstage/plugin-search-backend-module-pg'), + () => import('@backstage/plugin-search-backend-module-catalog'), + + // user settings plugin - enables centralized storage for starred entities and user preferences + () => import('@backstage/plugin-user-settings-backend'), + + // IMPORTANT: catalog-backend-module MUST be registered before openchoreo-backend + // because openchoreo-backend depends on the AnnotationStore which is initialized + // by the catalog module. + () => import('@openchoreo/backstage-plugin-catalog-backend-module'), + () => import('@openchoreo/backstage-plugin-backend'), + () => import('@openchoreo/backstage-plugin-scaffolder-backend-module'), + () => + import( + '@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users' + ), + () => import('@openchoreo/backstage-plugin-platform-engineer-core-backend'), + () => import('@openchoreo/backstage-plugin-openchoreo-observability-backend'), + () => import('@openchoreo/backstage-plugin-openchoreo-ci-backend'), + + // External CI Platform Integrations + // Jenkins: Handles missing config gracefully (API calls fail, not startup) + () => import('@backstage-community/plugin-jenkins-backend'), + + () => import('@openchoreo/backstage-plugin-openchoreo-workflows-backend'), +]; + +/** + * The OpenChoreo Portal's backend composition as a single feature. + * + * Bundles every backend plugin, module, and service factory the stock portal + * runs — Backstage core plugins (app, auth, catalog, scaffolder, search, + * techdocs, permission, events, proxy, user-settings), the Jenkins CI + * integration, and all OpenChoreo backend plugins and modules. Add it to a + * backend with a single `backend.add(portalBackendFeatures)`; additional + * features can still be added alongside it with further `backend.add(...)` + * calls. + * + * Note: the portal's root HTTP router middleware is intentionally NOT part of + * this bundle — add {@link portalRootHttpRouterServiceFactory} separately so + * hosts can substitute their own root router configuration. + * + * @public + */ +export const portalBackendFeatures = createBackendFeatureLoader({ + *loader() { + yield* portalServiceFactories; + + for (const load of portalFeatureLoaders) { + yield load(); + } + }, +}); diff --git a/packages/portal-backend/src/index.ts b/packages/portal-backend/src/index.ts new file mode 100644 index 000000000..dc84e94fd --- /dev/null +++ b/packages/portal-backend/src/index.ts @@ -0,0 +1,2 @@ +export { portalBackendFeatures } from './features'; +export { portalRootHttpRouterServiceFactory } from './rootHttpRouter'; diff --git a/packages/portal-backend/src/rootHttpRouter.test.ts b/packages/portal-backend/src/rootHttpRouter.test.ts new file mode 100644 index 000000000..06717add7 --- /dev/null +++ b/packages/portal-backend/src/rootHttpRouter.test.ts @@ -0,0 +1,10 @@ +import { portalRootHttpRouterServiceFactory } from './rootHttpRouter'; + +describe('portalRootHttpRouterServiceFactory', () => { + it('overrides the root HTTP router service', () => { + const factory = portalRootHttpRouterServiceFactory as any; + expect(factory.$$type).toBe('@backstage/BackendFeature'); + expect(factory.service.id).toBe('core.rootHttpRouter'); + expect(factory.service.scope).toBe('root'); + }); +}); diff --git a/packages/portal-backend/src/rootHttpRouter.ts b/packages/portal-backend/src/rootHttpRouter.ts new file mode 100644 index 000000000..d69fb0fec --- /dev/null +++ b/packages/portal-backend/src/rootHttpRouter.ts @@ -0,0 +1,25 @@ +import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter'; +import { createIdpTokenHeaderMiddleware } from '@openchoreo/openchoreo-auth'; + +/** + * Root HTTP router service factory pre-configured with the OpenChoreo IDP + * token header middleware. + * + * The middleware reads the IDP token from the x-openchoreo-token header and + * establishes the AsyncLocalStorage context so getUserTokenFromContext() works + * in the permission policy and elsewhere. It must wrap ALL route handlers, + * which is why it is registered on the root HTTP router before applyDefaults(). + * + * @public + */ +export const portalRootHttpRouterServiceFactory = rootHttpRouterServiceFactory({ + configure: ({ app, applyDefaults }) => { + // Registered before applyDefaults() so it wraps all route handlers. + app.use(createIdpTokenHeaderMiddleware()); + + // applyDefaults() applies the standard middleware stack ONCE: + // helmet, cors, compression, logging, rateLimit, then routes + error + // handling. Do NOT re-apply any of these manually here. + applyDefaults(); + }, +}); diff --git a/plugins/openchoreo-react/src/hooks/useOpenChoreoCache.test.tsx b/plugins/openchoreo-react/src/hooks/useOpenChoreoCache.test.tsx index 0a0167c52..3b07ce7d1 100644 --- a/plugins/openchoreo-react/src/hooks/useOpenChoreoCache.test.tsx +++ b/plugins/openchoreo-react/src/hooks/useOpenChoreoCache.test.tsx @@ -32,24 +32,33 @@ describe('useOpenChoreoCache', () => { }); it('fetchQuery serves the cached value within staleTime (dedupes the fetcher)', async () => { - const fetcher = jest.fn().mockResolvedValue('v'); - const { result } = renderHook(() => useOpenChoreoCache(), { - wrapper: createQueryClientWrapper(), - }); + // The wrapper's QueryClient uses gcTime: 0, which evicts the unobserved + // entry on a setTimeout(0) once the first fetch settles — so with real + // timers the second fetchQuery races the GC timer. Fake timers keep that + // timer pending so this asserts the staleTime contract, not scheduling. + jest.useFakeTimers(); + try { + const fetcher = jest.fn().mockResolvedValue('v'); + const { result } = renderHook(() => useOpenChoreoCache(), { + wrapper: createQueryClientWrapper(), + }); - await act(async () => { - await result.current.fetchQuery(['k'], fetcher, { staleTime: 60_000 }); - }); - let second: unknown; - await act(async () => { - second = await result.current.fetchQuery(['k'], fetcher, { - staleTime: 60_000, + await act(async () => { + await result.current.fetchQuery(['k'], fetcher, { staleTime: 60_000 }); + }); + let second: unknown; + await act(async () => { + second = await result.current.fetchQuery(['k'], fetcher, { + staleTime: 60_000, + }); }); - }); - expect(second).toBe('v'); - // Within staleTime the fetcher is not called again. - expect(fetcher).toHaveBeenCalledTimes(1); + expect(second).toBe('v'); + // Within staleTime the fetcher is not called again. + expect(fetcher).toHaveBeenCalledTimes(1); + } finally { + jest.useRealTimers(); + } }); it('fetchQuery propagates a rejected fetcher and leaves nothing cached', async () => { diff --git a/yarn.lock b/yarn.lock index 24e14b1a3..2df2b5bf6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10307,6 +10307,130 @@ __metadata: languageName: unknown linkType: soft +"@openchoreo/backstage-portal-app@workspace:^, @openchoreo/backstage-portal-app@workspace:packages/portal-app": + version: 0.0.0-use.local + resolution: "@openchoreo/backstage-portal-app@workspace:packages/portal-app" + dependencies: + "@backstage-community/plugin-github-actions": "npm:^0.20.0" + "@backstage-community/plugin-jenkins": "npm:^0.28.0" + "@backstage/catalog-client": "npm:^1.15.1" + "@backstage/catalog-model": "npm:^1.9.0" + "@backstage/cli": "npm:^0.36.2" + "@backstage/config": "npm:^1.3.8" + "@backstage/core-app-api": "npm:^1.20.1" + "@backstage/core-compat-api": "npm:^0.5.11" + "@backstage/core-components": "npm:^0.18.10" + "@backstage/core-plugin-api": "npm:^1.12.6" + "@backstage/frontend-app-api": "npm:^0.16.3" + "@backstage/frontend-defaults": "npm:^0.5.2" + "@backstage/frontend-plugin-api": "npm:^0.17.0" + "@backstage/frontend-test-utils": "npm:^0.6.0" + "@backstage/integration-react": "npm:^1.2.18" + "@backstage/plugin-api-docs": "npm:^0.14.1" + "@backstage/plugin-app": "npm:^0.4.6" + "@backstage/plugin-app-react": "npm:^0.2.3" + "@backstage/plugin-catalog": "npm:^2.0.5" + "@backstage/plugin-catalog-common": "npm:^1.1.10" + "@backstage/plugin-catalog-graph": "npm:^0.6.4" + "@backstage/plugin-catalog-import": "npm:^0.13.13" + "@backstage/plugin-catalog-react": "npm:^3.0.0" + "@backstage/plugin-home": "npm:^0.9.6" + "@backstage/plugin-kubernetes": "npm:^0.12.19" + "@backstage/plugin-org": "npm:^0.7.4" + "@backstage/plugin-permission-common": "npm:^0.9.9" + "@backstage/plugin-permission-react": "npm:^0.5.1" + "@backstage/plugin-scaffolder": "npm:^1.37.0" + "@backstage/plugin-scaffolder-common": "npm:^2.2.0" + "@backstage/plugin-scaffolder-react": "npm:^2.0.0" + "@backstage/plugin-search": "npm:^1.7.4" + "@backstage/plugin-search-react": "npm:^1.11.4" + "@backstage/plugin-techdocs": "npm:^1.17.6" + "@backstage/plugin-techdocs-common": "npm:^0.1.1" + "@backstage/plugin-techdocs-module-addons-contrib": "npm:^1.1.36" + "@backstage/plugin-techdocs-react": "npm:^1.3.11" + "@backstage/plugin-user-settings": "npm:^0.9.3" + "@backstage/test-utils": "npm:^1.7.18" + "@backstage/theme": "npm:^0.7.3" + "@gitbeaker/rest": "npm:^40.6.0" + "@immobiliarelabs/backstage-plugin-gitlab": "npm:^6.13.0" + "@material-ui/core": "npm:4.12.4" + "@material-ui/icons": "npm:4.11.3" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@openchoreo/backstage-design-system": "workspace:^" + "@openchoreo/backstage-plugin": "workspace:^" + "@openchoreo/backstage-plugin-common": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-ci": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-observability": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-portal-assistant": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-workflows": "workspace:^" + "@openchoreo/backstage-plugin-platform-engineer-core": "workspace:^" + "@openchoreo/backstage-plugin-react": "workspace:^" + "@rjsf/core": "npm:5.24.13" + "@rjsf/material-ui": "npm:5.24.13" + "@rjsf/utils": "npm:5.24.13" + "@rjsf/validator-ajv8": "npm:5.24.13" + "@tanstack/react-query": "npm:^5.101.2" + "@testing-library/dom": "npm:9.3.4" + "@testing-library/jest-dom": "npm:6.9.1" + "@testing-library/react": "npm:14.3.1" + "@testing-library/user-event": "npm:14.6.1" + "@types/react-dom": "npm:*" + clsx: "npm:^2.1.1" + d3-selection: "npm:^3.0.0" + d3-zoom: "npm:^3.0.0" + devicons-react: "npm:^1.5.0" + json-schema: "npm:0.4.0" + react: "npm:18.3.1" + react-dom: "npm:18.3.1" + react-router-dom: "npm:6.30.1" + react-use: "npm:17.6.0" + yaml: "npm:^2.8.1" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + react-router-dom: ^6.0.0 + languageName: unknown + linkType: soft + +"@openchoreo/backstage-portal-backend@workspace:^, @openchoreo/backstage-portal-backend@workspace:packages/portal-backend": + version: 0.0.0-use.local + resolution: "@openchoreo/backstage-portal-backend@workspace:packages/portal-backend" + dependencies: + "@backstage-community/plugin-jenkins-backend": "npm:^0.24.1" + "@backstage/backend-defaults": "npm:^0.17.1" + "@backstage/backend-plugin-api": "npm:^1.9.1" + "@backstage/cli": "npm:^0.36.2" + "@backstage/plugin-app-backend": "npm:^0.5.14" + "@backstage/plugin-auth-backend": "npm:^0.29.0" + "@backstage/plugin-auth-backend-module-github-provider": "npm:^0.5.3" + "@backstage/plugin-auth-backend-module-guest-provider": "npm:^0.2.19" + "@backstage/plugin-catalog-backend": "npm:^3.7.0" + "@backstage/plugin-catalog-backend-module-logs": "npm:^0.1.22" + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.2.20" + "@backstage/plugin-events-backend": "npm:^0.6.2" + "@backstage/plugin-permission-backend": "npm:^0.7.12" + "@backstage/plugin-proxy-backend": "npm:^0.6.13" + "@backstage/plugin-scaffolder-backend": "npm:^4.0.0" + "@backstage/plugin-scaffolder-backend-module-github": "npm:^0.9.9" + "@backstage/plugin-search-backend": "npm:^2.1.2" + "@backstage/plugin-search-backend-module-catalog": "npm:^0.3.15" + "@backstage/plugin-search-backend-module-pg": "npm:^0.5.55" + "@backstage/plugin-techdocs-backend": "npm:^2.2.0" + "@backstage/plugin-user-settings-backend": "npm:^0.4.3" + "@openchoreo/backstage-plugin-auth-backend-module-openchoreo-auth": "workspace:^" + "@openchoreo/backstage-plugin-backend": "workspace:^" + "@openchoreo/backstage-plugin-catalog-backend-module": "workspace:^" + "@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-ci-backend": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-observability-backend": "workspace:^" + "@openchoreo/backstage-plugin-openchoreo-workflows-backend": "workspace:^" + "@openchoreo/backstage-plugin-permission-backend-module-openchoreo-policy": "workspace:^" + "@openchoreo/backstage-plugin-platform-engineer-core-backend": "workspace:^" + "@openchoreo/backstage-plugin-scaffolder-backend-module": "workspace:^" + "@openchoreo/openchoreo-auth": "workspace:^" + languageName: unknown + linkType: soft + "@openchoreo/cell-diagram@workspace:^, @openchoreo/cell-diagram@workspace:packages/cell-diagram": version: 0.0.0-use.local resolution: "@openchoreo/cell-diagram@workspace:packages/cell-diagram" @@ -16936,85 +17060,17 @@ __metadata: resolution: "app@workspace:packages/app" dependencies: "@axe-core/playwright": "npm:^4.11.3" - "@backstage-community/plugin-github-actions": "npm:^0.20.0" - "@backstage-community/plugin-jenkins": "npm:^0.28.0" - "@backstage/app-defaults": "npm:^1.7.8" - "@backstage/catalog-client": "npm:^1.15.1" - "@backstage/catalog-model": "npm:^1.9.0" "@backstage/cli": "npm:^0.36.2" - "@backstage/config": "npm:^1.3.8" - "@backstage/core-app-api": "npm:^1.20.1" - "@backstage/core-compat-api": "npm:^0.5.11" - "@backstage/core-components": "npm:^0.18.10" - "@backstage/core-plugin-api": "npm:^1.12.6" - "@backstage/frontend-app-api": "npm:^0.16.3" - "@backstage/frontend-defaults": "npm:^0.5.2" - "@backstage/frontend-plugin-api": "npm:^0.17.0" - "@backstage/frontend-test-utils": "npm:^0.6.0" - "@backstage/integration-react": "npm:^1.2.18" - "@backstage/plugin-api-docs": "npm:^0.14.1" - "@backstage/plugin-app": "npm:^0.4.6" - "@backstage/plugin-app-react": "npm:^0.2.3" - "@backstage/plugin-catalog": "npm:^2.0.5" - "@backstage/plugin-catalog-common": "npm:^1.1.10" - "@backstage/plugin-catalog-graph": "npm:^0.6.4" - "@backstage/plugin-catalog-import": "npm:^0.13.13" - "@backstage/plugin-catalog-react": "npm:^3.0.0" - "@backstage/plugin-home": "npm:^0.9.6" - "@backstage/plugin-kubernetes": "npm:^0.12.19" - "@backstage/plugin-org": "npm:^0.7.4" - "@backstage/plugin-permission-common": "npm:^0.9.9" - "@backstage/plugin-permission-react": "npm:^0.5.1" - "@backstage/plugin-scaffolder": "npm:^1.37.0" - "@backstage/plugin-scaffolder-common": "npm:^2.2.0" - "@backstage/plugin-scaffolder-react": "npm:^2.0.0" - "@backstage/plugin-search": "npm:^1.7.4" - "@backstage/plugin-search-react": "npm:^1.11.4" - "@backstage/plugin-techdocs": "npm:^1.17.6" - "@backstage/plugin-techdocs-common": "npm:^0.1.1" - "@backstage/plugin-techdocs-module-addons-contrib": "npm:^1.1.36" - "@backstage/plugin-techdocs-react": "npm:^1.3.11" - "@backstage/plugin-user-settings": "npm:^0.9.3" - "@backstage/test-utils": "npm:^1.7.18" - "@backstage/theme": "npm:^0.7.3" "@backstage/ui": "npm:^0.15.0" - "@gitbeaker/rest": "npm:^40.6.0" - "@immobiliarelabs/backstage-plugin-gitlab": "npm:^6.13.0" - "@material-ui/core": "npm:4.12.4" - "@material-ui/icons": "npm:4.11.3" - "@material-ui/lab": "npm:4.0.0-alpha.61" - "@openchoreo/backstage-design-system": "workspace:^" - "@openchoreo/backstage-plugin": "workspace:^" - "@openchoreo/backstage-plugin-common": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-ci": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-observability": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-portal-assistant": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-workflows": "workspace:^" - "@openchoreo/backstage-plugin-platform-engineer-core": "workspace:^" - "@openchoreo/backstage-plugin-react": "workspace:^" + "@openchoreo/backstage-portal-app": "workspace:^" "@playwright/test": "npm:1.56.0" - "@rjsf/core": "npm:5.24.13" - "@rjsf/material-ui": "npm:5.24.13" - "@rjsf/utils": "npm:5.24.13" - "@rjsf/validator-ajv8": "npm:5.24.13" - "@tanstack/react-query": "npm:^5.101.2" "@testing-library/dom": "npm:9.3.4" "@testing-library/jest-dom": "npm:6.9.1" "@testing-library/react": "npm:14.3.1" - "@testing-library/user-event": "npm:14.6.1" "@types/react-dom": "npm:*" - clsx: "npm:^2.1.1" cross-env: "npm:7.0.3" - d3-selection: "npm:^3.0.0" - d3-zoom: "npm:^3.0.0" - devicons-react: "npm:^1.5.0" - json-schema: "npm:0.4.0" react: "npm:18.3.1" react-dom: "npm:18.3.1" - react-router: "npm:6.30.1" - react-router-dom: "npm:6.30.1" - react-use: "npm:17.6.0" - yaml: "npm:^2.8.1" languageName: unknown linkType: soft @@ -17632,47 +17688,19 @@ __metadata: version: 0.0.0-use.local resolution: "backend@workspace:packages/backend" dependencies: - "@backstage-community/plugin-jenkins-backend": "npm:^0.24.1" "@backstage/backend-defaults": "npm:^0.17.1" "@backstage/backend-plugin-api": "npm:^1.9.1" "@backstage/cli": "npm:^0.36.2" "@backstage/config": "npm:^1.3.8" - "@backstage/plugin-app-backend": "npm:^0.5.14" - "@backstage/plugin-auth-backend": "npm:^0.29.0" - "@backstage/plugin-auth-backend-module-github-provider": "npm:^0.5.3" - "@backstage/plugin-auth-backend-module-guest-provider": "npm:^0.2.19" "@backstage/plugin-auth-node": "npm:^0.7.1" - "@backstage/plugin-catalog-backend": "npm:^3.7.0" - "@backstage/plugin-catalog-backend-module-logs": "npm:^0.1.22" - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.2.20" - "@backstage/plugin-events-backend": "npm:^0.6.2" "@backstage/plugin-kubernetes-backend": "npm:^0.21.4" - "@backstage/plugin-permission-backend": "npm:^0.7.12" "@backstage/plugin-permission-common": "npm:^0.9.9" "@backstage/plugin-permission-node": "npm:^0.11.0" - "@backstage/plugin-proxy-backend": "npm:^0.6.13" - "@backstage/plugin-scaffolder-backend": "npm:^4.0.0" - "@backstage/plugin-scaffolder-backend-module-github": "npm:^0.9.9" - "@backstage/plugin-search-backend": "npm:^2.1.2" - "@backstage/plugin-search-backend-module-catalog": "npm:^0.3.15" - "@backstage/plugin-search-backend-module-pg": "npm:^0.5.55" "@backstage/plugin-search-backend-module-techdocs": "npm:^0.4.14" "@backstage/plugin-search-backend-node": "npm:^1.4.4" - "@backstage/plugin-techdocs-backend": "npm:^2.2.0" - "@backstage/plugin-user-settings-backend": "npm:^0.4.3" "@immobiliarelabs/backstage-plugin-gitlab-backend": "npm:^6.13.0" - "@openchoreo/backstage-plugin-auth-backend-module-openchoreo-auth": "workspace:^" - "@openchoreo/backstage-plugin-backend": "workspace:^" - "@openchoreo/backstage-plugin-catalog-backend-module": "workspace:^" - "@openchoreo/backstage-plugin-catalog-backend-module-openchoreo-users": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-ci-backend": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-observability-backend": "workspace:^" "@openchoreo/backstage-plugin-openchoreo-portal-assistant-backend": "workspace:^" - "@openchoreo/backstage-plugin-openchoreo-workflows-backend": "workspace:^" - "@openchoreo/backstage-plugin-permission-backend-module-openchoreo-policy": "workspace:^" - "@openchoreo/backstage-plugin-platform-engineer-core-backend": "workspace:^" - "@openchoreo/backstage-plugin-scaffolder-backend-module": "workspace:^" - "@openchoreo/openchoreo-auth": "workspace:^" + "@openchoreo/backstage-portal-backend": "workspace:^" "@types/cookie-parser": "npm:1.4.8" "@types/express-session": "npm:1.18.2" app: "workspace:^" @@ -32195,7 +32223,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.1, qs@npm:^6.11.0, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.9.4, qs@npm:~6.15.1": +"qs@npm:^6.10.1, qs@npm:^6.11.0, qs@npm:^6.11.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.9.4, qs@npm:~6.15.1": version: 6.15.2 resolution: "qs@npm:6.15.2" dependencies: @@ -32204,6 +32232,16 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.12.2": + version: 6.15.3 + resolution: "qs@npm:6.15.3" + dependencies: + es-define-property: "npm:^1.0.1" + side-channel: "npm:^1.1.1" + checksum: 10c0/8f3f6e45ece255347d57696628401cde29e9ec649fff698b53bd3150dea7cefdf33036e1bc1826b9f110bfa7cb0ec4ab9f5297eca628ce216c55af82c304e08e + languageName: node + linkType: hard + "quansync@npm:^0.2.7": version: 0.2.11 resolution: "quansync@npm:0.2.11" @@ -34789,6 +34827,16 @@ __metadata: languageName: node linkType: hard +"side-channel-list@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-list@npm:1.0.1" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.4" + checksum: 10c0/d346c787fd2f9f1c2fdea14f00e8250118db0e7596d85a6cb9faa75f105d31a73a8f7a341c93d7df2a2429098c3d37a77bd3be9e88c37094b8c01807bc77c7a2 + languageName: node + linkType: hard + "side-channel-map@npm:^1.0.1": version: 1.0.1 resolution: "side-channel-map@npm:1.0.1" @@ -34827,6 +34875,19 @@ __metadata: languageName: node linkType: hard +"side-channel@npm:^1.1.1": + version: 1.1.1 + resolution: "side-channel@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.4" + side-channel-list: "npm:^1.0.1" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10c0/dc0ab81d67f61bda9247d053ce93f41c3fd8ad2bdcb9cf9d8d2f8540d488f26d87a5e99ebfc07eea49ec025867b2452b705442d974b1478f0395e69f6bfb3270 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7"