From 2ad085a38fe72595581ec018c53bbca7cde84c15 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 28 Dec 2023 14:35:49 -0800 Subject: [PATCH] Add Radius pages Signed-off-by: Ryan Nowak --- README.md | 29 +- app-config.local.yaml | 28 + backstage.json | 2 +- package.json | 2 +- packages/app/package.json | 56 +- packages/app/src/App.tsx | 69 +- packages/app/src/components/Root/LogoFull.tsx | 20 - packages/app/src/components/Root/LogoIcon.tsx | 21 - packages/app/src/components/Root/Root.tsx | 28 +- .../app/src/components/catalog/EntityPage.tsx | 5 +- .../app/src/components/home/CommunityCard.tsx | 42 + packages/app/src/components/home/HomePage.tsx | 65 + .../app/src/components/home/LearnCard.tsx | 47 + .../app/src/components/home/SupportCard.tsx | 39 + packages/backend/package.json | 41 +- packages/backend/src/plugins/kubernetes.ts | 1 + packages/rad-components/index.ts | 1 + packages/rad-components/jest.config.json | 5 + packages/rad-components/package.json | 1 + .../src/components/appgraph/AppGraph.tsx | 51 +- .../components/resourcenode/ResourceNode.tsx | 30 +- .../__test__/ResourceNode.test.tsx | 7 +- packages/rad-components/src/index.ts | 1 + plugins/plugin-radius-backend/package.json | 4 +- plugins/plugin-radius/index.ts | 1 + plugins/plugin-radius/package.json | 24 +- plugins/plugin-radius/src/api/api.test.ts | 131 ++ plugins/plugin-radius/src/api/api.ts | 196 +++ plugins/plugin-radius/src/api/index.ts | 1 + .../ApplicationPageComponent.test.tsx | 29 - .../ApplicationPageComponent.tsx | 34 - .../ApplicationPageComponent/index.ts | 1 - .../EnvironmentPageComponent.tsx | 37 - .../EnvironmentPageComponent/index.ts | 1 - .../ExampleFetchComponent.test.tsx | 19 - .../ExampleFetchComponent.tsx | 308 ----- .../components/ExampleFetchComponent/index.ts | 1 - .../applications/ApplicationListPage.test.tsx | 51 + .../applications/ApplicationListPage.tsx | 20 + .../src/components/applications/index.ts | 1 + .../environments/EnvironmentListPage.test.tsx | 47 + .../environments/EnvironmentListPage.tsx | 23 + .../src/components/environments/index.ts | 1 + .../src/components/logo/RadiusLogo.test.tsx | 26 + .../src/components/logo/RadiusLogo.tsx | 145 +++ .../resourcelink/ResourceLink.test.tsx | 59 + .../components/resourcelink/ResourceLink.tsx | 37 + .../src/components/resourcelink/index.ts | 1 + .../components/resources/ApplicationTab.tsx | 97 ++ .../src/components/resources/DetailsTab.tsx | 11 + .../src/components/resources/OverviewTab.tsx | 27 + .../components/resources/ResourceLayout.tsx | 26 + .../components/resources/ResourceListPage.tsx | 17 + .../resources/ResourcePage.test.tsx | 108 ++ .../src/components/resources/ResourcePage.tsx | 60 + .../src/components/resources/ResourcesTab.tsx | 11 + .../src/components/resources/index.ts | 2 + .../resourcetable/ResourceTable.test.tsx | 204 +++ .../resourcetable/ResourceTable.tsx | 97 ++ .../src/components/resourcetable/index.ts | 1 + plugins/plugin-radius/src/index.ts | 9 +- plugins/plugin-radius/src/plugin.ts | 61 +- plugins/plugin-radius/src/resources/index.ts | 8 + .../plugin-radius/src/resources/resource.ts | 21 + .../src/resources/resourceId.test.ts | 22 + .../plugin-radius/src/resources/resourceId.ts | 19 + plugins/plugin-radius/src/routes.ts | 17 + plugins/plugin-radius/src/setupTests.ts | 1 + yarn.lock | 1133 +++++------------ 69 files changed, 2277 insertions(+), 1464 deletions(-) create mode 100644 app-config.local.yaml delete mode 100644 packages/app/src/components/Root/LogoFull.tsx delete mode 100644 packages/app/src/components/Root/LogoIcon.tsx create mode 100644 packages/app/src/components/home/CommunityCard.tsx create mode 100644 packages/app/src/components/home/HomePage.tsx create mode 100644 packages/app/src/components/home/LearnCard.tsx create mode 100644 packages/app/src/components/home/SupportCard.tsx create mode 100644 packages/rad-components/index.ts create mode 100644 plugins/plugin-radius/index.ts create mode 100644 plugins/plugin-radius/src/api/api.test.ts create mode 100644 plugins/plugin-radius/src/api/api.ts create mode 100644 plugins/plugin-radius/src/api/index.ts delete mode 100644 plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.test.tsx delete mode 100644 plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.tsx delete mode 100644 plugins/plugin-radius/src/components/ApplicationPageComponent/index.ts delete mode 100644 plugins/plugin-radius/src/components/EnvironmentPageComponent/EnvironmentPageComponent.tsx delete mode 100644 plugins/plugin-radius/src/components/EnvironmentPageComponent/index.ts delete mode 100644 plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx delete mode 100644 plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx delete mode 100644 plugins/plugin-radius/src/components/ExampleFetchComponent/index.ts create mode 100644 plugins/plugin-radius/src/components/applications/ApplicationListPage.test.tsx create mode 100644 plugins/plugin-radius/src/components/applications/ApplicationListPage.tsx create mode 100644 plugins/plugin-radius/src/components/applications/index.ts create mode 100644 plugins/plugin-radius/src/components/environments/EnvironmentListPage.test.tsx create mode 100644 plugins/plugin-radius/src/components/environments/EnvironmentListPage.tsx create mode 100644 plugins/plugin-radius/src/components/environments/index.ts create mode 100644 plugins/plugin-radius/src/components/logo/RadiusLogo.test.tsx create mode 100644 plugins/plugin-radius/src/components/logo/RadiusLogo.tsx create mode 100644 plugins/plugin-radius/src/components/resourcelink/ResourceLink.test.tsx create mode 100644 plugins/plugin-radius/src/components/resourcelink/ResourceLink.tsx create mode 100644 plugins/plugin-radius/src/components/resourcelink/index.ts create mode 100644 plugins/plugin-radius/src/components/resources/ApplicationTab.tsx create mode 100644 plugins/plugin-radius/src/components/resources/DetailsTab.tsx create mode 100644 plugins/plugin-radius/src/components/resources/OverviewTab.tsx create mode 100644 plugins/plugin-radius/src/components/resources/ResourceLayout.tsx create mode 100644 plugins/plugin-radius/src/components/resources/ResourceListPage.tsx create mode 100644 plugins/plugin-radius/src/components/resources/ResourcePage.test.tsx create mode 100644 plugins/plugin-radius/src/components/resources/ResourcePage.tsx create mode 100644 plugins/plugin-radius/src/components/resources/ResourcesTab.tsx create mode 100644 plugins/plugin-radius/src/components/resources/index.ts create mode 100644 plugins/plugin-radius/src/components/resourcetable/ResourceTable.test.tsx create mode 100644 plugins/plugin-radius/src/components/resourcetable/ResourceTable.tsx create mode 100644 plugins/plugin-radius/src/components/resourcetable/index.ts create mode 100644 plugins/plugin-radius/src/resources/index.ts create mode 100644 plugins/plugin-radius/src/resources/resource.ts create mode 100644 plugins/plugin-radius/src/resources/resourceId.test.ts create mode 100644 plugins/plugin-radius/src/resources/resourceId.ts diff --git a/README.md b/README.md index 1f40c27..74d72c8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,24 @@ Frontend experience for Project Radius This repo uses [corepack](https://nodejs.org/api/corepack.html) and [yarn workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces/). -It is organized as a mono-repo. +It is organized as a mono-repo, and contains the following packages. + +| Package | Path | Description | +| ----------------------- | --------------------------------- | ------------------------------------------------------------------------------- | +| `` | `.` | The root `package.json` for the repo. Used as the entrypoint for most commands. | +| `app` | `./packages/app` | The frontend (React) part of the Dashboard. | +| `backend` | `./packages/backend` | The backend (Node.js) part of the Dashboard. | +| `rad-components` | `./packages/rad-components` | A library of reusable React components for Radius (no dependency on Backstage). | +| `plugin-radius` | `./plugins/plugin-radius` | The Radius frontend (React) plugin for Backstage. | +| `plugin-radius-backend` | `./plugins/plugin-radius-backend` | The Radius backend (Node.js) plugin for Backstage. | + +### Understanding the organization + +Our repo builds three primary outputs: + +- The Radius Dashboard: this is a skinned deployment of Backstage with the layout, and set of plugins optimized for Radius. +- The Radius Backstage plugin: our plugin is available standalone (outside of the Dashboard) so users can add our functionality to Backstage. +- The `rad-components` library: contains reusable UI like the Radius App Graph visualization. This is a separate package so we can use it in other contexts besides Backstage. ## Prerequisites @@ -48,7 +65,15 @@ yarn workspace rad-components run build:all yarn workspace rad-components run lint ``` -## Developing +## Developing: Dashboard + +### Configuration + +The configuration for local development (`yarn dev`) is stored in `app-config.local.yaml`. This file is a set of overrides for development that will be combined with `app-config.yaml`. + +This file is checked in but `.gitignored`'d. Feel free to make changes as needed. + +## Developing: rad-components **Launch Storybook to experiment with rad-components:** diff --git a/app-config.local.yaml b/app-config.local.yaml new file mode 100644 index 0000000..f7e07c6 --- /dev/null +++ b/app-config.local.yaml @@ -0,0 +1,28 @@ +# Backstage override configuration for your local development environment +# +# This will be used with `yarn dev` +# +# The configuration provided here is meant to be used with a local copy of the dashboard +# talking to a local Kubernetes cluster on localhost. The local Kubernetes cluster should +# have Radius installed. +# +# Use `kubectl proxy` to open a proxy to your local Kubernetes cluster. + +# See: https://backstage.io/docs/conf/writing for the file as a whole. + +# See: https://backstage.io/docs/features/kubernetes/configuration/ +kubernetes: + # Configure backstage to support multiple kubernetes clusters, even though + # only one is used here. + serviceLocatorMethod: + type: multiTenant + # Use the local proxy on localhost:8001 to talk to the local Kubernetes cluster. + clusterLocatorMethods: + - type: localKubectlProxy + +backend: + # Allow the backend to make requests to the local Kubernetes cluster. + reading: + allow: + - host: localhost:8001 + - host: 127.0.0.1:8001 diff --git a/backstage.json b/backstage.json index 498b8c1..ef5ca67 100644 --- a/backstage.json +++ b/backstage.json @@ -1,3 +1,3 @@ { - "version": "1.20.0" + "version": "1.21.1" } diff --git a/package.json b/package.json index 1e6c113..0eddd9b 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ ] }, "devDependencies": { - "@backstage/cli": "^0.24.0", + "@backstage/cli": "^0.25.0", "@backstage/e2e-test-utils": "^0.1.0", "@playwright/test": "^1.32.3", "@spotify/prettier-config": "^12.0.0", diff --git a/packages/app/package.json b/packages/app/package.json index 7ea8779..fc78ede 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -14,32 +14,33 @@ "lint": "backstage-cli package lint" }, "dependencies": { - "@backstage/app-defaults": "^1.4.5", + "@backstage/app-defaults": "^1.4.6", "@backstage/catalog-model": "^1.4.3", - "@backstage/cli": "^0.24.0", - "@backstage/core-app-api": "^1.11.1", - "@backstage/core-components": "^0.13.8", - "@backstage/core-plugin-api": "^1.8.0", - "@backstage/integration-react": "^1.1.21", - "@backstage/plugin-api-docs": "^0.10.0", - "@backstage/plugin-catalog": "^1.15.0", - "@backstage/plugin-catalog-common": "^1.0.18", - "@backstage/plugin-catalog-graph": "^0.3.0", - "@backstage/plugin-catalog-import": "^0.10.2", - "@backstage/plugin-catalog-react": "^1.9.0", - "@backstage/plugin-github-actions": "^0.6.7", - "@backstage/plugin-kubernetes": "^0.11.2", - "@backstage/plugin-org": "^0.6.16", - "@backstage/plugin-permission-react": "^0.4.17", - "@backstage/plugin-scaffolder": "^1.16.0", - "@backstage/plugin-search": "^1.4.2", - "@backstage/plugin-search-react": "^1.7.2", - "@backstage/plugin-tech-radar": "^0.6.10", - "@backstage/plugin-techdocs": "^1.9.0", - "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.2", - "@backstage/plugin-techdocs-react": "^1.1.13", - "@backstage/plugin-user-settings": "^0.7.12", - "@backstage/theme": "^0.4.4", + "@backstage/cli": "^0.25.0", + "@backstage/core-app-api": "^1.11.2", + "@backstage/core-components": "^0.13.9", + "@backstage/core-plugin-api": "^1.8.1", + "@backstage/integration-react": "^1.1.22", + "@backstage/plugin-api-docs": "^0.10.2", + "@backstage/plugin-catalog": "^1.16.0", + "@backstage/plugin-catalog-common": "^1.0.19", + "@backstage/plugin-catalog-graph": "^0.3.2", + "@backstage/plugin-catalog-import": "^0.10.4", + "@backstage/plugin-catalog-react": "^1.9.2", + "@backstage/plugin-github-actions": "^0.6.9", + "@backstage/plugin-home": "^0.6.0", + "@backstage/plugin-kubernetes": "^0.11.3", + "@backstage/plugin-org": "^0.6.18", + "@backstage/plugin-permission-react": "^0.4.18", + "@backstage/plugin-scaffolder": "^1.17.0", + "@backstage/plugin-search": "^1.4.4", + "@backstage/plugin-search-react": "^1.7.4", + "@backstage/plugin-tech-radar": "^0.6.11", + "@backstage/plugin-techdocs": "^1.9.2", + "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.3", + "@backstage/plugin-techdocs-react": "^1.1.14", + "@backstage/plugin-user-settings": "^0.7.14", + "@backstage/theme": "^0.5.0", "@internal/plugin-radius": "^0.1.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -52,13 +53,14 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/test-utils": "^1.4.5", + "@backstage/test-utils": "^1.4.6", "@playwright/test": "^1.32.3", "@testing-library/dom": "^8.0.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", - "@types/react-dom": "*", + "@types/react": "^17", + "@types/react-dom": "^17", "cross-env": "^7.0.0" }, "browserslist": { diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index fb191d4..57bc3f8 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -1,39 +1,34 @@ import React from 'react'; -import { Navigate, Route } from 'react-router-dom'; -import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs'; +import { Route } from 'react-router-dom'; +import { apiDocsPlugin } from '@backstage/plugin-api-docs'; import { CatalogEntityPage, CatalogIndexPage, catalogPlugin, } from '@backstage/plugin-catalog'; -import { - CatalogImportPage, - catalogImportPlugin, -} from '@backstage/plugin-catalog-import'; -import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { catalogImportPlugin } from '@backstage/plugin-catalog-import'; +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { orgPlugin } from '@backstage/plugin-org'; import { SearchPage } from '@backstage/plugin-search'; -import { TechRadarPage } from '@backstage/plugin-tech-radar'; -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 { techdocsPlugin } from '@backstage/plugin-techdocs'; import { UserSettingsPage } from '@backstage/plugin-user-settings'; import { apis } from './apis'; import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; +import { HomepageCompositionRoot } from '@backstage/plugin-home'; import { Root } from './components/Root'; import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; import { createApp } from '@backstage/app-defaults'; import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; -import { RequirePermission } from '@backstage/plugin-permission-react'; -import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; -import { ApplicationPage, EnvironmentPage } from '@internal/plugin-radius'; +import { + ApplicationListPage, + EnvironmentListPage, + ResourceListPage, + ResourcePage, + radiusPlugin, +} from '@internal/plugin-radius'; import { UnifiedThemeProvider, createBaseThemeOptions, @@ -42,6 +37,7 @@ import { palettes, shapes, } from '@backstage/theme'; +import { HomePage } from './components/home/HomePage'; const lightTheme = createUnifiedTheme({ ...createBaseThemeOptions({ @@ -95,6 +91,7 @@ const app = createApp({ }, ], bindRoutes({ bind }) { + bind(radiusPlugin.externalRoutes, {}); bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, viewTechDoc: techdocsPlugin.routes.docRoot, @@ -115,7 +112,9 @@ const app = createApp({ const routes = ( - } /> + }> + + } /> {entityPage} - } /> - } - > - - - - - } /> - } /> - } - /> - - - - } - /> }> {searchPage} } /> } /> - } /> - } /> + } /> + } /> + } /> + } + /> ); diff --git a/packages/app/src/components/Root/LogoFull.tsx b/packages/app/src/components/Root/LogoFull.tsx deleted file mode 100644 index 5a1ec2d..0000000 --- a/packages/app/src/components/Root/LogoFull.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core'; - -const useStyles = makeStyles({ - svg: { - width: 'auto', - height: 30, - }, -}); -const LogoFull = () => { - const classes = useStyles(); - - return ( - - - - ); -}; - -export default LogoFull; diff --git a/packages/app/src/components/Root/LogoIcon.tsx b/packages/app/src/components/Root/LogoIcon.tsx deleted file mode 100644 index 2db4d7c..0000000 --- a/packages/app/src/components/Root/LogoIcon.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core'; - -const useStyles = makeStyles({ - svg: { - width: 'auto', - height: 28, - }, -}); - -const LogoIcon = () => { - const classes = useStyles(); - - return ( - - - - ); -}; - -export default LogoIcon; diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 2ee58e0..09368b5 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -4,11 +4,7 @@ import HomeIcon from '@material-ui/icons/Home'; import ExtensionIcon from '@material-ui/icons/Extension'; import PublicIcon from '@material-ui/icons/Public'; import ListAltIcon from '@material-ui/icons/ListAlt'; -import MapIcon from '@material-ui/icons/MyLocation'; -import LibraryBooks from '@material-ui/icons/LibraryBooks'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import LogoFull from './LogoFull'; -import LogoIcon from './LogoIcon'; +import AssessmentIcon from '@material-ui/icons/Assessment'; import { Settings as SidebarSettings, UserSettingsSignInAvatar, @@ -28,6 +24,7 @@ import { } from '@backstage/core-components'; import MenuIcon from '@material-ui/icons/Menu'; import SearchIcon from '@material-ui/icons/Search'; +import { RadiusLogo } from '@internal/plugin-radius'; const useSidebarLogoStyles = makeStyles({ root: { @@ -42,6 +39,10 @@ const useSidebarLogoStyles = makeStyles({ width: sidebarConfig.drawerWidthClosed, marginLeft: 24, }, + svg: { + width: 'auto', + height: 28, + }, }); const SidebarLogo = () => { @@ -51,7 +52,11 @@ const SidebarLogo = () => { return (
- {isOpen ? : } + {isOpen ? ( + + ) : ( + + )}
); @@ -67,17 +72,14 @@ export const Root = ({ children }: PropsWithChildren>) => ( }> {/* Global nav, not org-specific */} - + - - - + + {/* End global nav */} - - - + diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index bb7031e..c7f5581 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -305,9 +305,12 @@ const groupPage = ( - + + + + diff --git a/packages/app/src/components/home/CommunityCard.tsx b/packages/app/src/components/home/CommunityCard.tsx new file mode 100644 index 0000000..0f54561 --- /dev/null +++ b/packages/app/src/components/home/CommunityCard.tsx @@ -0,0 +1,42 @@ +import { InfoCard, LinkButton } from '@backstage/core-components'; +import { CardActions, Typography } from '@material-ui/core'; + +import React from 'react'; + +const actions = () => ( + + + Visit on Github + + + Good first issues + + + Join us on Discord + + +); + +export const CommunityCard = () => ( + + + We welcome and encourage users to contribute to the Radius open-source + project in various ways. By joining our community, you can make a + meaningful impact and help shape the future of this project. + + +); + +export default CommunityCard; diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx new file mode 100644 index 0000000..e54fbe0 --- /dev/null +++ b/packages/app/src/components/home/HomePage.tsx @@ -0,0 +1,65 @@ +import { HomePageCompanyLogo } from '@backstage/plugin-home'; +import { Grid, makeStyles } from '@material-ui/core'; +import React from 'react'; +import { SearchContextProvider } from '@backstage/plugin-search-react'; +import { Content, InfoCard, Page } from '@backstage/core-components'; +import { RadiusLogo } from '@internal/plugin-radius'; +import LearnCard from './LearnCard'; +import CommunityCard from './CommunityCard'; +import SupportCard from './SupportCard'; + +const useLogoStyles = makeStyles(theme => ({ + container: { + margin: theme.spacing(5, 0), + }, + svg: { + width: 'auto', + height: 100, + }, +})); + +export const HomePage = () => { + const { container, svg } = useLogoStyles(); + + return ( + + + + + } + /> + + + + + + + + + + + + + + + Applications are great. + {/* placeholder for content */} +
+ + + + + You're going to need some enviornments. Trust us. + {/* placeholder for content */} +
+ + + + + + + + ); +}; diff --git a/packages/app/src/components/home/LearnCard.tsx b/packages/app/src/components/home/LearnCard.tsx new file mode 100644 index 0000000..4338c1e --- /dev/null +++ b/packages/app/src/components/home/LearnCard.tsx @@ -0,0 +1,47 @@ +import { InfoCard, LinkButton } from '@backstage/core-components'; +import { CardActions, Typography } from '@material-ui/core'; + +import React from 'react'; + +const actions = () => ( + + + Get Started + + + Tutorials + + + Reference + + +); + +export const LearnCard = () => ( + + + Radius is an open-source, cloud-native, application platform that enables + developers and the operators that support them to define, deploy, and + collaborate on cloud-native applications across public clouds and private + infrastructure + + +); + +export default LearnCard; diff --git a/packages/app/src/components/home/SupportCard.tsx b/packages/app/src/components/home/SupportCard.tsx new file mode 100644 index 0000000..f399a97 --- /dev/null +++ b/packages/app/src/components/home/SupportCard.tsx @@ -0,0 +1,39 @@ +import { InfoCard, LinkButton } from '@backstage/core-components'; +import { CardActions, Typography } from '@material-ui/core'; + +import React from 'react'; + +const actions = () => ( + + + Ask a Question + + + Report an Issue + + +); + +export const SupportCard = () => ( + + + Participate in discussions, forums, and chat channels related to Radius. + Seek guidance, offer help to others, and build connections within the + community. + + +); + +export default SupportCard; diff --git a/packages/backend/package.json b/packages/backend/package.json index 4790837..6ccade7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -16,27 +16,28 @@ "build-image": "docker build ../.. -f Dockerfile --tag backstage" }, "dependencies": { - "@backstage/backend-common": "^0.19.9", - "@backstage/backend-tasks": "^0.5.12", - "@backstage/catalog-client": "^1.4.6", + "@backstage/backend-common": "^0.20.0", + "@backstage/backend-tasks": "^0.5.13", + "@backstage/catalog-client": "^1.5.1", "@backstage/catalog-model": "^1.4.3", "@backstage/config": "^1.1.1", - "@backstage/plugin-app-backend": "^0.3.55", - "@backstage/plugin-auth-backend": "^0.20.0", - "@backstage/plugin-auth-node": "^0.4.1", - "@backstage/plugin-catalog-backend": "^1.15.0", - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.1.4", - "@backstage/plugin-kubernetes-backend": "^0.13.1", - "@backstage/plugin-permission-common": "^0.7.10", - "@backstage/plugin-permission-node": "^0.7.18", - "@backstage/plugin-proxy-backend": "^0.4.5", - "@backstage/plugin-scaffolder-backend": "^1.19.0", - "@backstage/plugin-search-backend": "^1.4.7", - "@backstage/plugin-search-backend-module-catalog": "^0.1.11", - "@backstage/plugin-search-backend-module-pg": "^0.5.16", - "@backstage/plugin-search-backend-module-techdocs": "^0.1.11", - "@backstage/plugin-search-backend-node": "^1.2.11", - "@backstage/plugin-techdocs-backend": "^1.9.0", + "@backstage/plugin-app-backend": "^0.3.56", + "@backstage/plugin-auth-backend": "^0.20.2", + "@backstage/plugin-auth-node": "^0.4.2", + "@backstage/plugin-catalog-backend": "^1.16.0", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.1.5", + "@backstage/plugin-kubernetes-backend": "^0.14.0", + "@backstage/plugin-kubernetes-node": "^0.1.2", + "@backstage/plugin-permission-common": "^0.7.11", + "@backstage/plugin-permission-node": "^0.7.19", + "@backstage/plugin-proxy-backend": "^0.4.6", + "@backstage/plugin-scaffolder-backend": "^1.19.2", + "@backstage/plugin-search-backend": "^1.4.8", + "@backstage/plugin-search-backend-module-catalog": "^0.1.12", + "@backstage/plugin-search-backend-module-pg": "^0.5.17", + "@backstage/plugin-search-backend-module-techdocs": "^0.1.12", + "@backstage/plugin-search-backend-node": "^1.2.12", + "@backstage/plugin-techdocs-backend": "^1.9.1", "@internal/plugin-radius-backend": "^0.1.0", "app": "^0.1.0", "better-sqlite3": "^9.0.0", @@ -48,7 +49,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.24.0", + "@backstage/cli": "^0.25.0", "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts index dd90bdf..32c1c12 100644 --- a/packages/backend/src/plugins/kubernetes.ts +++ b/packages/backend/src/plugins/kubernetes.ts @@ -13,5 +13,6 @@ export default async function createPlugin( catalogApi, permissions: env.permissions, }).build(); + return router; } diff --git a/packages/rad-components/index.ts b/packages/rad-components/index.ts new file mode 100644 index 0000000..8420b10 --- /dev/null +++ b/packages/rad-components/index.ts @@ -0,0 +1 @@ +export * from './src'; diff --git a/packages/rad-components/jest.config.json b/packages/rad-components/jest.config.json index 348e330..7a6d493 100644 --- a/packages/rad-components/jest.config.json +++ b/packages/rad-components/jest.config.json @@ -1,4 +1,9 @@ { + "coveragePathIgnorePatterns": [ + "/node_modules/", + "/.storybook/", + "/**/__docs__/*" + ], "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", "\\.(css|less)$": "identity-obj-proxy" diff --git a/packages/rad-components/package.json b/packages/rad-components/package.json index a6c4231..e052a53 100644 --- a/packages/rad-components/package.json +++ b/packages/rad-components/package.json @@ -24,6 +24,7 @@ "author": "", "license": "ISC", "devDependencies": { + "@babel/core": "^7.23.0", "@babel/preset-env": "^7.23.6", "@babel/preset-react": "^7.23.3", "@backstage/cli": "^0.25.0", diff --git a/packages/rad-components/src/components/appgraph/AppGraph.tsx b/packages/rad-components/src/components/appgraph/AppGraph.tsx index 7c9b3fd..e93e53f 100644 --- a/packages/rad-components/src/components/appgraph/AppGraph.tsx +++ b/packages/rad-components/src/components/appgraph/AppGraph.tsx @@ -14,13 +14,9 @@ import { ResourceNode } from '../resourcenode/index'; import 'reactflow/dist/style.css'; -export type AppGraphProps = { - graph: AppGraphData; -}; - const nodeTypes = { default: ResourceNode }; -const LayoutFlow = (props: AppGraphProps) => { +const LayoutFlow = (props: { graph: AppGraphData }) => { const initial = initialNodes(props.graph); const { fitView } = useReactFlow(); @@ -41,10 +37,17 @@ const LayoutFlow = (props: AppGraphProps) => { [nodes, edges], ); + // Notes on our usage of ReactFlow: + // + // - We're using an uncontrolled flow: https://reactflow.dev/learn/advanced-use/uncontrolled-flow + // - We implemented a custom node type: https://reactflow.dev/learn/customization/custom-nodes + // - We're using Dagre for layout: https://reactflow.dev/learn/layouting/layouting#dagre + return ( { ); }; -function AppGraph(props: AppGraphProps) { +function AppGraph(props: { graph: AppGraphData }) { return ( -
+
@@ -70,11 +73,27 @@ function initialNodes(graph: AppGraphData): { const nodes: Node[] = []; const edges: Edge[] = []; - let i = 0; + // Very simple layout scheme here for nodes. + const orderData: { [order: number]: number } = {}; + for (const resource of graph.resources) { + // The computed 'Order' is used to compute the 'y' coordinate for the initial layout. + // This is computed based on the number of inbound connections (or whether it's a container). + const order = + resource.connections?.filter(c => c.direction === 'Inbound').length || + resource.type === 'Applications.Core/containers' + ? 0 + : 3; + + // The computed 'Rank' is used to compute the 'x' coordinate for the initial layout. + // This is computed based on the number of resources at the same 'Order'. + const rank = (orderData[order] = (orderData[order] || 0) + 1); + + // This provides the initial bias for the layout. Dagre will adjust this. + nodes.push({ id: resource.id, - position: { x: 0, y: 200 * i++ }, + position: { x: rank * 50, y: order * 50 }, data: resource, type: 'default', }); @@ -84,16 +103,14 @@ function initialNodes(graph: AppGraphData): { if (connection.direction === 'Inbound') { edges.push({ id: `${connection.id}-${resource.id}`, - type: 'smoothstep', - source: connection.id, - target: resource.id, + source: resource.id, + target: connection.id, }); } else { edges.push({ id: `${resource.id}-${connection.id}`, - type: 'smoothstep', - source: resource.id, - target: connection.id, + source: connection.id, + target: resource.id, }); } } diff --git a/packages/rad-components/src/components/resourcenode/ResourceNode.tsx b/packages/rad-components/src/components/resourcenode/ResourceNode.tsx index 504dc2c..a5e8ea2 100644 --- a/packages/rad-components/src/components/resourcenode/ResourceNode.tsx +++ b/packages/rad-components/src/components/resourcenode/ResourceNode.tsx @@ -1,21 +1,23 @@ -import React, { PropsWithChildren } from 'react'; +import React from 'react'; import { Resource } from '../../graph'; +import { Handle, NodeProps, Position } from 'reactflow'; -export interface ResourceNodeProps { - data: Resource; -} +// Note: the default style assigned to a node gives it a 150px width +// from style: .react-flow__node-default. +// +// We're setting a f -function ResourceNode(props: PropsWithChildren) { +function ResourceNode(props: Pick, 'data'>) { return ( -
-

{props.data.name}

-
-
{props.data.type}
-
asdsfdsdfafdfdff
-
+ <> + +
+

{props.data.name}

+
+
{props.data.type}
+
+ + ); } diff --git a/packages/rad-components/src/components/resourcenode/__test__/ResourceNode.test.tsx b/packages/rad-components/src/components/resourcenode/__test__/ResourceNode.test.tsx index 8cdc860..fb3846c 100644 --- a/packages/rad-components/src/components/resourcenode/__test__/ResourceNode.test.tsx +++ b/packages/rad-components/src/components/resourcenode/__test__/ResourceNode.test.tsx @@ -3,11 +3,16 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import ResourceNode from '../ResourceNode'; import * as sampledata from '../../../sampledata'; +import { ReactFlowProvider } from 'reactflow'; describe('ResourceNode component', () => { it('ResourceNode should render correctly', () => { const resource = sampledata.ContainerResource; - render(); + render( + + + , + ); const name = screen.getByRole('heading', { name: resource.name }); expect(name).toBeInTheDocument(); const type = screen.getByRole('heading', { name: resource.type }); diff --git a/packages/rad-components/src/index.ts b/packages/rad-components/src/index.ts index 07635cb..5973eaf 100644 --- a/packages/rad-components/src/index.ts +++ b/packages/rad-components/src/index.ts @@ -1 +1,2 @@ export * from './components'; +export type { AppGraph as AppGraphData } from './graph'; diff --git a/plugins/plugin-radius-backend/package.json b/plugins/plugin-radius-backend/package.json index 0093b06..54cdb54 100644 --- a/plugins/plugin-radius-backend/package.json +++ b/plugins/plugin-radius-backend/package.json @@ -24,7 +24,7 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.19.9", + "@backstage/backend-common": "^0.20.0", "@backstage/catalog-model": "^1.4.3", "@backstage/config": "^1.1.1", "@backstage/plugin-catalog-common": "^1.0.19", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.24.0", + "@backstage/cli": "^0.25.0", "@types/supertest": "^2.0.12", "msw": "^1.0.0", "supertest": "^6.2.4" diff --git a/plugins/plugin-radius/index.ts b/plugins/plugin-radius/index.ts new file mode 100644 index 0000000..8420b10 --- /dev/null +++ b/plugins/plugin-radius/index.ts @@ -0,0 +1 @@ +export * from './src'; diff --git a/plugins/plugin-radius/package.json b/plugins/plugin-radius/package.json index 9029c90..0bc8b50 100644 --- a/plugins/plugin-radius/package.json +++ b/plugins/plugin-radius/package.json @@ -24,25 +24,33 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.13.8", - "@backstage/core-plugin-api": "^1.8.0", - "@backstage/theme": "^0.4.4", + "@backstage/core-components": "^0.13.9", + "@backstage/core-plugin-api": "^1.8.1", + "@backstage/plugin-kubernetes": "^0.11.3", + "@backstage/theme": "^0.5.0", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.61", + "rad-components": "^0.0.1", + "react-error-boundary": "^4.0.12", "react-use": "^17.2.4" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0" + "react": "^16.13.1 || ^17.0.0", + "react-dom": "^16.13.1 || ^17.0.0", + "react-router-dom": "^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.24.0", - "@backstage/core-app-api": "^1.11.1", - "@backstage/dev-utils": "^1.0.23", - "@backstage/test-utils": "^1.4.5", + "@backstage/cli": "^0.25.0", + "@backstage/core-app-api": "^1.11.2", + "@backstage/dev-utils": "^1.0.25", + "@backstage/plugin-kubernetes-common": "^0.7.2", + "@backstage/test-utils": "^1.4.6", + "@testing-library/dom": "^8.0.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", + "jest-canvas-mock": "^2.5.2", "msw": "^1.0.0" }, "files": [ diff --git a/plugins/plugin-radius/src/api/api.test.ts b/plugins/plugin-radius/src/api/api.test.ts new file mode 100644 index 0000000..cb39457 --- /dev/null +++ b/plugins/plugin-radius/src/api/api.test.ts @@ -0,0 +1,131 @@ +import { makePath, makePathForId, RadiusApiImpl } from './api'; + +describe('makePath', () => { + it('makes path for scopes', () => { + const path = makePath({ scopes: [{ type: 'radius', value: 'local' }] }); + expect(path).toEqual(makePathForId('/planes/radius/local')); + }); + it('makes path for multiple scopes', () => { + const path = makePath({ + scopes: [ + { type: 'radius', value: 'local' }, + { + type: 'resourceGroups', + value: 'test-group', + }, + ], + }); + expect(path).toEqual( + makePathForId('/planes/radius/local/resourceGroups/test-group'), + ); + }); + it('makes path for scope list', () => { + const path = makePath({ + scopes: [ + { type: 'radius', value: 'local' }, + { + type: 'resourceGroups', + }, + ], + }); + expect(path).toEqual(makePathForId('/planes/radius/local/resourceGroups')); + }); + it('makes path for scope action', () => { + const path = makePath({ + scopes: [{ type: 'radius', value: 'local' }], + action: 'action', + }); + expect(path).toEqual(makePathForId('/planes/radius/local/action')); + }); + it('makes path for resource type without name', () => { + const path = makePath({ + scopes: [{ type: 'radius', value: 'local' }], + type: 'Applications.Core/applications', + }); + expect(path).toEqual( + makePathForId( + '/planes/radius/local/providers/Applications.Core/applications', + ), + ); + }); + it('makes path for resource type with name', () => { + const path = makePath({ + scopes: [{ type: 'radius', value: 'local' }], + type: 'Applications.Core/applications', + name: 'test-app', + }); + expect(path).toEqual( + makePathForId( + '/planes/radius/local/providers/Applications.Core/applications/test-app', + ), + ); + }); + it('makes path for resource type with name and action', () => { + const path = makePath({ + scopes: [{ type: 'radius', value: 'local' }], + type: 'Applications.Core/applications', + name: 'test-app', + action: 'restart', + }); + expect(path).toEqual( + makePathForId( + '/planes/radius/local/providers/Applications.Core/applications/test-app/restart', + ), + ); + }); +}); + +describe('RadiusApi', () => { + it('selectCluster returns first cluster', async () => { + const api = new RadiusApiImpl({ + getClusters: async () => [ + { name: 'test-cluster1', authProvider: 'test' }, + { + name: 'test-cluster2', + authProvider: 'test', + }, + ], + proxy: async () => { + throw new Error('not implemented'); + }, + }); + // eslint-disable-next-line dot-notation + expect(await api['selectCluster']()).toEqual('test-cluster1'); + }); + it('makeRequest handles errors', async () => { + const api = new RadiusApiImpl({ + getClusters: async () => { + throw new Error('not implemented'); + }, + proxy: async () => Promise.resolve(new Response('test', { status: 404 })), + }); + // eslint-disable-next-line dot-notation + await expect(api['makeRequest']('cluster', 'path')).rejects.toThrow( + 'Request failed: 404:\n\ntest', + ); + }); + it('makeRequest expects JSON', async () => { + const api = new RadiusApiImpl({ + getClusters: async () => { + throw new Error('not implemented'); + }, + proxy: async () => Promise.resolve(new Response('test')), + }); + // eslint-disable-next-line dot-notation + await expect(api['makeRequest']('cluster', 'path')).rejects.toThrow( + 'invalid json response body at reason: Unexpected token \'e\', "test" is not valid JSON', + ); + }); + it('makeRequest parses JSON', async () => { + const api = new RadiusApiImpl({ + getClusters: async () => { + throw new Error('not implemented'); + }, + proxy: async () => Promise.resolve(new Response('{ "message": "test" }')), + }); + // eslint-disable-next-line dot-notation + await expect(api['makeRequest']('cluster', 'path')).resolves.toEqual({ + message: 'test', + }); + }); +}); diff --git a/plugins/plugin-radius/src/api/api.ts b/plugins/plugin-radius/src/api/api.ts new file mode 100644 index 0000000..fe5beee --- /dev/null +++ b/plugins/plugin-radius/src/api/api.ts @@ -0,0 +1,196 @@ +import { KubernetesApi } from '@backstage/plugin-kubernetes'; +import { + ApplicationProperties, + EnvironmentProperties, + Resource, + ResourceList, +} from '../resources'; + +export interface RadiusApi { + getResourceById(opts: { + id: string; + }): Promise>; + listApplications(opts?: { + resourceGroup?: string; + }): Promise>; + listEnvironments(opts?: { + resourceGroup?: string; + }): Promise>; + + listResources(opts?: { + resourceType?: string; + resourceGroup?: string; + }): Promise>; +} + +const pathPrefix = '/apis/api.ucp.dev/v1alpha3'; +const apiVersion = '?api-version=2023-10-01-preview'; + +export const makePathForId = (id: string) => { + return `${pathPrefix}${id}${apiVersion}`; +}; + +export const makePath = ({ + scopes, + type, + name, + action, +}: { + scopes: { type: string; value?: string }[]; + type?: string; + name?: string; + action?: string; +}) => { + const scopePart = scopes + .map(s => { + if (s.value) { + return `${s.type}/${s.value}`; + } + + return s.type; + }) + .join('/'); + const typePart = type ? `/providers/${type}` : ''; + const namePart = name ? `/${name}` : ''; + const actionPart = action ? `/${action}` : ''; + const id = `/planes/${scopePart}${typePart}${namePart}${actionPart}`; + return makePathForId(id); +}; + +export class RadiusApiImpl implements RadiusApi { + constructor( + private readonly kubernetesApi: Pick< + KubernetesApi, + 'getClusters' | 'proxy' + >, + ) {} + + async listResources( + opts?: { resourceType?: string; resourceGroup?: string } | undefined, + ): Promise> { + const cluster = await this.selectCluster(); + + // Fast path for listing resources of a specific type. + if (opts?.resourceType) { + const path = makePath({ + scopes: this.makeScopes(opts), + type: opts.resourceType, + }); + return this.makeRequest>(cluster, path); + } + + // no way to list resources in all groups yet :-/. Let's do the O(n) thing for now. + const groups = await this.makeRequest>>( + cluster, + makePath({ + scopes: [ + { type: 'radius', value: 'local' }, + { + type: 'resourceGroups', + }, + ], + }), + ); + const resources: Resource[] = []; + for (const group of groups.value) { + // Unfortunately we don't include all of the relevant properties in tracked resources. + // This is inefficient, but we'll have to do it for now. + const path = makePath({ + scopes: [ + { type: 'radius', value: 'local' }, + { + type: 'resourceGroups', + value: group.name, + }, + ], + action: 'resources', + }); + const groupResources = await this.makeRequest< + ResourceList> + >(cluster, path); + for (const resource of groupResources.value) { + // Deployments show up in tracked resources, but the RP may not hold onto them. + // There's limited value here so skip them for now. + if (resource.type === 'Microsoft.Resources/deployments') { + continue; + } + + resources.push(await this.getResourceById({ id: resource.id })); + } + } + return { value: resources }; + } + + async getResourceById(opts: { + id: string; + }): Promise> { + const cluster = await this.selectCluster(); + + const path = makePathForId(opts.id); + return this.makeRequest>(cluster, path); + } + + async listApplications(opts?: { + resourceGroup?: string; + }): Promise> { + const cluster = await this.selectCluster(); + + const path = makePath({ + scopes: this.makeScopes(opts), + type: 'Applications.Core/applications', + }); + return this.makeRequest>(cluster, path); + } + + async listEnvironments(opts?: { + resourceGroup?: string; + }): Promise> { + const cluster = await this.selectCluster(); + + const path = makePath({ + scopes: this.makeScopes(opts), + type: 'Applications.Core/environments', + }); + return this.makeRequest>(cluster, path); + } + + private async selectCluster(): Promise { + const clusters = await this.kubernetesApi.getClusters(); + for (const cluster of clusters) { + return cluster.name; + } + + throw new Error('No kubernetes clusters found'); + } + + private makeScopes(opts?: { + resourceGroup?: string; + }): { type: string; value?: string }[] { + const scopes = [{ type: 'radius', value: 'local' }]; + if (opts?.resourceGroup) { + scopes.push({ type: 'resourceGroups', value: opts.resourceGroup }); + } + return scopes; + } + + private async makeRequest(cluster: string, path: string): Promise { + const response = await this.kubernetesApi.proxy({ + clusterName: cluster, + path: path, + init: { + referrerPolicy: 'no-referrer', // See https://github.com/radius-project/radius/issues/6983 + mode: 'cors', + cache: 'no-cache', + method: 'GET', + }, + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`Request failed: ${response.status}:\n\n${text}`); + } + + const data = (await response.json()) as T; + return data; + } +} diff --git a/plugins/plugin-radius/src/api/index.ts b/plugins/plugin-radius/src/api/index.ts new file mode 100644 index 0000000..05a54d5 --- /dev/null +++ b/plugins/plugin-radius/src/api/index.ts @@ -0,0 +1 @@ +export type { RadiusApi } from './api'; diff --git a/plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.test.tsx b/plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.test.tsx deleted file mode 100644 index 18ba92d..0000000 --- a/plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.test.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { ApplicationPageComponent } from './ApplicationPageComponent'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { screen } from '@testing-library/react'; -import { - setupRequestMockHandlers, - renderInTestApp, -} from '@backstage/test-utils'; - -describe('ApplicationPageComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - setupRequestMockHandlers(server); - - // setup mock response - beforeEach(() => { - server.use( - rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), - ); - }); - - it('should render', async () => { - await renderInTestApp(); - expect( - screen.getByText('Displaying deployed applications.'), - ).toBeInTheDocument(); - }); -}); diff --git a/plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.tsx b/plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.tsx deleted file mode 100644 index eafed5d..0000000 --- a/plugins/plugin-radius/src/components/ApplicationPageComponent/ApplicationPageComponent.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - Content, - ContentHeader, - SupportButton, -} from '@backstage/core-components'; -import { ExampleFetchComponent } from '../ExampleFetchComponent'; - -export const ApplicationPageComponent = () => ( - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - - -); diff --git a/plugins/plugin-radius/src/components/ApplicationPageComponent/index.ts b/plugins/plugin-radius/src/components/ApplicationPageComponent/index.ts deleted file mode 100644 index eff90e0..0000000 --- a/plugins/plugin-radius/src/components/ApplicationPageComponent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ApplicationPageComponent } from './ApplicationPageComponent'; diff --git a/plugins/plugin-radius/src/components/EnvironmentPageComponent/EnvironmentPageComponent.tsx b/plugins/plugin-radius/src/components/EnvironmentPageComponent/EnvironmentPageComponent.tsx deleted file mode 100644 index 55ad04a..0000000 --- a/plugins/plugin-radius/src/components/EnvironmentPageComponent/EnvironmentPageComponent.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - Content, - ContentHeader, - SupportButton, -} from '@backstage/core-components'; -import { ExampleFetchComponent } from '../ExampleFetchComponent'; - -export const EnvironmentPageComponent = () => ( - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - - -); diff --git a/plugins/plugin-radius/src/components/EnvironmentPageComponent/index.ts b/plugins/plugin-radius/src/components/EnvironmentPageComponent/index.ts deleted file mode 100644 index 301c4ef..0000000 --- a/plugins/plugin-radius/src/components/EnvironmentPageComponent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { EnvironmentPageComponent } from './EnvironmentPageComponent'; diff --git a/plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx deleted file mode 100644 index 1e746ff..0000000 --- a/plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { ExampleFetchComponent } from './ExampleFetchComponent'; - -describe('ExampleFetchComponent', () => { - it('renders the user table', async () => { - render(); - - // Wait for the table to render - const table = await screen.findByRole('table'); - const nationality = screen.getAllByText('GB'); - // Assert that the table contains the expected user data - expect(table).toBeInTheDocument(); - expect(screen.getByAltText('Carolyn')).toBeInTheDocument(); - expect(screen.getByText('Carolyn Moore')).toBeInTheDocument(); - expect(screen.getByText('carolyn.moore@example.com')).toBeInTheDocument(); - expect(nationality[0]).toBeInTheDocument(); - }); -}); diff --git a/plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index 88e0652..0000000 --- a/plugins/plugin-radius/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import { - Table, - TableColumn, - Progress, - ResponseErrorPanel, -} from '@backstage/core-components'; -import useAsync from 'react-use/lib/useAsync'; - -export const exampleUsers = { - results: [ - { - gender: 'female', - name: { - title: 'Miss', - first: 'Carolyn', - last: 'Moore', - }, - email: 'carolyn.moore@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Carolyn', - nat: 'GB', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Esma', - last: 'Berberoğlu', - }, - email: 'esma.berberoglu@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Esma', - nat: 'TR', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Isabella', - last: 'Rhodes', - }, - email: 'isabella.rhodes@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella', - nat: 'GB', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Derrick', - last: 'Carter', - }, - email: 'derrick.carter@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Derrick', - nat: 'IE', - }, - { - gender: 'female', - name: { - title: 'Miss', - first: 'Mattie', - last: 'Lambert', - }, - email: 'mattie.lambert@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mattie', - nat: 'AU', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Mijat', - last: 'Rakić', - }, - email: 'mijat.rakic@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mijat', - nat: 'RS', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Javier', - last: 'Reid', - }, - email: 'javier.reid@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Javier', - nat: 'US', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Isabella', - last: 'Li', - }, - email: 'isabella.li@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella', - nat: 'CA', - }, - { - gender: 'female', - name: { - title: 'Mrs', - first: 'Stephanie', - last: 'Garrett', - }, - email: 'stephanie.garrett@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Stephanie', - nat: 'AU', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Antonia', - last: 'Núñez', - }, - email: 'antonia.nunez@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Antonia', - nat: 'ES', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Donald', - last: 'Young', - }, - email: 'donald.young@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Donald', - nat: 'US', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Iegor', - last: 'Holodovskiy', - }, - email: 'iegor.holodovskiy@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Iegor', - nat: 'UA', - }, - { - gender: 'female', - name: { - title: 'Madame', - first: 'Jessica', - last: 'David', - }, - email: 'jessica.david@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jessica', - nat: 'CH', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Eve', - last: 'Martinez', - }, - email: 'eve.martinez@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Eve', - nat: 'FR', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Caleb', - last: 'Silva', - }, - email: 'caleb.silva@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Caleb', - nat: 'US', - }, - { - gender: 'female', - name: { - title: 'Miss', - first: 'Marcia', - last: 'Jenkins', - }, - email: 'marcia.jenkins@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Marcia', - nat: 'US', - }, - { - gender: 'female', - name: { - title: 'Mrs', - first: 'Mackenzie', - last: 'Jones', - }, - email: 'mackenzie.jones@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mackenzie', - nat: 'NZ', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Jeremiah', - last: 'Gutierrez', - }, - email: 'jeremiah.gutierrez@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jeremiah', - nat: 'AU', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Luciara', - last: 'Souza', - }, - email: 'luciara.souza@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Luciara', - nat: 'BR', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Valgi', - last: 'da Cunha', - }, - email: 'valgi.dacunha@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Valgi', - nat: 'BR', - }, - ], -}; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - email: string; // "duane.reed@example.com" - picture: string; // "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane" - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable = ({ users }: DenseTableProps) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map(user => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( - - ); -}; - -export const ExampleFetchComponent = () => { - const { value, loading, error } = useAsync(async (): Promise => { - // Would use fetch in a real world example - return exampleUsers.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return ; - } - - return ; -}; diff --git a/plugins/plugin-radius/src/components/ExampleFetchComponent/index.ts b/plugins/plugin-radius/src/components/ExampleFetchComponent/index.ts deleted file mode 100644 index 41a43e8..0000000 --- a/plugins/plugin-radius/src/components/ExampleFetchComponent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ExampleFetchComponent } from './ExampleFetchComponent'; diff --git a/plugins/plugin-radius/src/components/applications/ApplicationListPage.test.tsx b/plugins/plugin-radius/src/components/applications/ApplicationListPage.test.tsx new file mode 100644 index 0000000..d71dbab --- /dev/null +++ b/plugins/plugin-radius/src/components/applications/ApplicationListPage.test.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { ApplicationListPage } from './ApplicationListPage'; +import { screen } from '@testing-library/react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { RadiusApi } from '../../api'; +import { radiusApiRef } from '../../plugin'; +import { ApplicationProperties, ResourceList } from '../../resources'; + +describe('ApplicationListPage', () => { + // Rendering an empty table is fine for now, we have good unit tests for the + // table logic elsewhere. + it('should render table', async () => { + const api: Pick = { + listResources: async () => + Promise.resolve>({ + value: [], + }), + }; + + await renderInTestApp( + + + , + ); + expect( + screen.getByText('Displaying deployed applications.'), + ).toBeInTheDocument(); + expect(screen.getByRole('table')).toBeInTheDocument(); + + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + + const rows = screen.getAllByRole('row'); + expect(rows).toHaveLength(2); // Header + empty row + const [header] = rows; + + // Verify correct headings (we had headings that will never be shown for an application) + const expectedColumns = [ + 'Name', + 'Resource Group', + 'Type', + 'Environment', + 'Status', + ]; + const headings = header.querySelectorAll('th'); + expect(headings).toHaveLength(expectedColumns.length); + headings.forEach((heading, index) => { + expect(heading).toHaveTextContent(expectedColumns[index]); + }); + }); +}); diff --git a/plugins/plugin-radius/src/components/applications/ApplicationListPage.tsx b/plugins/plugin-radius/src/components/applications/ApplicationListPage.tsx new file mode 100644 index 0000000..a278ffb --- /dev/null +++ b/plugins/plugin-radius/src/components/applications/ApplicationListPage.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Grid } from '@material-ui/core'; +import { Header, Page, Content } from '@backstage/core-components'; +import { ResourceTable } from '../resourcetable'; + +export const ApplicationListPage = () => ( + +
+ + + + + + + + +); diff --git a/plugins/plugin-radius/src/components/applications/index.ts b/plugins/plugin-radius/src/components/applications/index.ts new file mode 100644 index 0000000..9647405 --- /dev/null +++ b/plugins/plugin-radius/src/components/applications/index.ts @@ -0,0 +1 @@ +export { ApplicationListPage } from './ApplicationListPage'; diff --git a/plugins/plugin-radius/src/components/environments/EnvironmentListPage.test.tsx b/plugins/plugin-radius/src/components/environments/EnvironmentListPage.test.tsx new file mode 100644 index 0000000..87384a8 --- /dev/null +++ b/plugins/plugin-radius/src/components/environments/EnvironmentListPage.test.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { EnvironmentListPage } from './EnvironmentListPage'; +import { screen } from '@testing-library/react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { RadiusApi } from '../../api'; +import { radiusApiRef } from '../../plugin'; +import { EnvironmentProperties, ResourceList } from '../../resources'; + +describe('EnvironmentListPage', () => { + // Rendering an empty table is fine for now, we have good unit tests for the + // table logic elsewhere. + it('should render table', async () => { + const api: Pick = { + listResources: async () => + Promise.resolve>({ + value: [], + }), + }; + + await renderInTestApp( + + + , + ); + expect( + screen.getByText( + 'Displaying environments where applications can be deployed.', + ), + ).toBeInTheDocument(); + expect(screen.getByRole('table')).toBeInTheDocument(); + + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + + const rows = screen.getAllByRole('row'); + expect(rows).toHaveLength(2); // Header + empty row + const [header] = rows; + + // Verify correct headings (we had headings that will never be shown for an environment) + const expectedColumns = ['Name', 'Resource Group', 'Type', 'Status']; + const headings = header.querySelectorAll('th'); + expect(headings).toHaveLength(expectedColumns.length); + headings.forEach((heading, index) => { + expect(heading).toHaveTextContent(expectedColumns[index]); + }); + }); +}); diff --git a/plugins/plugin-radius/src/components/environments/EnvironmentListPage.tsx b/plugins/plugin-radius/src/components/environments/EnvironmentListPage.tsx new file mode 100644 index 0000000..827b6aa --- /dev/null +++ b/plugins/plugin-radius/src/components/environments/EnvironmentListPage.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Grid } from '@material-ui/core'; +import { Header, Page, Content } from '@backstage/core-components'; +import { ResourceTable } from '../resourcetable'; + +export const EnvironmentListPage = () => ( + +
+ + + + + + + + +); diff --git a/plugins/plugin-radius/src/components/environments/index.ts b/plugins/plugin-radius/src/components/environments/index.ts new file mode 100644 index 0000000..031430e --- /dev/null +++ b/plugins/plugin-radius/src/components/environments/index.ts @@ -0,0 +1 @@ +export { EnvironmentListPage } from './EnvironmentListPage'; diff --git a/plugins/plugin-radius/src/components/logo/RadiusLogo.test.tsx b/plugins/plugin-radius/src/components/logo/RadiusLogo.test.tsx new file mode 100644 index 0000000..6fbba52 --- /dev/null +++ b/plugins/plugin-radius/src/components/logo/RadiusLogo.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import RadiusLogo from './RadiusLogo'; +import { render, screen } from '@testing-library/react'; + +describe('RadiusLogo', () => { + it('should render the full logo by default', () => { + const result = render(); + const svg = result.baseElement.querySelector('svg'); + expect(svg).toBeInTheDocument(); + expect(svg).toHaveAttribute('viewBox', '0 0 3500 950'); + }); + + it('should render the full logo when asked', () => { + const result = render(); + const svg = result.baseElement.querySelector('svg'); + expect(svg).toBeInTheDocument(); + expect(svg).toHaveAttribute('viewBox', '0 0 3500 950'); + }); + + it('should render the square logo when asked', () => { + const result = render(); + const svg = result.baseElement.querySelector('svg'); + expect(svg).toBeInTheDocument(); + expect(svg).toHaveAttribute('viewBox', '0 0 950 950'); + }); +}); diff --git a/plugins/plugin-radius/src/components/logo/RadiusLogo.tsx b/plugins/plugin-radius/src/components/logo/RadiusLogo.tsx new file mode 100644 index 0000000..09f7e31 --- /dev/null +++ b/plugins/plugin-radius/src/components/logo/RadiusLogo.tsx @@ -0,0 +1,145 @@ +import React from 'react'; +import { SvgIcon, SvgIconProps } from '@material-ui/core'; + +export const RadiusLogo = ( + props: SvgIconProps & { shape?: 'full' | 'square' }, +) => { + const originalWidth = 3500; + const originalHeight = 950; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default RadiusLogo; diff --git a/plugins/plugin-radius/src/components/resourcelink/ResourceLink.test.tsx b/plugins/plugin-radius/src/components/resourcelink/ResourceLink.test.tsx new file mode 100644 index 0000000..a41e183 --- /dev/null +++ b/plugins/plugin-radius/src/components/resourcelink/ResourceLink.test.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { screen, waitFor } from '@testing-library/react'; +import { ResourceLink } from './ResourceLink'; +import { renderInTestApp } from '@backstage/test-utils'; +import { resourcePageRouteRef } from '../../routes'; +import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; + +describe('ResourceLink', () => { + it('should render', async () => { + const id = + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/environments/test-environment'; + await renderInTestApp(Hi There!, { + mountedRoutes: { + '/resource/:group/:namespace/:type/:name': resourcePageRouteRef, + }, + }); + expect(screen.getByRole('link', { name: 'Hi There!' })).toHaveAttribute( + 'href', + '/resource/test-group/Applications.Core/environments/test-environment', + ); + }); + + it('should render the provided resource name', async () => { + const id = + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/environments/test-environment'; + await renderInTestApp(, { + mountedRoutes: { + '/resource/:group/:namespace/:type/:name': resourcePageRouteRef, + }, + }); + expect( + screen.getByRole('link', { name: 'test-environment' }), + ).toHaveAttribute( + 'href', + '/resource/test-group/Applications.Core/environments/test-environment', + ); + }); + + it('should throw for invalid resource id', async () => { + const id = + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Cor12323231e-----/environments'; + const fallbackRender = jest.fn((_: FallbackProps) => null); + await renderInTestApp( + + Should not render + , + { + mountedRoutes: { + '/resource/:group/:namespace/:type/:name': resourcePageRouteRef, + }, + }, + ); + + await waitFor(() => expect(fallbackRender).toHaveBeenCalled()); + expect(fallbackRender.mock.calls[0][0].error).toEqual( + new Error(`Invalid resource id ${id}`), + ); + }); +}); diff --git a/plugins/plugin-radius/src/components/resourcelink/ResourceLink.tsx b/plugins/plugin-radius/src/components/resourcelink/ResourceLink.tsx new file mode 100644 index 0000000..ee2d09c --- /dev/null +++ b/plugins/plugin-radius/src/components/resourcelink/ResourceLink.tsx @@ -0,0 +1,37 @@ +import React, { PropsWithChildren } from 'react'; +import { Link } from '@backstage/core-components'; +import { parseResourceId } from '../../resources/resourceId'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { resourcePageRouteRef } from '../../routes'; + +export const ResourceLink = (props: PropsWithChildren<{ id: string }>) => { + const parsed = parseResourceId(props.id); + if (!parsed) { + throw new Error(`Invalid resource id ${props.id}`); + } + + const route = useRouteRef(resourcePageRouteRef); + + return ( + + {props.children ?? parsed.name} + + ); +}; + +export const OptionalResourceLink = ( + props: PropsWithChildren<{ id?: string }>, +) => { + if (!props.id) { + return null; + } + + return {props.children}; +}; diff --git a/plugins/plugin-radius/src/components/resourcelink/index.ts b/plugins/plugin-radius/src/components/resourcelink/index.ts new file mode 100644 index 0000000..e512842 --- /dev/null +++ b/plugins/plugin-radius/src/components/resourcelink/index.ts @@ -0,0 +1 @@ +export { ResourceLink, OptionalResourceLink } from './ResourceLink'; diff --git a/plugins/plugin-radius/src/components/resources/ApplicationTab.tsx b/plugins/plugin-radius/src/components/resources/ApplicationTab.tsx new file mode 100644 index 0000000..dc32204 --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/ApplicationTab.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { Resource, parseResourceId } from '../../resources'; +import { + InfoCard, + Progress, + ResponseErrorPanel, +} from '@backstage/core-components'; +import { kubernetesApiRef } from '@backstage/plugin-kubernetes'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { AppGraph } from 'rad-components'; +import { makeStyles } from '@material-ui/core'; + +export interface AppGraphData { + name: string; + resources: ResourceData[]; +} + +export interface ResourceData { + id: string; + name: string; + type: string; + provider: string; + provisioningState: string; + resources?: ResourceData[]; + connections?: Connection[]; +} + +export interface Connection { + id: string; + name: string; + type: string; + provider: string; + direction: Direction; +} + +export type Direction = 'Outbound' | 'Inbound'; + +const useStyles = makeStyles({ + container: { + height: '800px', + width: '100%', + }, +}); + +export const ApplicationTab = (props: { resource: Resource }) => { + const styles = useStyles(); + const application = props.resource.properties?.application as string; + const kubernetesApi = useApi(kubernetesApiRef); + const { value, loading, error } = + useAsync(async (): Promise => { + let first = ''; + const clusters = await kubernetesApi.getClusters(); + for (const cluster of clusters) { + first = cluster.name; + } + + const response = await kubernetesApi.proxy({ + clusterName: first, + path: `/apis/api.ucp.dev/v1alpha3/${application}/getGraph?api-version=2023-10-01-preview`, + init: { + referrerPolicy: 'no-referrer', + mode: 'cors', + cache: 'no-cache', + method: 'POST', + }, + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`Request failed: ${response.status}:\n\n ${text}`); + } + + return (await response.json()) as AppGraphData; + }, [application]); + + if (loading || !value) { + return ; + } else if (error) { + return ; + } + + return ( + <> + +
+ +
+
+ +
{JSON.stringify(value, null, 2)}
+
+ + ); +}; diff --git a/plugins/plugin-radius/src/components/resources/DetailsTab.tsx b/plugins/plugin-radius/src/components/resources/DetailsTab.tsx new file mode 100644 index 0000000..3429e4e --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/DetailsTab.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Resource } from '../../resources'; +import { InfoCard } from '@backstage/core-components'; + +export const DetailsTab = (props: { resource: Resource }) => { + return ( + +
{JSON.stringify(props.resource, null, 2)}
+
+ ); +}; diff --git a/plugins/plugin-radius/src/components/resources/OverviewTab.tsx b/plugins/plugin-radius/src/components/resources/OverviewTab.tsx new file mode 100644 index 0000000..3f4c7eb --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/OverviewTab.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Resource, parseResourceId } from '../../resources'; +import { InfoCard, StructuredMetadataTable } from '@backstage/core-components'; +import { OptionalResourceLink } from '../resourcelink/ResourceLink'; + +export const OverviewTab = (props: { resource: Resource }) => { + const metadata = { + name: props.resource.name, + type: props.resource.type, + group: parseResourceId(props.resource.id)?.group, + application: ( + + ), + environment: ( + + ), + }; + return ( + + + + ); +}; diff --git a/plugins/plugin-radius/src/components/resources/ResourceLayout.tsx b/plugins/plugin-radius/src/components/resources/ResourceLayout.tsx new file mode 100644 index 0000000..fb1b192 --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/ResourceLayout.tsx @@ -0,0 +1,26 @@ +import { Content, Header, Page } from '@backstage/core-components'; +import { useRouteRefParams } from '@backstage/core-plugin-api'; +import { Grid } from '@material-ui/core'; +import React, { PropsWithChildren } from 'react'; +import { resourcePageRouteRef } from '../../routes'; +import { Resource } from '../../resources'; + +export const ResourceLayout = ( + props: PropsWithChildren<{ resource: Resource }>, +) => { + const params = useRouteRefParams(resourcePageRouteRef); + + return ( + +
+ + + {props.children} + + + + ); +}; diff --git a/plugins/plugin-radius/src/components/resources/ResourceListPage.tsx b/plugins/plugin-radius/src/components/resources/ResourceListPage.tsx new file mode 100644 index 0000000..f478771 --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/ResourceListPage.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Grid } from '@material-ui/core'; +import { Header, Page, Content } from '@backstage/core-components'; +import { ResourceTable } from '../resourcetable'; + +export const ResourceListPage = () => ( + +
+ + + + + + + + +); diff --git a/plugins/plugin-radius/src/components/resources/ResourcePage.test.tsx b/plugins/plugin-radius/src/components/resources/ResourcePage.test.tsx new file mode 100644 index 0000000..49756dc --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/ResourcePage.test.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen, waitFor } from '@testing-library/react'; +import { RadiusApi } from '../../api'; +import { radiusApiRef } from '../../plugin'; +import { Resource, ResourceList } from '../../resources'; +import { ResourcePage } from './ResourcePage'; +import { resourcePageRouteRef } from '../../routes'; + +jest.mock('react-router-dom', () => { + return { + ...jest.requireActual('react-router-dom'), + useParams: () => ({ + group: 'test-group', + namespace: 'Applications.Core', + type: 'applications', + name: 'test-app', + }), + }; +}); + +describe('ResourcePage', () => { + it('should display loading indicator while loading', async () => { + // This is the boilerplate for an unresolved promise. + const deferred: ((resolve: Resource) => void)[] = []; + const api: Pick = { + getResourceById: async () => + new Promise>(resolve => { + deferred.push( + resolve as unknown as (resolve: Resource) => void, + ); + }), + }; + + await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect(screen.getByTestId('progress')).toBeInTheDocument(); + }); + + // "Complete" the loading of resources. + deferred[0]({ + id: '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/applications/test-app', + type: 'Applications.Core/applications', + name: 'test-app', + systemData: {}, + properties: {}, + }); + + await waitFor(() => { + expect(screen.queryByTestId('progress')).toBeNull(); + }); + + expect( + screen.getByText( + 'Displaying details for Applications.Core/applications: test-app', + ), + ).toBeInTheDocument(); + + await waitFor(() => { + expect(screen.getByText('Overview')).toBeInTheDocument(); + }); + }); + + it('should display error message when loading fails', async () => { + const api: Pick = { + getResourceById: async () => Promise.reject(new Error('Oh noes!')), + }; + + await renderInTestApp( + + + , + ); + const alert = screen.getByRole('alert'); + expect(alert).toBeInTheDocument(); + expect(alert).toHaveTextContent('Oh noes!'); + }); + + it('should render when loaded', async () => { + const api: Pick = { + getResourceById: async () => + Promise.resolve({ + id: '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/applications/test-app', + type: 'Applications.Core/applications', + name: 'test-app', + systemData: {}, + properties: {} as T, + }), + }; + + await renderInTestApp( + + + , + ); + + expect( + screen.getByText( + 'Displaying details for Applications.Core/applications: test-app', + ), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/plugin-radius/src/components/resources/ResourcePage.tsx b/plugins/plugin-radius/src/components/resources/ResourcePage.tsx new file mode 100644 index 0000000..5a7f91f --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/ResourcePage.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { ResourceLayout } from './ResourceLayout'; +import { + Progress, + ResponseErrorPanel, + TabbedLayout, +} from '@backstage/core-components'; +import { OverviewTab } from './OverviewTab'; +import { DetailsTab } from './DetailsTab'; +import { Resource } from '../../resources'; +import useAsync from 'react-use/lib/useAsync'; +import { useApi, useRouteRefParams } from '@backstage/core-plugin-api'; +import { resourcePageRouteRef } from '../../routes'; +import { ApplicationTab } from './ApplicationTab'; +import { ResourcesTab } from './ResourcesTab'; +import { radiusApiRef } from '../../plugin'; + +export const ResourcePage = () => { + const radiusApi = useApi(radiusApiRef); + const params = useRouteRefParams(resourcePageRouteRef); + const id = `/planes/radius/local/resourceGroups/${params.group}/providers/${params.namespace}/${params.type}/${params.name}`; + + const { value, loading, error } = useAsync(async (): Promise => { + return radiusApi.getResourceById({ id }); + }, [id]); + + if (loading) { + return ; + } else if (error) { + return ; + } else if (!value) { + throw new Error('This should not happen.'); + } + + const hasApplication = value?.properties?.application || false; + const isApplication = value?.type === 'Applications.Core/applications'; + + return ( + + + + + + + + + {hasApplication && ( + + + + )} + {isApplication && ( + + + + )} + + + ); +}; diff --git a/plugins/plugin-radius/src/components/resources/ResourcesTab.tsx b/plugins/plugin-radius/src/components/resources/ResourcesTab.tsx new file mode 100644 index 0000000..8a229ba --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/ResourcesTab.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Resource } from '../../resources'; +import { InfoCard } from '@backstage/core-components'; + +export const ResourcesTab = (props: { resource: Resource }) => { + return ( + +
{JSON.stringify(props.resource, null, 2)}
+
+ ); +}; diff --git a/plugins/plugin-radius/src/components/resources/index.ts b/plugins/plugin-radius/src/components/resources/index.ts new file mode 100644 index 0000000..2ed4b0a --- /dev/null +++ b/plugins/plugin-radius/src/components/resources/index.ts @@ -0,0 +1,2 @@ +export { ResourcePage } from './ResourcePage'; +export { ResourceListPage } from './ResourceListPage'; diff --git a/plugins/plugin-radius/src/components/resourcetable/ResourceTable.test.tsx b/plugins/plugin-radius/src/components/resourcetable/ResourceTable.test.tsx new file mode 100644 index 0000000..0a00cbe --- /dev/null +++ b/plugins/plugin-radius/src/components/resourcetable/ResourceTable.test.tsx @@ -0,0 +1,204 @@ +import React from 'react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen, waitFor } from '@testing-library/react'; +import { RadiusApi } from '../../api'; +import { radiusApiRef } from '../../plugin'; +import { ResourceList } from '../../resources'; +import { ResourceTable } from './ResourceTable'; +import { resourcePageRouteRef } from '../../routes'; + +describe('ResourceTable', () => { + it('should display loading indicator while loading', async () => { + // This is the boilerplate for an unresolved promise. + const deferred: ((resolve: { value: never[] }) => void)[] = []; + const api: Pick = { + listResources: async () => + new Promise>(resolve => { + deferred.push(resolve); + }), + }; + + await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect(screen.getByTestId('progress')).toBeInTheDocument(); + }); + + // "Complete" the loading of resources. + deferred[0]({ + value: [], + }); + + await waitFor(() => { + expect(screen.queryByTestId('progress')).toBeNull(); + }); + + await waitFor(() => { + expect(screen.getByRole('table')).toBeInTheDocument(); + }); + }); + + it('should display error message when loading fails', async () => { + const api: Pick = { + listResources: async () => Promise.reject(new Error('Oh noes!')), + }; + + await renderInTestApp( + + + , + ); + const alert = screen.getByRole('alert'); + expect(alert).toBeInTheDocument(); + expect(alert).toHaveTextContent('Oh noes!'); + }); + + it('should render empty table', async () => { + const api: Pick = { + listResources: async () => + Promise.resolve({ + value: [], + }), + }; + + await renderInTestApp( + + + , + ); + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + }); + + it('should render table with items', async () => { + const api: Pick = { + listResources: async () => + Promise.resolve>({ + value: [ + { + id: '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/applications/test-app', + type: 'Applications.Core/applications', + name: 'test-app', + systemData: {}, + properties: { + provisioningState: 'Succeeded', + environment: + '/planes/radius/local/resourceGroups/test-group-env/providers/Applications.Core/environments/test-env', + } as T, + }, + { + id: '/planes/radius/local/resourceGroups/test-group-env/providers/Applications.Core/environments/test-env', + type: 'Applications.Core/environments', + name: 'test-env', + systemData: {}, + properties: { + provisioningState: 'Succeeded', + } as T, + }, + { + id: '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/containers/test-container', + type: 'Applications.Core/containers', + name: 'test-container', + systemData: {}, + properties: { + provisioningState: 'Succeeded', + application: + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/applications/test-app', + environment: + '/planes/radius/local/resourceGroups/test-group-env/providers/Applications.Core/environments/test-env', + } as T, + }, + { + id: '/planes/radius/local/resourceGroups/test-group/providers/Applications.Datastores/redisCaches/test-db', + type: 'Applications.Datastores/redisCaches', + name: 'test-db', + systemData: {}, + properties: { + provisioningState: 'Succeeded', + application: + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/applications/test-app', + environment: + '/planes/radius/local/resourceGroups/test-group-env/providers/Applications.Core/environments/test-env', + } as T, + }, + ], + }), + }; + + await renderInTestApp( + + + , + { + mountedRoutes: { + '/resources': resourcePageRouteRef, + }, + }, + ); + const table = screen.getByRole('table'); + expect(table).toBeInTheDocument(); + + const rows = screen.getAllByRole('row'); + expect(rows).toHaveLength(5); // Header + 4 resources + const [header, app, env, container, db] = rows; + + // Verify correct headings + const expectedColumns = [ + 'Name', + 'Resource Group', + 'Type', + 'Application', + 'Environment', + 'Status', + ]; + const headings = header.querySelectorAll('th'); + expect(headings).toHaveLength(expectedColumns.length); + headings.forEach((heading, index) => { + expect(heading).toHaveTextContent(expectedColumns[index]); + }); + + // Verify correct data: application + const appCells = app.querySelectorAll('td'); + expect(appCells).toHaveLength(expectedColumns.length); + expect(appCells[0]).toHaveTextContent('test-app'); + expect(appCells[1]).toHaveTextContent('test-group'); + expect(appCells[2]).toHaveTextContent('Applications.Core/applications'); + expect(appCells[3]).toHaveTextContent(''); + expect(appCells[4]).toHaveTextContent('test-env'); + expect(appCells[5]).toHaveTextContent('Succeeded'); + + // Verify correct data: environment + const envCells = env.querySelectorAll('td'); + expect(envCells).toHaveLength(expectedColumns.length); + expect(envCells[0]).toHaveTextContent('test-env'); + expect(envCells[1]).toHaveTextContent('test-group'); + expect(envCells[2]).toHaveTextContent('Applications.Core/environments'); + expect(envCells[3]).toHaveTextContent(''); + expect(envCells[4]).toHaveTextContent(''); + expect(envCells[5]).toHaveTextContent('Succeeded'); + + // Verify correct data: container + const containerCells = container.querySelectorAll('td'); + expect(containerCells).toHaveLength(expectedColumns.length); + expect(containerCells[0]).toHaveTextContent('test-container'); + expect(containerCells[1]).toHaveTextContent('test-group'); + expect(containerCells[2]).toHaveTextContent('Applications.Core/containers'); + expect(containerCells[3]).toHaveTextContent('test-app'); + expect(containerCells[4]).toHaveTextContent('test-env'); + expect(containerCells[5]).toHaveTextContent('Succeeded'); + + // Verify correct data: db + const dbCells = db.querySelectorAll('td'); + expect(dbCells).toHaveLength(expectedColumns.length); + expect(dbCells[0]).toHaveTextContent('test-db'); + expect(dbCells[1]).toHaveTextContent('test-group'); + expect(dbCells[2]).toHaveTextContent('Applications.Datastores/redisCaches'); + expect(dbCells[3]).toHaveTextContent('test-app'); + expect(dbCells[4]).toHaveTextContent('test-env'); + expect(dbCells[5]).toHaveTextContent('Succeeded'); + }); +}); diff --git a/plugins/plugin-radius/src/components/resourcetable/ResourceTable.tsx b/plugins/plugin-radius/src/components/resourcetable/ResourceTable.tsx new file mode 100644 index 0000000..b4cc4e7 --- /dev/null +++ b/plugins/plugin-radius/src/components/resourcetable/ResourceTable.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { Resource, ResourceList, parseResourceId } from '../../resources'; +import { + Progress, + ResponseErrorPanel, + Table, + TableColumn, +} from '@backstage/core-components'; +import { + OptionalResourceLink, + ResourceLink, +} from '../resourcelink/ResourceLink'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { radiusApiRef } from '../../plugin'; + +const DataTable = (props: { + resources: Resource[]; + title: string; + resourceType?: string; +}) => { + const columns: TableColumn[] = [ + { + title: 'Name', + render: row => , + }, + { title: 'Resource Group', render: row => parseResourceId(row.id)?.group }, + { title: 'Type', field: 'type' }, + ]; + + // Special case some additional fields by hiding them when they would never have a value. + if (props.resourceType === 'Applications.Core/environments') { + // Nothing to add + } else if (props.resourceType === 'Applications.Core/applications') { + columns.push({ + title: 'Environment', + field: 'properties.environment', + render: row => ( + + ), + }); + } else { + columns.push({ + title: 'Application', + field: 'properties.application', + render: row => ( + + ), + }); + columns.push({ + title: 'Environment', + field: 'properties.environment', + render: row => ( + + ), + }); + } + + columns.push({ title: 'Status', field: 'properties.provisioningState' }); + + return ( +
+ ); +}; + +export const ResourceTable = (props: { + title: string; + resourceType?: string; +}) => { + const radiusApi = useApi(radiusApiRef); + const { value, loading, error } = useAsync( + async (): Promise> => { + return radiusApi.listResources<{ [key: string]: unknown }>({ + resourceType: props.resourceType, + }); + }, + ); + + if (loading) { + return ; + } else if (error) { + return ; + } + + return ( + + ); +}; diff --git a/plugins/plugin-radius/src/components/resourcetable/index.ts b/plugins/plugin-radius/src/components/resourcetable/index.ts new file mode 100644 index 0000000..290432a --- /dev/null +++ b/plugins/plugin-radius/src/components/resourcetable/index.ts @@ -0,0 +1 @@ +export { ResourceTable } from './ResourceTable'; diff --git a/plugins/plugin-radius/src/index.ts b/plugins/plugin-radius/src/index.ts index 5030c92..aeff0db 100644 --- a/plugins/plugin-radius/src/index.ts +++ b/plugins/plugin-radius/src/index.ts @@ -1 +1,8 @@ -export { radiusPlugin, ApplicationPage, EnvironmentPage } from './plugin'; +export { + radiusPlugin, + ApplicationListPage, + EnvironmentListPage, + ResourceListPage, + ResourcePage, +} from './plugin'; +export { RadiusLogo } from './components/logo/RadiusLogo'; diff --git a/plugins/plugin-radius/src/plugin.ts b/plugins/plugin-radius/src/plugin.ts index 70d894e..df96aab 100644 --- a/plugins/plugin-radius/src/plugin.ts +++ b/plugins/plugin-radius/src/plugin.ts @@ -1,35 +1,74 @@ import { + createApiFactory, + createApiRef, createPlugin, createRoutableExtension, } from '@backstage/core-plugin-api'; -import { rootRouteRef } from './routes'; +import { + applicationListPageRouteRef, + environmentListPageRouteRef, + resourceListPageRouteRef, + resourcePageRouteRef, + rootRouteRef, +} from './routes'; +import { RadiusApi } from './api'; +import { KubernetesApi, kubernetesApiRef } from '@backstage/plugin-kubernetes'; +import { RadiusApiImpl } from './api/api'; + +export const radiusApiRef = createApiRef({ + id: 'radius-api', +}); export const radiusPlugin = createPlugin({ id: 'radius', + apis: [ + createApiFactory({ + api: radiusApiRef, + deps: { + kubernetesApi: kubernetesApiRef, + }, + factory: (deps: { kubernetesApi: KubernetesApi }) => { + return new RadiusApiImpl(deps.kubernetesApi); + }, + }), + ], routes: { root: rootRouteRef, }, }); -export const EnvironmentPage = radiusPlugin.provide( +export const EnvironmentListPage = radiusPlugin.provide( createRoutableExtension({ name: 'Environments', component: () => - import('./components/EnvironmentPageComponent').then( - m => m.EnvironmentPageComponent, - ), - mountPoint: rootRouteRef, + import('./components/environments').then(m => m.EnvironmentListPage), + mountPoint: environmentListPageRouteRef, }), ); -export const ApplicationPage = radiusPlugin.provide( +export const ApplicationListPage = radiusPlugin.provide( createRoutableExtension({ name: 'Applications', component: () => - import('./components/ApplicationPageComponent').then( - m => m.ApplicationPageComponent, - ), - mountPoint: rootRouteRef, + import('./components/applications').then(m => m.ApplicationListPage), + mountPoint: applicationListPageRouteRef, + }), +); + +export const ResourceListPage = radiusPlugin.provide( + createRoutableExtension({ + name: 'Resources', + component: () => + import('./components/resources').then(m => m.ResourceListPage), + mountPoint: resourceListPageRouteRef, + }), +); + +export const ResourcePage = radiusPlugin.provide( + createRoutableExtension({ + name: 'Resources', + component: () => import('./components/resources').then(m => m.ResourcePage), + mountPoint: resourcePageRouteRef, }), ); diff --git a/plugins/plugin-radius/src/resources/index.ts b/plugins/plugin-radius/src/resources/index.ts new file mode 100644 index 0000000..58acedf --- /dev/null +++ b/plugins/plugin-radius/src/resources/index.ts @@ -0,0 +1,8 @@ +export type { + Resource, + ResourceList, + ApplicationProperties, + EnvironmentProperties, +} from './resource'; +export { parseResourceId } from './resourceId'; +export type { ResourceId } from './resourceId'; diff --git a/plugins/plugin-radius/src/resources/resource.ts b/plugins/plugin-radius/src/resources/resource.ts new file mode 100644 index 0000000..3e0ada6 --- /dev/null +++ b/plugins/plugin-radius/src/resources/resource.ts @@ -0,0 +1,21 @@ +export interface Resource { + id: string; + type: string; + name: string; + tags?: Record; + systemData: Record; + properties: T; +} + +export interface ResourceList { + value: Resource[]; +} + +export interface ApplicationProperties { + provisioningState: string; + environment: string; +} + +export interface EnvironmentProperties { + provisioningState: string; +} diff --git a/plugins/plugin-radius/src/resources/resourceId.test.ts b/plugins/plugin-radius/src/resources/resourceId.test.ts new file mode 100644 index 0000000..3ea0a9e --- /dev/null +++ b/plugins/plugin-radius/src/resources/resourceId.test.ts @@ -0,0 +1,22 @@ +import { parseResourceId } from './resourceId'; + +describe('parseResourceId', () => { + it('should parse a valid environment resource ID', () => { + const resourceId = + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Core/environments/test-environment'; + const parsed = parseResourceId(resourceId); + expect(parsed).toEqual({ + plane: 'local', + group: 'test-group', + type: 'Applications.Core/environments', + name: 'test-environment', + }); + }); + + it('should return undefined for an invalid resource ID', () => { + const resourceId = + '/planes/radius/local/resourceGroups/test-group/providers/Applications.Cor12323231e-----/environments'; + const parsed = parseResourceId(resourceId); + expect(parsed).toBeUndefined(); + }); +}); diff --git a/plugins/plugin-radius/src/resources/resourceId.ts b/plugins/plugin-radius/src/resources/resourceId.ts new file mode 100644 index 0000000..dcef704 --- /dev/null +++ b/plugins/plugin-radius/src/resources/resourceId.ts @@ -0,0 +1,19 @@ +const RESOURCE_ID_REGEX = + /^\/planes\/radius\/(?[0-9a-zA-Z-]+)\/resourceGroups\/(?[0-9a-zA-Z-]+)\/providers\/(?[a-zA-Z\\.]+)\/(?[a-zA-Z]+)\/(?[0-9a-zA-Z-]+)$/i; + +export interface ResourceId { + plane: string; + group: string; + type: string; + name: string; +} + +export function parseResourceId(resourceId: string): ResourceId | undefined { + const match = RESOURCE_ID_REGEX.exec(resourceId); + if (!match) { + return undefined; + } + + const { plane, group, namespace, type, name } = match.groups ?? {}; + return { plane, group, type: `${namespace}/${type}`, name }; +} diff --git a/plugins/plugin-radius/src/routes.ts b/plugins/plugin-radius/src/routes.ts index 5c35055..e20a91c 100644 --- a/plugins/plugin-radius/src/routes.ts +++ b/plugins/plugin-radius/src/routes.ts @@ -3,3 +3,20 @@ import { createRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ id: 'radius', }); + +export const applicationListPageRouteRef = createRouteRef({ + id: 'radius-application-list-page', +}); + +export const environmentListPageRouteRef = createRouteRef({ + id: 'radius-environment-list-page', +}); + +export const resourceListPageRouteRef = createRouteRef({ + id: 'radius-resource-list-page', +}); + +export const resourcePageRouteRef = createRouteRef({ + id: 'radius-resource-page', + params: ['group', 'namespace', 'type', 'name'], +}); diff --git a/plugins/plugin-radius/src/setupTests.ts b/plugins/plugin-radius/src/setupTests.ts index 7b0828b..068c53d 100644 --- a/plugins/plugin-radius/src/setupTests.ts +++ b/plugins/plugin-radius/src/setupTests.ts @@ -1 +1,2 @@ import '@testing-library/jest-dom'; +import 'jest-canvas-mock'; diff --git a/yarn.lock b/yarn.lock index 4a1a1e5..41fd65c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -225,6 +225,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/sha256-js@npm:^5.0.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-js@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 6c48701f8336341bb104dfde3d0050c89c288051f6b5e9bdfeb8091cf3ffc86efcd5c9e6ff2a4a134406b019c07aca9db608128f8d9267c952578a3108db9fd1 + languageName: node + linkType: hard + "@aws-crypto/supports-web-crypto@npm:^3.0.0": version: 3.0.0 resolution: "@aws-crypto/supports-web-crypto@npm:3.0.0" @@ -245,6 +256,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/util@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/util@npm:5.2.0" + dependencies: + "@aws-sdk/types": "npm:^3.222.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 0362d4c197b1fd64b423966945130207d1fe23e1bb2878a18e361f7743c8d339dad3f8729895a29aa34fff6a86c65f281cf5167c4bf253f21627ae80b6dd2951 + languageName: node + linkType: hard + "@aws-sdk/abort-controller@npm:^3.347.0": version: 3.374.0 resolution: "@aws-sdk/abort-controller@npm:3.374.0" @@ -1063,7 +1085,7 @@ __metadata: languageName: node linkType: hard -"@azure/core-util@npm:^1.0.0, @azure/core-util@npm:^1.1.0, @azure/core-util@npm:^1.1.1, @azure/core-util@npm:^1.2.0, @azure/core-util@npm:^1.3.0, @azure/core-util@npm:^1.6.1": +"@azure/core-util@npm:^1.0.0, @azure/core-util@npm:^1.1.0, @azure/core-util@npm:^1.1.1, @azure/core-util@npm:^1.2.0, @azure/core-util@npm:^1.3.0": version: 1.6.1 resolution: "@azure/core-util@npm:1.6.1" dependencies: @@ -1073,28 +1095,6 @@ __metadata: languageName: node linkType: hard -"@azure/identity@npm:^3.2.1": - version: 3.4.1 - resolution: "@azure/identity@npm:3.4.1" - dependencies: - "@azure/abort-controller": "npm:^1.0.0" - "@azure/core-auth": "npm:^1.5.0" - "@azure/core-client": "npm:^1.4.0" - "@azure/core-rest-pipeline": "npm:^1.1.0" - "@azure/core-tracing": "npm:^1.0.0" - "@azure/core-util": "npm:^1.6.1" - "@azure/logger": "npm:^1.0.0" - "@azure/msal-browser": "npm:^3.5.0" - "@azure/msal-node": "npm:^2.5.1" - events: "npm:^3.0.0" - jws: "npm:^4.0.0" - open: "npm:^8.0.0" - stoppable: "npm:^1.1.0" - tslib: "npm:^2.2.0" - checksum: 6365d9d03af1c6dbff91bba6353b4d6bdec82858796313e24bf82a91d35b984e91053c7ab07c55b48b27a12a47063c807b3435d018f7fad493f4e3288264d6ec - languageName: node - linkType: hard - "@azure/identity@npm:^4.0.0": version: 4.0.0 resolution: "@azure/identity@npm:4.0.0" @@ -1490,7 +1490,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.15, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.6": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.6": version: 7.23.6 resolution: "@babel/parser@npm:7.23.6" bin: @@ -2682,7 +2682,7 @@ __metadata: languageName: node linkType: hard -"@backstage/app-defaults@npm:^1.4.5, @backstage/app-defaults@npm:^1.4.6": +"@backstage/app-defaults@npm:^1.4.6": version: 1.4.6 resolution: "@backstage/app-defaults@npm:1.4.6" dependencies: @@ -2834,7 +2834,7 @@ __metadata: languageName: node linkType: hard -"@backstage/backend-plugin-api@npm:^0.6.7, @backstage/backend-plugin-api@npm:^0.6.8": +"@backstage/backend-plugin-api@npm:^0.6.8": version: 0.6.8 resolution: "@backstage/backend-plugin-api@npm:0.6.8" dependencies: @@ -2850,7 +2850,7 @@ __metadata: languageName: node linkType: hard -"@backstage/backend-tasks@npm:^0.5.12, @backstage/backend-tasks@npm:^0.5.13": +"@backstage/backend-tasks@npm:^0.5.13": version: 0.5.13 resolution: "@backstage/backend-tasks@npm:0.5.13" dependencies: @@ -2871,7 +2871,7 @@ __metadata: languageName: node linkType: hard -"@backstage/catalog-client@npm:^1.4.6, @backstage/catalog-client@npm:^1.5.0, @backstage/catalog-client@npm:^1.5.1": +"@backstage/catalog-client@npm:^1.5.0, @backstage/catalog-client@npm:^1.5.1": version: 1.5.1 resolution: "@backstage/catalog-client@npm:1.5.1" dependencies: @@ -2902,7 +2902,7 @@ __metadata: languageName: node linkType: hard -"@backstage/cli-node@npm:^0.2.0, @backstage/cli-node@npm:^0.2.1": +"@backstage/cli-node@npm:^0.2.1": version: 0.2.1 resolution: "@backstage/cli-node@npm:0.2.1" dependencies: @@ -2918,138 +2918,6 @@ __metadata: languageName: node linkType: hard -"@backstage/cli@npm:^0.24.0": - version: 0.24.0 - resolution: "@backstage/cli@npm:0.24.0" - dependencies: - "@backstage/catalog-model": "npm:^1.4.3" - "@backstage/cli-common": "npm:^0.1.13" - "@backstage/cli-node": "npm:^0.2.0" - "@backstage/config": "npm:^1.1.1" - "@backstage/config-loader": "npm:^1.5.3" - "@backstage/errors": "npm:^1.2.3" - "@backstage/eslint-plugin": "npm:^0.1.3" - "@backstage/integration": "npm:^1.7.2" - "@backstage/release-manifests": "npm:^0.0.11" - "@backstage/types": "npm:^1.1.1" - "@manypkg/get-packages": "npm:^1.1.3" - "@octokit/graphql": "npm:^5.0.0" - "@octokit/graphql-schema": "npm:^13.7.0" - "@octokit/oauth-app": "npm:^4.2.0" - "@octokit/request": "npm:^6.0.0" - "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.7" - "@rollup/plugin-commonjs": "npm:^23.0.0" - "@rollup/plugin-json": "npm:^5.0.0" - "@rollup/plugin-node-resolve": "npm:^13.0.6" - "@rollup/plugin-yaml": "npm:^4.0.0" - "@spotify/eslint-config-base": "npm:^14.0.0" - "@spotify/eslint-config-react": "npm:^14.0.0" - "@spotify/eslint-config-typescript": "npm:^14.0.0" - "@sucrase/webpack-loader": "npm:^2.0.0" - "@svgr/core": "npm:6.5.x" - "@svgr/plugin-jsx": "npm:6.5.x" - "@svgr/plugin-svgo": "npm:6.5.x" - "@svgr/rollup": "npm:6.5.x" - "@svgr/webpack": "npm:6.5.x" - "@swc/core": "npm:^1.3.46" - "@swc/helpers": "npm:^0.5.0" - "@swc/jest": "npm:^0.2.22" - "@types/jest": "npm:^29.0.0" - "@types/webpack-env": "npm:^1.15.2" - "@typescript-eslint/eslint-plugin": "npm:6.10.0" - "@typescript-eslint/parser": "npm:^6.7.2" - "@yarnpkg/lockfile": "npm:^1.1.0" - "@yarnpkg/parsers": "npm:^3.0.0-rc.4" - bfj: "npm:^7.0.2" - buffer: "npm:^6.0.3" - chalk: "npm:^4.0.0" - chokidar: "npm:^3.3.1" - commander: "npm:^9.1.0" - cross-fetch: "npm:^4.0.0" - cross-spawn: "npm:^7.0.3" - css-loader: "npm:^6.5.1" - ctrlc-windows: "npm:^2.1.0" - diff: "npm:^5.0.0" - esbuild: "npm:^0.19.0" - esbuild-loader: "npm:^2.18.0" - eslint: "npm:^8.6.0" - eslint-config-prettier: "npm:^8.3.0" - eslint-formatter-friendly: "npm:^7.0.0" - eslint-plugin-deprecation: "npm:^1.3.2" - eslint-plugin-import: "npm:^2.25.4" - eslint-plugin-jest: "npm:^27.0.0" - eslint-plugin-jsx-a11y: "npm:^6.5.1" - eslint-plugin-react: "npm:^7.28.0" - eslint-plugin-react-hooks: "npm:^4.3.0" - eslint-webpack-plugin: "npm:^3.1.1" - express: "npm:^4.17.1" - fork-ts-checker-webpack-plugin: "npm:^7.0.0-alpha.8" - fs-extra: "npm:10.1.0" - git-url-parse: "npm:^13.0.0" - glob: "npm:^7.1.7" - global-agent: "npm:^3.0.0" - handlebars: "npm:^4.7.3" - html-webpack-plugin: "npm:^5.3.1" - inquirer: "npm:^8.2.0" - jest: "npm:^29.0.2" - jest-css-modules: "npm:^2.1.0" - jest-environment-jsdom: "npm:^29.0.2" - jest-runtime: "npm:^29.0.2" - json-schema: "npm:^0.4.0" - lodash: "npm:^4.17.21" - mini-css-extract-plugin: "npm:^2.4.2" - minimatch: "npm:^5.1.1" - node-fetch: "npm:^2.6.7" - node-libs-browser: "npm:^2.2.1" - npm-packlist: "npm:^5.0.0" - ora: "npm:^5.3.0" - postcss: "npm:^8.1.0" - process: "npm:^0.11.10" - react-dev-utils: "npm:^12.0.0-next.60" - react-refresh: "npm:^0.14.0" - recursive-readdir: "npm:^2.2.2" - replace-in-file: "npm:^6.0.0" - rollup: "npm:^2.60.2" - rollup-plugin-dts: "npm:^4.0.1" - rollup-plugin-esbuild: "npm:^4.7.2" - rollup-plugin-postcss: "npm:^4.0.0" - rollup-pluginutils: "npm:^2.8.2" - run-script-webpack-plugin: "npm:^0.2.0" - semver: "npm:^7.5.3" - style-loader: "npm:^3.3.1" - sucrase: "npm:^3.20.2" - swc-loader: "npm:^0.2.3" - tar: "npm:^6.1.12" - terser-webpack-plugin: "npm:^5.1.3" - tsx: "npm:^3.14.0" - util: "npm:^0.12.3" - webpack: "npm:^5.70.0" - webpack-dev-server: "npm:^4.7.3" - webpack-node-externals: "npm:^3.0.0" - yaml: "npm:^2.0.0" - yml-loader: "npm:^2.1.0" - yn: "npm:^4.0.0" - zod: "npm:^3.21.4" - peerDependencies: - "@vitejs/plugin-react": ^4.0.4 - vite: ^4.4.9 - vite-plugin-html: ^3.2.0 - vite-plugin-node-polyfills: ^0.16.0 - peerDependenciesMeta: - "@vitejs/plugin-react": - optional: true - vite: - optional: true - vite-plugin-html: - optional: true - vite-plugin-node-polyfills: - optional: true - bin: - backstage-cli: bin/backstage-cli - checksum: 93e15722486f3c2c5f1f7bde8b1dfb9d9de222c221822d5a033b3067feac056112d87c68e79bb7357e770cb63f254bf3130f0660b2e6b5ab37c744f88e3d752e - languageName: node - linkType: hard - "@backstage/cli@npm:^0.25.0": version: 0.25.0 resolution: "@backstage/cli@npm:0.25.0" @@ -3183,7 +3051,7 @@ __metadata: languageName: node linkType: hard -"@backstage/config-loader@npm:^1.5.3, @backstage/config-loader@npm:^1.6.0": +"@backstage/config-loader@npm:^1.6.0": version: 1.6.0 resolution: "@backstage/config-loader@npm:1.6.0" dependencies: @@ -3218,7 +3086,7 @@ __metadata: languageName: node linkType: hard -"@backstage/core-app-api@npm:^1.11.1, @backstage/core-app-api@npm:^1.11.2": +"@backstage/core-app-api@npm:^1.11.2": version: 1.11.2 resolution: "@backstage/core-app-api@npm:1.11.2" dependencies: @@ -3259,7 +3127,7 @@ __metadata: languageName: node linkType: hard -"@backstage/core-components@npm:^0.13.8, @backstage/core-components@npm:^0.13.9": +"@backstage/core-components@npm:^0.13.9": version: 0.13.9 resolution: "@backstage/core-components@npm:0.13.9" dependencies: @@ -3312,7 +3180,7 @@ __metadata: languageName: node linkType: hard -"@backstage/core-plugin-api@npm:^1.8.0, @backstage/core-plugin-api@npm:^1.8.1": +"@backstage/core-plugin-api@npm:^1.8.1": version: 1.8.1 resolution: "@backstage/core-plugin-api@npm:1.8.1" dependencies: @@ -3330,7 +3198,7 @@ __metadata: languageName: node linkType: hard -"@backstage/dev-utils@npm:^1.0.23": +"@backstage/dev-utils@npm:^1.0.25": version: 1.0.25 resolution: "@backstage/dev-utils@npm:1.0.25" dependencies: @@ -3379,7 +3247,7 @@ __metadata: languageName: node linkType: hard -"@backstage/eslint-plugin@npm:^0.1.3, @backstage/eslint-plugin@npm:^0.1.4": +"@backstage/eslint-plugin@npm:^0.1.4": version: 0.1.4 resolution: "@backstage/eslint-plugin@npm:0.1.4" dependencies: @@ -3425,7 +3293,7 @@ __metadata: languageName: node linkType: hard -"@backstage/integration-react@npm:^1.1.21, @backstage/integration-react@npm:^1.1.22": +"@backstage/integration-react@npm:^1.1.22": version: 1.1.22 resolution: "@backstage/integration-react@npm:1.1.22" dependencies: @@ -3443,7 +3311,7 @@ __metadata: languageName: node linkType: hard -"@backstage/integration@npm:^1.7.2, @backstage/integration@npm:^1.8.0": +"@backstage/integration@npm:^1.8.0": version: 1.8.0 resolution: "@backstage/integration@npm:1.8.0" dependencies: @@ -3459,7 +3327,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-api-docs@npm:^0.10.0": +"@backstage/plugin-api-docs@npm:^0.10.2": version: 0.10.2 resolution: "@backstage/plugin-api-docs@npm:0.10.2" dependencies: @@ -3490,7 +3358,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-app-backend@npm:^0.3.55": +"@backstage/plugin-app-backend@npm:^0.3.56": version: 0.3.56 resolution: "@backstage/plugin-app-backend@npm:0.3.56" dependencies: @@ -3632,7 +3500,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-auth-backend@npm:^0.20.0": +"@backstage/plugin-auth-backend@npm:^0.20.2": version: 0.20.2 resolution: "@backstage/plugin-auth-backend@npm:0.20.2" dependencies: @@ -3692,7 +3560,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-auth-node@npm:^0.4.1, @backstage/plugin-auth-node@npm:^0.4.2": +"@backstage/plugin-auth-node@npm:^0.4.2": version: 0.4.2 resolution: "@backstage/plugin-auth-node@npm:0.4.2" dependencies: @@ -3717,7 +3585,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.1.4, @backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.1.5": +"@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:^0.1.5": version: 0.1.5 resolution: "@backstage/plugin-catalog-backend-module-scaffolder-entity-model@npm:0.1.5" dependencies: @@ -3731,7 +3599,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-backend@npm:^1.15.0": +"@backstage/plugin-catalog-backend@npm:^1.16.0": version: 1.16.0 resolution: "@backstage/plugin-catalog-backend@npm:1.16.0" dependencies: @@ -3777,7 +3645,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-common@npm:^1.0.18, @backstage/plugin-catalog-common@npm:^1.0.19": +"@backstage/plugin-catalog-common@npm:^1.0.19": version: 1.0.19 resolution: "@backstage/plugin-catalog-common@npm:1.0.19" dependencies: @@ -3788,7 +3656,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-graph@npm:^0.3.0": +"@backstage/plugin-catalog-graph@npm:^0.3.2": version: 0.3.2 resolution: "@backstage/plugin-catalog-graph@npm:0.3.2" dependencies: @@ -3816,7 +3684,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-import@npm:^0.10.2": +"@backstage/plugin-catalog-import@npm:^0.10.4": version: 0.10.4 resolution: "@backstage/plugin-catalog-import@npm:0.10.4" dependencies: @@ -3851,7 +3719,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-node@npm:^1.5.0, @backstage/plugin-catalog-node@npm:^1.6.0": +"@backstage/plugin-catalog-node@npm:^1.6.0": version: 1.6.0 resolution: "@backstage/plugin-catalog-node@npm:1.6.0" dependencies: @@ -3867,7 +3735,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog-react@npm:^1.9.0, @backstage/plugin-catalog-react@npm:^1.9.2": +"@backstage/plugin-catalog-react@npm:^1.9.2": version: 1.9.2 resolution: "@backstage/plugin-catalog-react@npm:1.9.2" dependencies: @@ -3904,7 +3772,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-catalog@npm:^1.15.0, @backstage/plugin-catalog@npm:^1.16.0": +"@backstage/plugin-catalog@npm:^1.16.0": version: 1.16.0 resolution: "@backstage/plugin-catalog@npm:1.16.0" dependencies: @@ -3953,7 +3821,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-github-actions@npm:^0.6.7": +"@backstage/plugin-github-actions@npm:^0.6.9": version: 0.6.9 resolution: "@backstage/plugin-github-actions@npm:0.6.9" dependencies: @@ -3980,31 +3848,87 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-kubernetes-backend@npm:^0.13.1": - version: 0.13.1 - resolution: "@backstage/plugin-kubernetes-backend@npm:0.13.1" +"@backstage/plugin-home-react@npm:^0.1.6": + version: 0.1.6 + resolution: "@backstage/plugin-home-react@npm:0.1.6" dependencies: - "@aws-crypto/sha256-js": "npm:^3.0.0" + "@backstage/core-components": "npm:^0.13.9" + "@backstage/core-plugin-api": "npm:^1.8.1" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@rjsf/utils": "npm:5.15.0" + "@types/react": "npm:^16.13.1 || ^17.0.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 0d305873648467227e8ef30a51219d647340cede2d4f54cc4f137546b7533d64cf8a049900355d678bef1409e3f44aca12cf9fcbfaa903fe32d4cfa689a4b284 + languageName: node + linkType: hard + +"@backstage/plugin-home@npm:^0.6.0": + version: 0.6.0 + resolution: "@backstage/plugin-home@npm:0.6.0" + dependencies: + "@backstage/catalog-client": "npm:^1.5.0" + "@backstage/catalog-model": "npm:^1.4.3" + "@backstage/config": "npm:^1.1.1" + "@backstage/core-app-api": "npm:^1.11.2" + "@backstage/core-compat-api": "npm:^0.1.0" + "@backstage/core-components": "npm:^0.13.9" + "@backstage/core-plugin-api": "npm:^1.8.1" + "@backstage/frontend-plugin-api": "npm:^0.4.0" + "@backstage/plugin-catalog-react": "npm:^1.9.2" + "@backstage/plugin-home-react": "npm:^0.1.6" + "@backstage/theme": "npm:^0.5.0" + "@backstage/types": "npm:^1.1.1" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@rjsf/core": "npm:5.15.0" + "@rjsf/material-ui": "npm:5.15.0" + "@rjsf/utils": "npm:5.15.0" + "@rjsf/validator-ajv8": "npm:5.15.0" + "@types/react": "npm:^16.13.1 || ^17.0.0" + lodash: "npm:^4.17.21" + luxon: "npm:^3.4.3" + react-grid-layout: "npm:1.3.4" + react-resizable: "npm:^3.0.4" + react-use: "npm:^17.2.4" + zod: "npm:^3.22.4" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 5ce6d855cc9f5690dbab4ff19528a503e60446bc296e80661620743f934ce64c10b545553b9372d39729f8c5552e79d3f63c4259afcec5bf37ebb67ea3c86619 + languageName: node + linkType: hard + +"@backstage/plugin-kubernetes-backend@npm:^0.14.0": + version: 0.14.0 + resolution: "@backstage/plugin-kubernetes-backend@npm:0.14.0" + dependencies: + "@aws-crypto/sha256-js": "npm:^5.0.0" "@aws-sdk/credential-providers": "npm:^3.350.0" "@aws-sdk/signature-v4": "npm:^3.347.0" - "@azure/identity": "npm:^3.2.1" - "@backstage/backend-common": "npm:^0.19.9" - "@backstage/backend-plugin-api": "npm:^0.6.7" - "@backstage/catalog-client": "npm:^1.4.6" + "@azure/identity": "npm:^4.0.0" + "@backstage/backend-common": "npm:^0.20.0" + "@backstage/backend-plugin-api": "npm:^0.6.8" + "@backstage/catalog-client": "npm:^1.5.0" "@backstage/catalog-model": "npm:^1.4.3" "@backstage/config": "npm:^1.1.1" "@backstage/errors": "npm:^1.2.3" "@backstage/integration-aws-node": "npm:^0.1.8" - "@backstage/plugin-auth-node": "npm:^0.4.1" - "@backstage/plugin-catalog-node": "npm:^1.5.0" - "@backstage/plugin-kubernetes-common": "npm:^0.7.1" - "@backstage/plugin-kubernetes-node": "npm:^0.1.1" - "@backstage/plugin-permission-common": "npm:^0.7.10" - "@backstage/plugin-permission-node": "npm:^0.7.18" + "@backstage/plugin-auth-node": "npm:^0.4.2" + "@backstage/plugin-catalog-node": "npm:^1.6.0" + "@backstage/plugin-kubernetes-common": "npm:^0.7.2" + "@backstage/plugin-kubernetes-node": "npm:^0.1.2" + "@backstage/plugin-permission-common": "npm:^0.7.11" + "@backstage/plugin-permission-node": "npm:^0.7.19" "@backstage/types": "npm:^1.1.1" - "@google-cloud/container": "npm:^4.0.0" + "@google-cloud/container": "npm:^5.0.0" "@jest-mock/express": "npm:^2.0.1" - "@kubernetes/client-node": "npm:0.19.0" + "@kubernetes/client-node": "npm:0.20.0" "@types/express": "npm:^4.17.6" "@types/http-proxy-middleware": "npm:^0.19.3" "@types/luxon": "npm:^3.0.0" @@ -4022,11 +3946,11 @@ __metadata: stream-buffers: "npm:^3.0.2" winston: "npm:^3.2.1" yn: "npm:^4.0.0" - checksum: 763518844a23a4e9b29c9ff920d5b507eb5ecf3989b7a12686c86c0b1a6026a4d53ed06aa70274c2acaa22c99ee353acb2b4882f35d4452b39aa44aa8adef28e + checksum: aa6ba4ba5c7e59ae9ba11a1124ee8d2504fc9afcce1b82cff8b7032983d052fef4532509b21b9550b549fb1db8e732f8094b118cb50bf159c98c03147716acc8 languageName: node linkType: hard -"@backstage/plugin-kubernetes-common@npm:^0.7.1, @backstage/plugin-kubernetes-common@npm:^0.7.2": +"@backstage/plugin-kubernetes-common@npm:^0.7.2": version: 0.7.2 resolution: "@backstage/plugin-kubernetes-common@npm:0.7.2" dependencies: @@ -4042,7 +3966,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-kubernetes-node@npm:^0.1.1": +"@backstage/plugin-kubernetes-node@npm:^0.1.2": version: 0.1.2 resolution: "@backstage/plugin-kubernetes-node@npm:0.1.2" dependencies: @@ -4086,7 +4010,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-kubernetes@npm:^0.11.2": +"@backstage/plugin-kubernetes@npm:^0.11.3": version: 0.11.3 resolution: "@backstage/plugin-kubernetes@npm:0.11.3" dependencies: @@ -4124,7 +4048,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-org@npm:^0.6.16": +"@backstage/plugin-org@npm:^0.6.18": version: 0.6.18 resolution: "@backstage/plugin-org@npm:0.6.18" dependencies: @@ -4151,7 +4075,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-permission-common@npm:^0.7.10, @backstage/plugin-permission-common@npm:^0.7.11": +"@backstage/plugin-permission-common@npm:^0.7.11": version: 0.7.11 resolution: "@backstage/plugin-permission-common@npm:0.7.11" dependencies: @@ -4165,7 +4089,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-permission-node@npm:^0.7.18, @backstage/plugin-permission-node@npm:^0.7.19": +"@backstage/plugin-permission-node@npm:^0.7.19": version: 0.7.19 resolution: "@backstage/plugin-permission-node@npm:0.7.19" dependencies: @@ -4184,7 +4108,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-permission-react@npm:^0.4.17, @backstage/plugin-permission-react@npm:^0.4.18": +"@backstage/plugin-permission-react@npm:^0.4.18": version: 0.4.18 resolution: "@backstage/plugin-permission-react@npm:0.4.18" dependencies: @@ -4203,7 +4127,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-proxy-backend@npm:^0.4.5": +"@backstage/plugin-proxy-backend@npm:^0.4.6": version: 0.4.6 resolution: "@backstage/plugin-proxy-backend@npm:0.4.6" dependencies: @@ -4304,7 +4228,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-scaffolder-backend@npm:^1.19.0": +"@backstage/plugin-scaffolder-backend@npm:^1.19.2": version: 1.19.2 resolution: "@backstage/plugin-scaffolder-backend@npm:1.19.2" dependencies: @@ -4432,7 +4356,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-scaffolder@npm:^1.16.0": +"@backstage/plugin-scaffolder@npm:^1.17.0": version: 1.17.0 resolution: "@backstage/plugin-scaffolder@npm:1.17.0" dependencies: @@ -4488,7 +4412,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-module-catalog@npm:^0.1.11, @backstage/plugin-search-backend-module-catalog@npm:^0.1.12": +"@backstage/plugin-search-backend-module-catalog@npm:^0.1.12": version: 0.1.12 resolution: "@backstage/plugin-search-backend-module-catalog@npm:0.1.12" dependencies: @@ -4508,7 +4432,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-module-pg@npm:^0.5.16": +"@backstage/plugin-search-backend-module-pg@npm:^0.5.17": version: 0.5.17 resolution: "@backstage/plugin-search-backend-module-pg@npm:0.5.17" dependencies: @@ -4525,7 +4449,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-module-techdocs@npm:^0.1.11, @backstage/plugin-search-backend-module-techdocs@npm:^0.1.12": +"@backstage/plugin-search-backend-module-techdocs@npm:^0.1.12": version: 0.1.12 resolution: "@backstage/plugin-search-backend-module-techdocs@npm:0.1.12" dependencies: @@ -4549,7 +4473,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend-node@npm:^1.2.11, @backstage/plugin-search-backend-node@npm:^1.2.12": +"@backstage/plugin-search-backend-node@npm:^1.2.12": version: 1.2.12 resolution: "@backstage/plugin-search-backend-node@npm:1.2.12" dependencies: @@ -4570,7 +4494,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-backend@npm:^1.4.7": +"@backstage/plugin-search-backend@npm:^1.4.8": version: 1.4.8 resolution: "@backstage/plugin-search-backend@npm:1.4.8" dependencies: @@ -4608,7 +4532,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search-react@npm:^1.7.2, @backstage/plugin-search-react@npm:^1.7.4": +"@backstage/plugin-search-react@npm:^1.7.4": version: 1.7.4 resolution: "@backstage/plugin-search-react@npm:1.7.4" dependencies: @@ -4634,7 +4558,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-search@npm:^1.4.2": +"@backstage/plugin-search@npm:^1.4.4": version: 1.4.4 resolution: "@backstage/plugin-search@npm:1.4.4" dependencies: @@ -4665,7 +4589,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-tech-radar@npm:^0.6.10": +"@backstage/plugin-tech-radar@npm:^0.6.11": version: 0.6.11 resolution: "@backstage/plugin-tech-radar@npm:0.6.11" dependencies: @@ -4689,7 +4613,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-techdocs-backend@npm:^1.9.0": +"@backstage/plugin-techdocs-backend@npm:^1.9.1": version: 1.9.1 resolution: "@backstage/plugin-techdocs-backend@npm:1.9.1" dependencies: @@ -4719,7 +4643,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-techdocs-module-addons-contrib@npm:^1.1.2": +"@backstage/plugin-techdocs-module-addons-contrib@npm:^1.1.3": version: 1.1.3 resolution: "@backstage/plugin-techdocs-module-addons-contrib@npm:1.1.3" dependencies: @@ -4780,7 +4704,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-techdocs-react@npm:^1.1.13, @backstage/plugin-techdocs-react@npm:^1.1.14": +"@backstage/plugin-techdocs-react@npm:^1.1.14": version: 1.1.14 resolution: "@backstage/plugin-techdocs-react@npm:1.1.14" dependencies: @@ -4805,7 +4729,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-techdocs@npm:^1.9.0": +"@backstage/plugin-techdocs@npm:^1.9.2": version: 1.9.2 resolution: "@backstage/plugin-techdocs@npm:1.9.2" dependencies: @@ -4843,7 +4767,7 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-user-settings@npm:^0.7.12": +"@backstage/plugin-user-settings@npm:^0.7.14": version: 0.7.14 resolution: "@backstage/plugin-user-settings@npm:0.7.14" dependencies: @@ -4879,7 +4803,7 @@ __metadata: languageName: node linkType: hard -"@backstage/test-utils@npm:^1.4.5": +"@backstage/test-utils@npm:^1.4.6": version: 1.4.6 resolution: "@backstage/test-utils@npm:1.4.6" dependencies: @@ -4905,22 +4829,6 @@ __metadata: languageName: node linkType: hard -"@backstage/theme@npm:^0.4.4": - version: 0.4.4 - resolution: "@backstage/theme@npm:0.4.4" - dependencies: - "@emotion/react": "npm:^11.10.5" - "@emotion/styled": "npm:^11.10.5" - "@mui/material": "npm:^5.12.2" - peerDependencies: - "@material-ui/core": ^4.12.2 - "@types/react": ^16.13.1 || ^17.0.0 - react: ^16.13.1 || ^17.0.0 || ^18.0.0 - react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 - checksum: af63b7d1ba89c04db5791dd3da964e7f90440787b91c7d1361d93c162c032251c10cee3d65d65f49f033f855cd8faa1fd7e3b0cd0394ed642faecc270ebb06dc - languageName: node - linkType: hard - "@backstage/theme@npm:^0.5.0": version: 0.5.0 resolution: "@backstage/theme@npm:0.5.0" @@ -5960,12 +5868,12 @@ __metadata: languageName: node linkType: hard -"@google-cloud/container@npm:^4.0.0": - version: 4.16.0 - resolution: "@google-cloud/container@npm:4.16.0" +"@google-cloud/container@npm:^5.0.0": + version: 5.4.0 + resolution: "@google-cloud/container@npm:5.4.0" dependencies: - google-gax: "npm:^3.5.8" - checksum: 2758769db0f8e2d29d3c0d8659f8cbfb41b7e2c00239fc348be905e7a39940aebe2f7e940409387e123e5e498bb4d804456a9263704ee6db4b7fc573d93e98e7 + google-gax: "npm:^4.0.3" + checksum: 3b71368eecfae39c15f8cffc01ae0dd1c1e34ff6cedd3c21d8f9afebf68ef406c1272e77fab23bfb77b2c7e36c5da4f5374b79c9dd82425e1ed961ab2fc08ef0 languageName: node linkType: hard @@ -6309,16 +6217,6 @@ __metadata: languageName: node linkType: hard -"@grpc/grpc-js@npm:~1.8.0": - version: 1.8.21 - resolution: "@grpc/grpc-js@npm:1.8.21" - dependencies: - "@grpc/proto-loader": "npm:^0.7.0" - "@types/node": "npm:>=12.12.47" - checksum: 8b669932149e88bef410f0b8805e7d51a79f9defa0539a024bb17ac9353191b2b747c6f2071b66947473e17f58625f856abb07f345b90c9206de06f7a7b0c303 - languageName: node - linkType: hard - "@grpc/grpc-js@npm:~1.9.6": version: 1.9.13 resolution: "@grpc/grpc-js@npm:1.9.13" @@ -6384,9 +6282,9 @@ __metadata: version: 0.0.0-use.local resolution: "@internal/plugin-radius-backend@workspace:plugins/plugin-radius-backend" dependencies: - "@backstage/backend-common": "npm:^0.19.9" + "@backstage/backend-common": "npm:^0.20.0" "@backstage/catalog-model": "npm:^1.4.3" - "@backstage/cli": "npm:^0.24.0" + "@backstage/cli": "npm:^0.25.0" "@backstage/config": "npm:^1.1.1" "@backstage/plugin-catalog-common": "npm:^1.0.19" "@backstage/plugin-catalog-node": "npm:^1.6.0" @@ -6406,23 +6304,31 @@ __metadata: version: 0.0.0-use.local resolution: "@internal/plugin-radius@workspace:plugins/plugin-radius" dependencies: - "@backstage/cli": "npm:^0.24.0" - "@backstage/core-app-api": "npm:^1.11.1" - "@backstage/core-components": "npm:^0.13.8" - "@backstage/core-plugin-api": "npm:^1.8.0" - "@backstage/dev-utils": "npm:^1.0.23" - "@backstage/test-utils": "npm:^1.4.5" - "@backstage/theme": "npm:^0.4.4" + "@backstage/cli": "npm:^0.25.0" + "@backstage/core-app-api": "npm:^1.11.2" + "@backstage/core-components": "npm:^0.13.9" + "@backstage/core-plugin-api": "npm:^1.8.1" + "@backstage/dev-utils": "npm:^1.0.25" + "@backstage/plugin-kubernetes": "npm:^0.11.3" + "@backstage/plugin-kubernetes-common": "npm:^0.7.2" + "@backstage/test-utils": "npm:^1.4.6" + "@backstage/theme": "npm:^0.5.0" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.61" + "@testing-library/dom": "npm:^8.0.0" "@testing-library/jest-dom": "npm:^5.10.1" "@testing-library/react": "npm:^12.1.3" "@testing-library/user-event": "npm:^14.0.0" + jest-canvas-mock: "npm:^2.5.2" msw: "npm:^1.0.0" + rad-components: "npm:^0.0.1" + react-error-boundary: "npm:^4.0.12" react-use: "npm:^17.2.4" peerDependencies: react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: ^6.3.0 languageName: unknown linkType: soft @@ -6763,7 +6669,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: 0c6b5ae663087558039052a626d2d7ed5208da36cfd707dcc5cea4a07cfc918248403dcb5989a8f7afaf245ce0573b7cc6fd94c4a30453bd10e44d9363940ba5 @@ -6797,15 +6703,6 @@ __metadata: languageName: node linkType: hard -"@jsdoc/salty@npm:^0.2.1": - version: 0.2.7 - resolution: "@jsdoc/salty@npm:0.2.7" - dependencies: - lodash: "npm:^4.17.21" - checksum: 12af140143ff4b3fdd9451f9d89cb5f4cb254e9ca91ac9dbd7ece1b338c490e602819965059e7fe2d9337d4cb44c9fe4fb27b88e65ac3821fed23ab88219f546 - languageName: node - linkType: hard - "@jsep-plugin/regex@npm:^1.0.1": version: 1.0.3 resolution: "@jsep-plugin/regex@npm:1.0.3" @@ -6891,32 +6788,6 @@ __metadata: languageName: node linkType: hard -"@kubernetes/client-node@npm:0.19.0": - version: 0.19.0 - resolution: "@kubernetes/client-node@npm:0.19.0" - dependencies: - "@types/js-yaml": "npm:^4.0.1" - "@types/node": "npm:^20.1.1" - "@types/request": "npm:^2.47.1" - "@types/ws": "npm:^8.5.3" - byline: "npm:^5.0.0" - isomorphic-ws: "npm:^5.0.0" - js-yaml: "npm:^4.1.0" - jsonpath-plus: "npm:^7.2.0" - openid-client: "npm:^5.3.0" - request: "npm:^2.88.0" - rfc4648: "npm:^1.3.0" - stream-buffers: "npm:^3.0.2" - tar: "npm:^6.1.11" - tslib: "npm:^2.4.1" - ws: "npm:^8.11.0" - dependenciesMeta: - openid-client: - optional: true - checksum: 1f78ce6a84e161d9f1536f10048537d4637e544165dc0880424a4127f707c67c972eacb6bcaa6113bdc8cd391a081bff858078facdd0c18aa73f36a516976652 - languageName: node - linkType: hard - "@kubernetes/client-node@npm:0.20.0, @kubernetes/client-node@npm:^0.20.0": version: 0.20.0 resolution: "@kubernetes/client-node@npm:0.20.0" @@ -9066,25 +8937,6 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-commonjs@npm:^23.0.0": - version: 23.0.7 - resolution: "@rollup/plugin-commonjs@npm:23.0.7" - dependencies: - "@rollup/pluginutils": "npm:^5.0.1" - commondir: "npm:^1.0.1" - estree-walker: "npm:^2.0.2" - glob: "npm:^8.0.3" - is-reference: "npm:1.2.1" - magic-string: "npm:^0.27.0" - peerDependencies: - rollup: ^2.68.0||^3.0.0 - peerDependenciesMeta: - rollup: - optional: true - checksum: 43055fcbb39d65edf9ea3a830bf388a61efdca2d747a33711fb6316e748be9856d90863fcfeb758e2b921eba52cb819cdf818b1374083c6a640a11dc71137e54 - languageName: node - linkType: hard - "@rollup/plugin-commonjs@npm:^25.0.0": version: 25.0.7 resolution: "@rollup/plugin-commonjs@npm:25.0.7" @@ -9104,20 +8956,6 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-json@npm:^5.0.0": - version: 5.0.2 - resolution: "@rollup/plugin-json@npm:5.0.2" - dependencies: - "@rollup/pluginutils": "npm:^5.0.1" - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 - peerDependenciesMeta: - rollup: - optional: true - checksum: 7df4082c149ded032575deccec81f0963e8b722400d2d50ff99c43117f0c075240137a6d6f724bc09e046a0aac380a87b5742470d8279c293b66a7ef401dff07 - languageName: node - linkType: hard - "@rollup/plugin-json@npm:^6.0.0": version: 6.1.0 resolution: "@rollup/plugin-json@npm:6.1.0" @@ -9132,22 +8970,6 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-node-resolve@npm:^13.0.6": - version: 13.3.0 - resolution: "@rollup/plugin-node-resolve@npm:13.3.0" - dependencies: - "@rollup/pluginutils": "npm:^3.1.0" - "@types/resolve": "npm:1.17.1" - deepmerge: "npm:^4.2.2" - is-builtin-module: "npm:^3.1.0" - is-module: "npm:^1.0.0" - resolve: "npm:^1.19.0" - peerDependencies: - rollup: ^2.42.0 - checksum: 6caa32a8304a20f1c9953111b25e9543f4de7d254958d81ce0158ad909e4493946bc2060c4ace23d9748b560ebc84c920ee7bc1b7d50dbf8ba852ef13c91af58 - languageName: node - linkType: hard - "@rollup/plugin-node-resolve@npm:^15.0.0": version: 15.2.3 resolution: "@rollup/plugin-node-resolve@npm:15.2.3" @@ -9183,19 +9005,6 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^3.1.0": - version: 3.1.0 - resolution: "@rollup/pluginutils@npm:3.1.0" - dependencies: - "@types/estree": "npm:0.0.39" - estree-walker: "npm:^1.0.1" - picomatch: "npm:^2.2.2" - peerDependencies: - rollup: ^1.20.0||^2.0.0 - checksum: 7151753160d15ba2b259461a6c25b3932150994ea52dba8fd3144f634c7647c2e56733d986e2c15de67c4d96a9ee7d6278efa6d2e626a7169898fd64adc0f90c - languageName: node - linkType: hard - "@rollup/pluginutils@npm:^4.1.1, @rollup/pluginutils@npm:^4.2.1": version: 4.2.1 resolution: "@rollup/pluginutils@npm:4.2.1" @@ -9917,7 +9726,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-utf8@npm:^2.0.2": +"@smithy/util-utf8@npm:^2.0.0, @smithy/util-utf8@npm:^2.0.2": version: 2.0.2 resolution: "@smithy/util-utf8@npm:2.0.2" dependencies: @@ -12647,13 +12456,6 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:0.0.39": - version: 0.0.39 - resolution: "@types/estree@npm:0.0.39" - checksum: f0af6c95ac1988c4827964bd9d3b51d24da442e2188943f6dfcb1e1559103d5d024d564b2e9d3f84c53714a02a0a7435c7441138eb63d9af5de4dfc66cdc0d92 - languageName: node - linkType: hard - "@types/estree@npm:^0.0.51": version: 0.0.51 resolution: "@types/estree@npm:0.0.51" @@ -12699,16 +12501,6 @@ __metadata: languageName: node linkType: hard -"@types/glob@npm:*": - version: 8.1.0 - resolution: "@types/glob@npm:8.1.0" - dependencies: - "@types/minimatch": "npm:^5.1.2" - "@types/node": "npm:*" - checksum: ded07aa0d7a1caf3c47b85e262be82989ccd7933b4a14712b79c82fd45a239249811d9fc3a135b3e9457afa163e74a297033d7245b0dc63cd3d032f3906b053f - languageName: node - linkType: hard - "@types/graceful-fs@npm:^4.1.3": version: 4.1.9 resolution: "@types/graceful-fs@npm:4.1.9" @@ -12884,13 +12676,6 @@ __metadata: languageName: node linkType: hard -"@types/linkify-it@npm:*": - version: 3.0.5 - resolution: "@types/linkify-it@npm:3.0.5" - checksum: 696e09975991c649ba37c5585714929fdebf5c64a8bfb99910613ef838337dbbba6c608fccdfa03d6347432586ef12e139bc0e947ae6fec569096fef5cc1c550 - languageName: node - linkType: hard - "@types/lodash@npm:^4.14.167, @types/lodash@npm:^4.14.175": version: 4.14.202 resolution: "@types/lodash@npm:4.14.202" @@ -12926,16 +12711,6 @@ __metadata: languageName: node linkType: hard -"@types/markdown-it@npm:^12.2.3": - version: 12.2.3 - resolution: "@types/markdown-it@npm:12.2.3" - dependencies: - "@types/linkify-it": "npm:*" - "@types/mdurl": "npm:*" - checksum: f72e08f69d76be2e30cd367fd6e5302c6878aa44e5b1a952fe7e41280044502bcb9bac8459ad94f6bb5e4f9c4cb52803950609ad66786f0fddc3a8bd533f777d - languageName: node - linkType: hard - "@types/mdast@npm:^3.0.0": version: 3.0.15 resolution: "@types/mdast@npm:3.0.15" @@ -12945,13 +12720,6 @@ __metadata: languageName: node linkType: hard -"@types/mdurl@npm:*": - version: 1.0.5 - resolution: "@types/mdurl@npm:1.0.5" - checksum: 8991c781eb94fb3621e48e191251a94057908fc14be60f52bdd7c48684af923ffa77559ea979450a0475f85c08f8a472f99ff9c2ca4308961b9b9d35fd7584f7 - languageName: node - linkType: hard - "@types/mdx@npm:^2.0.0": version: 2.0.10 resolution: "@types/mdx@npm:2.0.10" @@ -12987,13 +12755,6 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:^5.1.2": - version: 5.1.2 - resolution: "@types/minimatch@npm:5.1.2" - checksum: 83cf1c11748891b714e129de0585af4c55dd4c2cafb1f1d5233d79246e5e1e19d1b5ad9e8db449667b3ffa2b6c80125c429dbee1054e9efb45758dbc4e118562 - languageName: node - linkType: hard - "@types/ms@npm:*": version: 0.7.34 resolution: "@types/ms@npm:0.7.34" @@ -13230,15 +12991,6 @@ __metadata: languageName: node linkType: hard -"@types/resolve@npm:1.17.1": - version: 1.17.1 - resolution: "@types/resolve@npm:1.17.1" - dependencies: - "@types/node": "npm:*" - checksum: 6eeb9c27d99bf4b393bf168d43208f63e78cefca5644662a0bdb2bdbf8352386f4f3aca66add138fc41bce5f66fd48a0de430a1473f11b612fbed0375ae78031 - languageName: node - linkType: hard - "@types/resolve@npm:1.20.2": version: 1.20.2 resolution: "@types/resolve@npm:1.20.2" @@ -13269,16 +13021,6 @@ __metadata: languageName: node linkType: hard -"@types/rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "@types/rimraf@npm:3.0.2" - dependencies: - "@types/glob": "npm:*" - "@types/node": "npm:*" - checksum: 08beaf5d5ac6d6ecb76df74e3f873453feab079b5993f7cdd00bf2789bc2dea6917d5d24e75a5346fe201f396fa8a6eccb1291f97695997e34733f9663228a86 - languageName: node - linkType: hard - "@types/scheduler@npm:*": version: 0.16.8 resolution: "@types/scheduler@npm:0.16.8" @@ -13520,31 +13262,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.10.0" - dependencies: - "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.10.0" - "@typescript-eslint/type-utils": "npm:6.10.0" - "@typescript-eslint/utils": "npm:6.10.0" - "@typescript-eslint/visitor-keys": "npm:6.10.0" - debug: "npm:^4.3.4" - graphemer: "npm:^1.4.0" - ignore: "npm:^5.2.4" - natural-compare: "npm:^1.4.0" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" - peerDependencies: - "@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: f50b17cb753afbfc99549d38585eba8558949b977eb4661dd584e73ee946b3dbe944c9e3b12a233fa06b5e1c7d101730ac88a00c7a91b0a7f1e2c37a98e13c7a - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:^6.12.0, @typescript-eslint/eslint-plugin@npm:^6.14.0": version: 6.16.0 resolution: "@typescript-eslint/eslint-plugin@npm:6.16.0" @@ -13598,16 +13315,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/scope-manager@npm:6.10.0" - dependencies: - "@typescript-eslint/types": "npm:6.10.0" - "@typescript-eslint/visitor-keys": "npm:6.10.0" - checksum: a5fbee770d763852a7f426b950d495529139f1629fdcb30136c93f787acd82236db4272f78dff1d05a3a10a6406472ae95ae94ab75cfb618a06d75b8cc536cbf - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:6.16.0": version: 6.16.0 resolution: "@typescript-eslint/scope-manager@npm:6.16.0" @@ -13618,23 +13325,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/type-utils@npm:6.10.0" - dependencies: - "@typescript-eslint/typescript-estree": "npm:6.10.0" - "@typescript-eslint/utils": "npm:6.10.0" - debug: "npm:^4.3.4" - ts-api-utils: "npm:^1.0.1" - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: f7c425d4da4d53d78b3d6630216dc1f2809f8dcaed62dc3cf12252102a53103a2aa39a160b310ca1cedebf87b8c339013be0c2360710c7c836b775374730c10e - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:6.16.0": version: 6.16.0 resolution: "@typescript-eslint/type-utils@npm:6.16.0" @@ -13659,13 +13349,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/types@npm:6.10.0" - checksum: 30f47de625405b3729db6d26a0376d98628bd966c70ca01fab1adcef91bba810d27ce643d844e42d1cc77bb2c6277e62efe278a090da63ba748dfe5710c4757b - languageName: node - linkType: hard - "@typescript-eslint/types@npm:6.16.0": version: 6.16.0 resolution: "@typescript-eslint/types@npm:6.16.0" @@ -13691,24 +13374,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.10.0" - dependencies: - "@typescript-eslint/types": "npm:6.10.0" - "@typescript-eslint/visitor-keys": "npm:6.10.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" - peerDependenciesMeta: - typescript: - optional: true - checksum: ca28ca5a55e2d431c649ad093e4a4302f2b37c430bbeebbe622b05c727fd14dab136aead5a96848499d3ff4d187889733f8871b8dd5205d19bed4a260ad74544 - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:6.16.0": version: 6.16.0 resolution: "@typescript-eslint/typescript-estree@npm:6.16.0" @@ -13728,23 +13393,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/utils@npm:6.10.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.4.0" - "@types/json-schema": "npm:^7.0.12" - "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.10.0" - "@typescript-eslint/types": "npm:6.10.0" - "@typescript-eslint/typescript-estree": "npm:6.10.0" - semver: "npm:^7.5.4" - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: 809a1d08b154f76ed7a99edddf872369f6ed93987cea19a18cb9f12b8390bddcff9138d9d94955545da54488d59e0001054bec13baf6d858a1761b059480b887 - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:6.16.0, @typescript-eslint/utils@npm:^6.0.0, @typescript-eslint/utils@npm:^6.16.0": version: 6.16.0 resolution: "@typescript-eslint/utils@npm:6.16.0" @@ -13790,16 +13438,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.10.0": - version: 6.10.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.10.0" - dependencies: - "@typescript-eslint/types": "npm:6.10.0" - eslint-visitor-keys: "npm:^3.4.1" - checksum: f9223c148655ce00bb17db8aa92ee964e62c75d15095893e0b4d653c60a4033f456329b06de3eab4b404d8df359904f0dd6e3c8c842885c6d130e28ccd95ce03 - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:6.16.0": version: 6.16.0 resolution: "@typescript-eslint/visitor-keys@npm:6.16.0" @@ -14551,33 +14189,34 @@ __metadata: version: 0.0.0-use.local resolution: "app@workspace:packages/app" dependencies: - "@backstage/app-defaults": "npm:^1.4.5" + "@backstage/app-defaults": "npm:^1.4.6" "@backstage/catalog-model": "npm:^1.4.3" - "@backstage/cli": "npm:^0.24.0" - "@backstage/core-app-api": "npm:^1.11.1" - "@backstage/core-components": "npm:^0.13.8" - "@backstage/core-plugin-api": "npm:^1.8.0" - "@backstage/integration-react": "npm:^1.1.21" - "@backstage/plugin-api-docs": "npm:^0.10.0" - "@backstage/plugin-catalog": "npm:^1.15.0" - "@backstage/plugin-catalog-common": "npm:^1.0.18" - "@backstage/plugin-catalog-graph": "npm:^0.3.0" - "@backstage/plugin-catalog-import": "npm:^0.10.2" - "@backstage/plugin-catalog-react": "npm:^1.9.0" - "@backstage/plugin-github-actions": "npm:^0.6.7" - "@backstage/plugin-kubernetes": "npm:^0.11.2" - "@backstage/plugin-org": "npm:^0.6.16" - "@backstage/plugin-permission-react": "npm:^0.4.17" - "@backstage/plugin-scaffolder": "npm:^1.16.0" - "@backstage/plugin-search": "npm:^1.4.2" - "@backstage/plugin-search-react": "npm:^1.7.2" - "@backstage/plugin-tech-radar": "npm:^0.6.10" - "@backstage/plugin-techdocs": "npm:^1.9.0" - "@backstage/plugin-techdocs-module-addons-contrib": "npm:^1.1.2" - "@backstage/plugin-techdocs-react": "npm:^1.1.13" - "@backstage/plugin-user-settings": "npm:^0.7.12" - "@backstage/test-utils": "npm:^1.4.5" - "@backstage/theme": "npm:^0.4.4" + "@backstage/cli": "npm:^0.25.0" + "@backstage/core-app-api": "npm:^1.11.2" + "@backstage/core-components": "npm:^0.13.9" + "@backstage/core-plugin-api": "npm:^1.8.1" + "@backstage/integration-react": "npm:^1.1.22" + "@backstage/plugin-api-docs": "npm:^0.10.2" + "@backstage/plugin-catalog": "npm:^1.16.0" + "@backstage/plugin-catalog-common": "npm:^1.0.19" + "@backstage/plugin-catalog-graph": "npm:^0.3.2" + "@backstage/plugin-catalog-import": "npm:^0.10.4" + "@backstage/plugin-catalog-react": "npm:^1.9.2" + "@backstage/plugin-github-actions": "npm:^0.6.9" + "@backstage/plugin-home": "npm:^0.6.0" + "@backstage/plugin-kubernetes": "npm:^0.11.3" + "@backstage/plugin-org": "npm:^0.6.18" + "@backstage/plugin-permission-react": "npm:^0.4.18" + "@backstage/plugin-scaffolder": "npm:^1.17.0" + "@backstage/plugin-search": "npm:^1.4.4" + "@backstage/plugin-search-react": "npm:^1.7.4" + "@backstage/plugin-tech-radar": "npm:^0.6.11" + "@backstage/plugin-techdocs": "npm:^1.9.2" + "@backstage/plugin-techdocs-module-addons-contrib": "npm:^1.1.3" + "@backstage/plugin-techdocs-react": "npm:^1.1.14" + "@backstage/plugin-user-settings": "npm:^0.7.14" + "@backstage/test-utils": "npm:^1.4.6" + "@backstage/theme": "npm:^0.5.0" "@internal/plugin-radius": "npm:^0.1.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" @@ -14586,7 +14225,8 @@ __metadata: "@testing-library/jest-dom": "npm:^5.10.1" "@testing-library/react": "npm:^12.1.3" "@testing-library/user-event": "npm:^14.0.0" - "@types/react-dom": "npm:*" + "@types/react": "npm:^17" + "@types/react-dom": "npm:^17" cross-env: "npm:^7.0.0" history: "npm:^5.0.0" jest-canvas-mock: "npm:^2.5.2" @@ -15270,28 +14910,29 @@ __metadata: version: 0.0.0-use.local resolution: "backend@workspace:packages/backend" dependencies: - "@backstage/backend-common": "npm:^0.19.9" - "@backstage/backend-tasks": "npm:^0.5.12" - "@backstage/catalog-client": "npm:^1.4.6" + "@backstage/backend-common": "npm:^0.20.0" + "@backstage/backend-tasks": "npm:^0.5.13" + "@backstage/catalog-client": "npm:^1.5.1" "@backstage/catalog-model": "npm:^1.4.3" - "@backstage/cli": "npm:^0.24.0" + "@backstage/cli": "npm:^0.25.0" "@backstage/config": "npm:^1.1.1" - "@backstage/plugin-app-backend": "npm:^0.3.55" - "@backstage/plugin-auth-backend": "npm:^0.20.0" - "@backstage/plugin-auth-node": "npm:^0.4.1" - "@backstage/plugin-catalog-backend": "npm:^1.15.0" - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.1.4" - "@backstage/plugin-kubernetes-backend": "npm:^0.13.1" - "@backstage/plugin-permission-common": "npm:^0.7.10" - "@backstage/plugin-permission-node": "npm:^0.7.18" - "@backstage/plugin-proxy-backend": "npm:^0.4.5" - "@backstage/plugin-scaffolder-backend": "npm:^1.19.0" - "@backstage/plugin-search-backend": "npm:^1.4.7" - "@backstage/plugin-search-backend-module-catalog": "npm:^0.1.11" - "@backstage/plugin-search-backend-module-pg": "npm:^0.5.16" - "@backstage/plugin-search-backend-module-techdocs": "npm:^0.1.11" - "@backstage/plugin-search-backend-node": "npm:^1.2.11" - "@backstage/plugin-techdocs-backend": "npm:^1.9.0" + "@backstage/plugin-app-backend": "npm:^0.3.56" + "@backstage/plugin-auth-backend": "npm:^0.20.2" + "@backstage/plugin-auth-node": "npm:^0.4.2" + "@backstage/plugin-catalog-backend": "npm:^1.16.0" + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "npm:^0.1.5" + "@backstage/plugin-kubernetes-backend": "npm:^0.14.0" + "@backstage/plugin-kubernetes-node": "npm:^0.1.2" + "@backstage/plugin-permission-common": "npm:^0.7.11" + "@backstage/plugin-permission-node": "npm:^0.7.19" + "@backstage/plugin-proxy-backend": "npm:^0.4.6" + "@backstage/plugin-scaffolder-backend": "npm:^1.19.2" + "@backstage/plugin-search-backend": "npm:^1.4.8" + "@backstage/plugin-search-backend-module-catalog": "npm:^0.1.12" + "@backstage/plugin-search-backend-module-pg": "npm:^0.5.17" + "@backstage/plugin-search-backend-module-techdocs": "npm:^0.1.12" + "@backstage/plugin-search-backend-node": "npm:^1.2.12" + "@backstage/plugin-techdocs-backend": "npm:^1.9.1" "@internal/plugin-radius-backend": "npm:^0.1.0" "@types/dockerode": "npm:^3.3.0" "@types/express": "npm:^4.17.6" @@ -15395,19 +15036,6 @@ __metadata: languageName: node linkType: hard -"bfj@npm:^7.0.2": - version: 7.1.0 - resolution: "bfj@npm:7.1.0" - dependencies: - bluebird: "npm:^3.7.2" - check-types: "npm:^11.2.3" - hoopy: "npm:^0.1.4" - jsonpath: "npm:^1.1.1" - tryer: "npm:^1.0.1" - checksum: e5fc6690cd093c06ca6ed7584a2caf0c4a762bc9d9d9cb18efbabc75c973b071a8dad7037c617d0ea4d97b7b439821fea32f7c232ed0be8fa7840533a9643171 - languageName: node - linkType: hard - "bfj@npm:^8.0.0": version: 8.0.0 resolution: "bfj@npm:8.0.0" @@ -16018,15 +15646,6 @@ __metadata: languageName: node linkType: hard -"catharsis@npm:^0.9.0": - version: 0.9.0 - resolution: "catharsis@npm:0.9.0" - dependencies: - lodash: "npm:^4.17.15" - checksum: 9ac03ca48154ac63cfdb6c1645481d9d04f3c3e0dea131debf3116a0c12aa47e8864be7dcf770932c46d75bdd844a99f0c116c234e57232ad1f427751498e7ed - languageName: node - linkType: hard - "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -16336,7 +15955,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.2.1": +"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.1, clsx@npm:^1.2.1": version: 1.2.1 resolution: "clsx@npm:1.2.1" checksum: 34dead8bee24f5e96f6e7937d711978380647e936a22e76380290e35486afd8634966ce300fc4b74a32f3762c7d4c0303f442c3e259f4ce02374eb0c82834f27 @@ -18776,7 +18395,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0, esbuild@npm:~0.18.20": +"esbuild@npm:^0.18.0": version: 0.18.20 resolution: "esbuild@npm:0.18.20" dependencies: @@ -18975,7 +18594,7 @@ __metadata: languageName: node linkType: hard -"escodegen@npm:^1.13.0, escodegen@npm:^1.8.1": +"escodegen@npm:^1.8.1": version: 1.14.3 resolution: "escodegen@npm:1.14.3" dependencies: @@ -19344,7 +18963,7 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.0.0, espree@npm:^9.6.0, espree@npm:^9.6.1": +"espree@npm:^9.6.0, espree@npm:^9.6.1": version: 9.6.1 resolution: "espree@npm:9.6.1" dependencies: @@ -19414,13 +19033,6 @@ __metadata: languageName: node linkType: hard -"estree-walker@npm:^1.0.1": - version: 1.0.1 - resolution: "estree-walker@npm:1.0.1" - checksum: fa9e5f8c1bbe8d01e314c0f03067b64a4f22d4c58410fc5237060d0c15b81e58c23921c41acc60abbdab490f1fdfcbd6408ede2d03ca704454272e0244d61a55 - languageName: node - linkType: hard - "estree-walker@npm:^2.0.1, estree-walker@npm:^2.0.2": version: 2.0.2 resolution: "estree-walker@npm:2.0.2" @@ -19826,7 +19438,7 @@ __metadata: languageName: node linkType: hard -"fast-text-encoding@npm:^1.0.0, fast-text-encoding@npm:^1.0.3": +"fast-text-encoding@npm:^1.0.0": version: 1.0.6 resolution: "fast-text-encoding@npm:1.0.6" checksum: e1d0381bda229c92c7906f63308f3b9caca8c78b732768b1ee16f560089ed21bc159bbe1434138ccd3815931ec8d4785bdade1ad1c45accfdf27ac6606ac67d2 @@ -20947,7 +20559,7 @@ __metadata: languageName: node linkType: hard -"google-auth-library@npm:^8.0.0, google-auth-library@npm:^8.0.2": +"google-auth-library@npm:^8.0.0": version: 8.9.0 resolution: "google-auth-library@npm:8.9.0" dependencies: @@ -20978,33 +20590,7 @@ __metadata: languageName: node linkType: hard -"google-gax@npm:^3.5.8": - version: 3.6.1 - resolution: "google-gax@npm:3.6.1" - dependencies: - "@grpc/grpc-js": "npm:~1.8.0" - "@grpc/proto-loader": "npm:^0.7.0" - "@types/long": "npm:^4.0.0" - "@types/rimraf": "npm:^3.0.2" - abort-controller: "npm:^3.0.0" - duplexify: "npm:^4.0.0" - fast-text-encoding: "npm:^1.0.3" - google-auth-library: "npm:^8.0.2" - is-stream-ended: "npm:^0.1.4" - node-fetch: "npm:^2.6.1" - object-hash: "npm:^3.0.0" - proto3-json-serializer: "npm:^1.0.0" - protobufjs: "npm:7.2.4" - protobufjs-cli: "npm:1.1.1" - retry-request: "npm:^5.0.0" - bin: - compileProtos: build/tools/compileProtos.js - minifyProtoJson: build/tools/minify.js - checksum: a935b7fac764fbf89b613d0a345ac9e531901fcd0223ec738c3e6ce29e9326603aca473326fe60216e04fc8adb7439dd5715f0f3b6b8200cd5f16c7fb4d7d3e7 - languageName: node - linkType: hard - -"google-gax@npm:^4.0.4": +"google-gax@npm:^4.0.3, google-gax@npm:^4.0.4": version: 4.0.5 resolution: "google-gax@npm:4.0.5" dependencies: @@ -21062,7 +20648,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -22181,7 +21767,7 @@ __metadata: languageName: node linkType: hard -"is-builtin-module@npm:^3.1.0, is-builtin-module@npm:^3.2.1": +"is-builtin-module@npm:^3.2.1": version: 3.2.1 resolution: "is-builtin-module@npm:3.2.1" dependencies: @@ -22498,13 +22084,6 @@ __metadata: languageName: node linkType: hard -"is-stream-ended@npm:^0.1.4": - version: 0.1.4 - resolution: "is-stream-ended@npm:0.1.4" - checksum: fa4136d91d44f54aabeedd7b8072e03e0e4a6dac4cd47000152781ccad6451787e39ae5db15e7400a261e4d8ef976713237d49c773856548dbf171cc82893afc - languageName: node - linkType: hard - "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -23408,15 +22987,6 @@ __metadata: languageName: node linkType: hard -"js2xmlparser@npm:^4.0.2": - version: 4.0.2 - resolution: "js2xmlparser@npm:4.0.2" - dependencies: - xmlcreate: "npm:^2.0.4" - checksum: b00de9351649d67d225e21734a08f456a4ecb3c29cafcd3bbecb36a8ab61ec841fad7f425bed50e21936fe387f472e49cfe75ce71d0beaacb0475b077c88ed39 - languageName: node - linkType: hard - "jsbn@npm:~0.1.0": version: 0.1.1 resolution: "jsbn@npm:0.1.1" @@ -23459,31 +23029,6 @@ __metadata: languageName: node linkType: hard -"jsdoc@npm:^4.0.0": - version: 4.0.2 - resolution: "jsdoc@npm:4.0.2" - dependencies: - "@babel/parser": "npm:^7.20.15" - "@jsdoc/salty": "npm:^0.2.1" - "@types/markdown-it": "npm:^12.2.3" - bluebird: "npm:^3.7.2" - catharsis: "npm:^0.9.0" - escape-string-regexp: "npm:^2.0.0" - js2xmlparser: "npm:^4.0.2" - klaw: "npm:^3.0.0" - markdown-it: "npm:^12.3.2" - markdown-it-anchor: "npm:^8.4.1" - marked: "npm:^4.0.10" - mkdirp: "npm:^1.0.4" - requizzle: "npm:^0.2.3" - strip-json-comments: "npm:^3.1.0" - underscore: "npm:~1.13.2" - bin: - jsdoc: jsdoc.js - checksum: 1320a49ea576e60cfe38e5912e0b6aab22e3861a76c1795afde2e02896b29e343abee4da0de6b82f1edb6ef6b6c4fc8e2ef41d0fe65a3522138b28b74b01e5c2 - languageName: node - linkType: hard - "jsdom@npm:^16.5.2": version: 16.7.0 resolution: "jsdom@npm:16.7.0" @@ -24078,15 +23623,6 @@ __metadata: languageName: node linkType: hard -"klaw@npm:^3.0.0": - version: 3.0.0 - resolution: "klaw@npm:3.0.0" - dependencies: - graceful-fs: "npm:^4.1.9" - checksum: 8391cf6df6337dce02e44628b620b39412d007eff162d907d37063c23986041d9b5c3558851d473c2fae92c1ccb0fde8864e36f9c55ac339fc469b517a2caa1b - languageName: node - linkType: hard - "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -24454,6 +23990,13 @@ __metadata: languageName: node linkType: hard +"lodash.isequal@npm:^4.0.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: dfdb2356db19631a4b445d5f37868a095e2402292d59539a987f134a8778c62a2810c2452d11ae9e6dcac71fc9de40a6fedcb20e2952a15b431ad8b29e50e28f + languageName: node + linkType: hard + "lodash.isinteger@npm:^4.0.4": version: 4.0.4 resolution: "lodash.isinteger@npm:4.0.4" @@ -24668,7 +24211,7 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.0.0": +"luxon@npm:^3.0.0, luxon@npm:^3.4.3": version: 3.4.4 resolution: "luxon@npm:3.4.4" checksum: 02e26a0b039c11fd5b75e1d734c8f0332c95510f6a514a9a0991023e43fb233884da02d7f966823ffb230632a733fc86d4a4b1e63c3fbe00058b8ee0f8c728af @@ -24700,15 +24243,6 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.27.0": - version: 0.27.0 - resolution: "magic-string@npm:0.27.0" - dependencies: - "@jridgewell/sourcemap-codec": "npm:^1.4.13" - checksum: cddacfea14441ca57ae8a307bc3cf90bac69efaa4138dd9a80804cffc2759bf06f32da3a293fb13eaa96334b7d45b7768a34f1d226afae25d2f05b05a3bb37d8 - languageName: node - linkType: hard - "magic-string@npm:^0.30.3, magic-string@npm:^0.30.5": version: 0.30.5 resolution: "magic-string@npm:0.30.5" @@ -24821,17 +24355,7 @@ __metadata: languageName: node linkType: hard -"markdown-it-anchor@npm:^8.4.1": - version: 8.6.7 - resolution: "markdown-it-anchor@npm:8.6.7" - peerDependencies: - "@types/markdown-it": "*" - markdown-it: "*" - checksum: f117866488013b7e4085a6b59d12bf62879181aef65ea2851f01ed1b763b8c052580c2c27fa8bd009421886220c6beeb373a65af9e885ce63a36ee9f8dcd0e89 - languageName: node - linkType: hard - -"markdown-it@npm:^12.2.0, markdown-it@npm:^12.3.2": +"markdown-it@npm:^12.2.0": version: 12.3.2 resolution: "markdown-it@npm:12.3.2" dependencies: @@ -24862,7 +24386,7 @@ __metadata: languageName: node linkType: hard -"marked@npm:^4.0.10, marked@npm:^4.0.14": +"marked@npm:^4.0.14": version: 4.3.0 resolution: "marked@npm:4.3.0" bin: @@ -28540,7 +28064,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.7, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:15.x, prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.7, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -28574,15 +28098,6 @@ __metadata: languageName: node linkType: hard -"proto3-json-serializer@npm:^1.0.0": - version: 1.1.1 - resolution: "proto3-json-serializer@npm:1.1.1" - dependencies: - protobufjs: "npm:^7.0.0" - checksum: bc8de2a4798bd344084a7e710421ffd9a4c6b909e8f724b213b7a93d7818c98beed03a93d3e40f9897609e4390d3e47129ae5a2a1c0908e7167a4d34d52b960b - languageName: node - linkType: hard - "proto3-json-serializer@npm:^2.0.0": version: 2.0.0 resolution: "proto3-json-serializer@npm:2.0.0" @@ -28592,49 +28107,6 @@ __metadata: languageName: node linkType: hard -"protobufjs-cli@npm:1.1.1": - version: 1.1.1 - resolution: "protobufjs-cli@npm:1.1.1" - dependencies: - chalk: "npm:^4.0.0" - escodegen: "npm:^1.13.0" - espree: "npm:^9.0.0" - estraverse: "npm:^5.1.0" - glob: "npm:^8.0.0" - jsdoc: "npm:^4.0.0" - minimist: "npm:^1.2.0" - semver: "npm:^7.1.2" - tmp: "npm:^0.2.1" - uglify-js: "npm:^3.7.7" - peerDependencies: - protobufjs: ^7.0.0 - bin: - pbjs: bin/pbjs - pbts: bin/pbts - checksum: 50ab15abf741e7008d2bd88881ac5760d33c07bbe1b28f5460bf74722c2f152c35671b77b5365fc3e6b83e392b44c2e354b227c307fdd870598d7220214b5f87 - languageName: node - linkType: hard - -"protobufjs@npm:7.2.4": - version: 7.2.4 - resolution: "protobufjs@npm:7.2.4" - dependencies: - "@protobufjs/aspromise": "npm:^1.1.2" - "@protobufjs/base64": "npm:^1.1.2" - "@protobufjs/codegen": "npm:^2.0.4" - "@protobufjs/eventemitter": "npm:^1.1.0" - "@protobufjs/fetch": "npm:^1.1.0" - "@protobufjs/float": "npm:^1.0.2" - "@protobufjs/inquire": "npm:^1.1.0" - "@protobufjs/path": "npm:^1.1.2" - "@protobufjs/pool": "npm:^1.1.0" - "@protobufjs/utf8": "npm:^1.1.0" - "@types/node": "npm:>=13.7.0" - long: "npm:^5.0.0" - checksum: 7610b4e3b961e7637e8f61099931161af7f47f8b88b4a0d55124158f9b7fbe6e8fdfe551803b0c65fefad9d58d49332f0b5b4aa5826d8dcd8b062c351cbe2ebb - languageName: node - linkType: hard - "protobufjs@npm:7.2.5, protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5": version: 7.2.5 resolution: "protobufjs@npm:7.2.5" @@ -28856,10 +28328,11 @@ __metadata: languageName: node linkType: hard -"rad-components@workspace:packages/rad-components": +"rad-components@npm:^0.0.1, rad-components@workspace:packages/rad-components": version: 0.0.0-use.local resolution: "rad-components@workspace:packages/rad-components" dependencies: + "@babel/core": "npm:^7.23.0" "@babel/preset-env": "npm:^7.23.6" "@babel/preset-react": "npm:^7.23.3" "@backstage/cli": "npm:^0.25.0" @@ -29205,6 +28678,19 @@ __metadata: languageName: node linkType: hard +"react-draggable@npm:^4.0.0, react-draggable@npm:^4.0.3": + version: 4.4.6 + resolution: "react-draggable@npm:4.4.6" + dependencies: + clsx: "npm:^1.1.1" + prop-types: "npm:^15.8.1" + peerDependencies: + react: ">= 16.3.0" + react-dom: ">= 16.3.0" + checksum: 1e8cf47414a8554caa68447e5f27749bc40e1eabb4806e2dadcb39ab081d263f517d6aaec5231677e6b425603037c7e3386d1549898f9ffcc98a86cabafb2b9a + languageName: node + linkType: hard + "react-element-to-jsx-string@npm:^15.0.0": version: 15.0.0 resolution: "react-element-to-jsx-string@npm:15.0.0" @@ -29219,6 +28705,17 @@ __metadata: languageName: node linkType: hard +"react-error-boundary@npm:^4.0.12": + version: 4.0.12 + resolution: "react-error-boundary@npm:4.0.12" + dependencies: + "@babel/runtime": "npm:^7.12.5" + peerDependencies: + react: ">=16.13.1" + checksum: de599799efab78929447e11f4cfb3779e99ca87a8af1b3c56ab49ecb0bcc44fe924cfad4d2f3cc77c6cc8d992e3c47e356c70edebb825ae867aa621438035209 + languageName: node + linkType: hard + "react-error-overlay@npm:^6.0.11": version: 6.0.11 resolution: "react-error-overlay@npm:6.0.11" @@ -29233,6 +28730,22 @@ __metadata: languageName: node linkType: hard +"react-grid-layout@npm:1.3.4": + version: 1.3.4 + resolution: "react-grid-layout@npm:1.3.4" + dependencies: + clsx: "npm:^1.1.1" + lodash.isequal: "npm:^4.0.0" + prop-types: "npm:^15.8.1" + react-draggable: "npm:^4.0.0" + react-resizable: "npm:^3.0.4" + peerDependencies: + react: ">= 16.3.0" + react-dom: ">= 16.3.0" + checksum: 2c4a9ca1284cf6a618070aeccf8ffb8d2d91798452f7606395a4524bda27fad82ba9c818cb3e420d617fec8aed93c0caaae060c714d21a929c6f5c75727697b7 + languageName: node + linkType: hard + "react-helmet@npm:6.1.0": version: 6.1.0 resolution: "react-helmet@npm:6.1.0" @@ -29446,6 +28959,18 @@ __metadata: languageName: node linkType: hard +"react-resizable@npm:^3.0.4": + version: 3.0.5 + resolution: "react-resizable@npm:3.0.5" + dependencies: + prop-types: "npm:15.x" + react-draggable: "npm:^4.0.3" + peerDependencies: + react: ">= 16.3" + checksum: cfe50aa6efb79e0aa09bd681a5beab2fcd1186737c4952eb4c3974ed9395d5d263ccd1130961d06b8f5e24c8f544dd2967b5c740ce68719962d1771de7bdb350 + languageName: node + linkType: hard + "react-router-dom@npm:^6.3.0": version: 6.21.1 resolution: "react-router-dom@npm:6.21.1" @@ -30104,15 +29629,6 @@ __metadata: languageName: node linkType: hard -"requizzle@npm:^0.2.3": - version: 0.2.4 - resolution: "requizzle@npm:0.2.4" - dependencies: - lodash: "npm:^4.17.21" - checksum: ad138f987943aeda5f96cd1ccba9752c96352a729a7e3c3e2545568703f7fc9b978d9b46715803408ef178b0d61d36a4b1b506b367b7e78fe6d041fa5bfa5e06 - languageName: node - linkType: hard - "reselect@npm:^4.1.8": version: 4.1.8 resolution: "reselect@npm:4.1.8" @@ -30256,16 +29772,6 @@ __metadata: languageName: node linkType: hard -"retry-request@npm:^5.0.0": - version: 5.0.2 - resolution: "retry-request@npm:5.0.2" - dependencies: - debug: "npm:^4.1.1" - extend: "npm:^3.0.2" - checksum: 06de24fd2f08a3d7985ad12d5993a5772dd0a4e0a079577ad63c0ce9b4005fcf464c8b0b215b732bede995f326ac0408c0fa04658736c8ffae5adde5b0194ed9 - languageName: node - linkType: hard - "retry-request@npm:^7.0.0": version: 7.0.1 resolution: "retry-request@npm:7.0.1" @@ -30328,7 +29834,7 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": +"rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" dependencies: @@ -30456,7 +29962,7 @@ __metadata: version: 0.0.0-use.local resolution: "root@workspace:." dependencies: - "@backstage/cli": "npm:^0.24.0" + "@backstage/cli": "npm:^0.25.0" "@backstage/e2e-test-utils": "npm:^0.1.0" "@playwright/test": "npm:^1.32.3" "@spotify/prettier-config": "npm:^12.0.0" @@ -30727,7 +30233,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4": +"semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -31140,7 +30646,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.5.16, source-map-support@npm:^0.5.21, source-map-support@npm:~0.5.20": +"source-map-support@npm:^0.5.16, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -31798,7 +31304,7 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:^3.0.1, strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:^3.0.1, strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd @@ -32431,15 +31937,6 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.1": - version: 0.2.1 - resolution: "tmp@npm:0.2.1" - dependencies: - rimraf: "npm:^3.0.0" - checksum: 67607aa012059c9ce697bee820ee51bc0f39b29a8766def4f92d3f764d67c7cf9205d537d24e0cb1ce9685c40d4c628ead010910118ea18348666b5c46ed9123 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -32772,23 +32269,6 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^3.14.0": - version: 3.14.0 - resolution: "tsx@npm:3.14.0" - dependencies: - esbuild: "npm:~0.18.20" - fsevents: "npm:~2.3.3" - get-tsconfig: "npm:^4.7.2" - source-map-support: "npm:^0.5.21" - dependenciesMeta: - fsevents: - optional: true - bin: - tsx: dist/cli.mjs - checksum: b6c938bdae9c656aef2aa0130ee6aa8f3487b5d411d5f7934b705c28ff44ab268db3dde123cf5237b4e5e2ab4441a0bad4b1a39e3ff2170d138538e44082f05d - languageName: node - linkType: hard - "tsx@npm:^4.0.0": version: 4.7.0 resolution: "tsx@npm:4.7.0" @@ -33092,7 +32572,7 @@ __metadata: languageName: node linkType: hard -"uglify-js@npm:^3.1.4, uglify-js@npm:^3.7.7, uglify-js@npm:x": +"uglify-js@npm:^3.1.4, uglify-js@npm:x": version: 3.17.4 resolution: "uglify-js@npm:3.17.4" bin: @@ -33150,7 +32630,7 @@ __metadata: languageName: node linkType: hard -"underscore@npm:^1.12.1, underscore@npm:~1.13.2": +"underscore@npm:^1.12.1": version: 1.13.6 resolution: "underscore@npm:1.13.6" checksum: 5f57047f47273044c045fddeb8b141dafa703aa487afd84b319c2495de2e685cecd0b74abec098292320d518b267c0c4598e45aa47d4c3628d0d4020966ba521 @@ -34577,13 +34057,6 @@ __metadata: languageName: node linkType: hard -"xmlcreate@npm:^2.0.4": - version: 2.0.4 - resolution: "xmlcreate@npm:2.0.4" - checksum: fc4234e2d1942877d761d4f3d64410b54633d2ec60b13a5d56a6a06545aba39a0df8ed7ded10785a302f632eb4f0a4fedbf4bf10e17892e11d5075244b9e5705 - languageName: node - linkType: hard - "xpath@npm:0.0.32": version: 0.0.32 resolution: "xpath@npm:0.0.32" @@ -34777,7 +34250,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.21.4, zod@npm:^3.22.4": +"zod@npm:^3.22.4": version: 3.22.4 resolution: "zod@npm:3.22.4" checksum: 7578ab283dac0eee66a0ad0fc4a7f28c43e6745aadb3a529f59a4b851aa10872b3890398b3160f257f4b6817b4ce643debdda4fb21a2c040adda7862cab0a587