From 5a97d446f63397a0487614463ae20a00bded7cc4 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 13:26:06 +0200 Subject: [PATCH 01/24] feat: the app plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Co-authored-by: Camila Belo Co-authored-by: Camila Belo Signed-off-by: blam --- packages/app/package.json | 1 + packages/app/src/App.tsx | 2 + plugins/app-backend/package.json | 1 + plugins/app-node/package.json | 1 + plugins/app/.eslintrc.js | 1 + plugins/app/README.md | 13 +++ plugins/app/catalog-info.yaml | 9 ++ plugins/app/package.json | 61 ++++++++++++ plugins/app/src/index.ts | 17 ++++ plugins/app/src/plugin.test.ts | 22 +++++ plugins/app/src/plugin.ts | 21 ++++ plugins/app/src/setupTests.ts | 16 ++++ yarn.lock | 160 ++++++++++++++++++++++++++----- 13 files changed, 303 insertions(+), 22 deletions(-) create mode 100644 plugins/app/.eslintrc.js create mode 100644 plugins/app/README.md create mode 100644 plugins/app/catalog-info.yaml create mode 100644 plugins/app/package.json create mode 100644 plugins/app/src/index.ts create mode 100644 plugins/app/src/plugin.test.ts create mode 100644 plugins/app/src/plugin.ts create mode 100644 plugins/app/src/setupTests.ts diff --git a/packages/app/package.json b/packages/app/package.json index c27540f63e..8ef8a54221 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -45,6 +45,7 @@ "@backstage/frontend-app-api": "workspace:^", "@backstage/integration-react": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", + "@backstage/plugin-app": "^0.0.0", "@backstage/plugin-auth-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index ba556e8616..cc7512464a 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -82,6 +82,7 @@ import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; import { DevToolsPage } from '@backstage/plugin-devtools'; import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unprocessed-entities'; import { NotificationsPage } from '@backstage/plugin-notifications'; +import { AppPage } from '@backstage/plugin-app'; const app = createApp({ apis, @@ -208,6 +209,7 @@ const routes = ( {customDevToolsPage} } /> + } /> ); diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index b7fbef8b46..1a302aadc7 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -6,6 +6,7 @@ "role": "backend-plugin", "pluginId": "app", "pluginPackages": [ + "@backstage/plugin-app", "@backstage/plugin-app-backend", "@backstage/plugin-app-node" ] diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 4e28a853ed..b8ee9dd437 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -6,6 +6,7 @@ "role": "node-library", "pluginId": "app", "pluginPackages": [ + "@backstage/plugin-app", "@backstage/plugin-app-backend", "@backstage/plugin-app-node" ] diff --git a/plugins/app/.eslintrc.js b/plugins/app/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/app/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/app/README.md b/plugins/app/README.md new file mode 100644 index 0000000000..e258a1671a --- /dev/null +++ b/plugins/app/README.md @@ -0,0 +1,13 @@ +# app + +Welcome to the app plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/app](http://localhost:3000/app). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. diff --git a/plugins/app/catalog-info.yaml b/plugins/app/catalog-info.yaml new file mode 100644 index 0000000000..5bae243ea7 --- /dev/null +++ b/plugins/app/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-app + title: '@backstage/plugin-app' +spec: + lifecycle: experimental + type: backstage-frontend-plugin + owner: maintainers diff --git a/plugins/app/package.json b/plugins/app/package.json new file mode 100644 index 0000000000..72159d96f1 --- /dev/null +++ b/plugins/app/package.json @@ -0,0 +1,61 @@ +{ + "name": "@backstage/plugin-app", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/app" + }, + "backstage": { + "role": "frontend-plugin", + "pluginId": "app", + "pluginPackages": [ + "@backstage/plugin-app", + "@backstage/plugin-app-backend", + "@backstage/plugin-app-node" + ] + }, + "sideEffects": false, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/core-components": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.61", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/frontend-test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.0.0", + "msw": "^1.0.0", + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/app/src/index.ts b/plugins/app/src/index.ts new file mode 100644 index 0000000000..24f65df618 --- /dev/null +++ b/plugins/app/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { appPlugin as default } from './plugin'; diff --git a/plugins/app/src/plugin.test.ts b/plugins/app/src/plugin.test.ts new file mode 100644 index 0000000000..80588710a5 --- /dev/null +++ b/plugins/app/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { appPlugin } from './plugin'; + +describe('app', () => { + it('should export plugin', () => { + expect(appPlugin).toBeDefined(); + }); +}); diff --git a/plugins/app/src/plugin.ts b/plugins/app/src/plugin.ts new file mode 100644 index 0000000000..2c032bcb5e --- /dev/null +++ b/plugins/app/src/plugin.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; + +export const appPlugin = createFrontendPlugin({ + id: 'app', + extensions: [], +}); diff --git a/plugins/app/src/setupTests.ts b/plugins/app/src/setupTests.ts new file mode 100644 index 0000000000..658016ffdd --- /dev/null +++ b/plugins/app/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import '@testing-library/jest-dom'; diff --git a/yarn.lock b/yarn.lock index ce072776c3..c745d980d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4684,6 +4684,30 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-app@^0.0.0, @backstage/plugin-app@workspace:plugins/app": + version: 0.0.0-use.local + resolution: "@backstage/plugin-app@workspace:plugins/app" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/frontend-test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@material-ui/core": ^4.9.13 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": ^4.0.0-alpha.61 + "@testing-library/jest-dom": ^6.0.0 + "@testing-library/react": ^14.0.0 + "@testing-library/user-event": ^14.0.0 + msw: ^1.0.0 + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-use: ^17.2.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend-module-atlassian-provider@workspace:^, @backstage/plugin-auth-backend-module-atlassian-provider@workspace:plugins/auth-backend-module-atlassian-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-atlassian-provider@workspace:plugins/auth-backend-module-atlassian-provider" @@ -16954,6 +16978,22 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^9.0.0": + version: 9.3.4 + resolution: "@testing-library/dom@npm:9.3.4" + dependencies: + "@babel/code-frame": ^7.10.4 + "@babel/runtime": ^7.12.5 + "@types/aria-query": ^5.0.1 + aria-query: 5.1.3 + chalk: ^4.1.0 + dom-accessibility-api: ^0.5.9 + lz-string: ^1.5.0 + pretty-format: ^27.0.2 + checksum: dfd6fb0d6c7b4dd716ba3c47309bc9541b4a55772cb61758b4f396b3785efe2dbc75dc63423545c039078c7ffcc5e4b8c67c2db1b6af4799580466036f70026f + languageName: node + linkType: hard + "@testing-library/jest-dom@npm:^6.0.0": version: 6.4.2 resolution: "@testing-library/jest-dom@npm:6.4.2" @@ -17009,6 +17049,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^14.0.0": + version: 14.3.1 + resolution: "@testing-library/react@npm:14.3.1" + dependencies: + "@babel/runtime": ^7.12.5 + "@testing-library/dom": ^9.0.0 + "@types/react-dom": ^18.0.0 + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: b057d4c9db5a523acfc24d7bc4665a924ab8d6f252c7f51eecf7dd30f1239413e1134925fd5cc9cbdef80496af64c04e6719b2081f89fe05ba87e8c6305bcc16 + languageName: node + linkType: hard + "@testing-library/react@npm:^15.0.0": version: 15.0.2 resolution: "@testing-library/react@npm:15.0.2" @@ -20970,6 +21024,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: ^2.0.5 + checksum: 929ff95f02857b650fb4cbcd2f41072eee2f46159a6605ea03bf63aa572e35ffdff43d69e815ddc462e16e07de8faba3978afc2813650b4448ee18c9895d982b + languageName: node + linkType: hard + "aria-query@npm:5.3.0, aria-query@npm:^5.0.0, aria-query@npm:^5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" @@ -20986,7 +21049,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "array-buffer-byte-length@npm:1.0.1" dependencies: @@ -24695,6 +24758,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: ^1.0.0 + call-bind: ^1.0.5 + es-get-iterator: ^1.1.3 + get-intrinsic: ^1.2.2 + is-arguments: ^1.1.1 + is-array-buffer: ^3.0.2 + is-date-object: ^1.0.5 + is-regex: ^1.1.4 + is-shared-array-buffer: ^1.0.2 + isarray: ^2.0.5 + object-is: ^1.1.5 + object-keys: ^1.1.1 + object.assign: ^4.1.4 + regexp.prototype.flags: ^1.5.1 + side-channel: ^1.0.4 + which-boxed-primitive: ^1.0.2 + which-collection: ^1.0.1 + which-typed-array: ^1.1.13 + checksum: ee8852f23e4d20a5626c13b02f415ba443a1b30b4b3d39eaf366d59c4a85e6545d7ec917db44d476a85ae5a86064f7e5f7af7479f38f113995ba869f3a1ddc53 + languageName: node + linkType: hard + "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -25771,6 +25860,23 @@ __metadata: languageName: node linkType: hard +"es-get-iterator@npm:^1.1.3": + version: 1.1.3 + resolution: "es-get-iterator@npm:1.1.3" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 + has-symbols: ^1.0.3 + is-arguments: ^1.1.1 + is-map: ^2.0.2 + is-set: ^2.0.2 + is-string: ^1.0.7 + isarray: ^2.0.5 + stop-iteration-iterator: ^1.0.0 + checksum: 8fa118da42667a01a7c7529f8a8cca514feeff243feec1ce0bb73baaa3514560bd09d2b3438873cf8a5aaec5d52da248131de153b28e2638a061b6e4df13267d + languageName: node + linkType: hard + "es-iterator-helpers@npm:^1.0.15, es-iterator-helpers@npm:^1.0.19": version: 1.0.19 resolution: "es-iterator-helpers@npm:1.0.19" @@ -26812,6 +26918,7 @@ __metadata: "@backstage/frontend-app-api": "workspace:^" "@backstage/integration-react": "workspace:^" "@backstage/plugin-api-docs": "workspace:^" + "@backstage/plugin-app": ^0.0.0 "@backstage/plugin-auth-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" @@ -28215,7 +28322,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": +"get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" dependencies: @@ -29878,7 +29985,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.7": +"internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.7": version: 1.0.7 resolution: "internal-slot@npm:1.0.7" dependencies: @@ -30017,7 +30124,7 @@ __metadata: languageName: node linkType: hard -"is-arguments@npm:^1.0.4": +"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" dependencies: @@ -30027,7 +30134,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.4": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4": version: 3.0.4 resolution: "is-array-buffer@npm:3.0.4" dependencies: @@ -30325,10 +30432,10 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.1": - version: 2.0.2 - resolution: "is-map@npm:2.0.2" - checksum: ace3d0ecd667bbdefdb1852de601268f67f2db725624b1958f279316e13fecb8fa7df91fd60f690d7417b4ec180712f5a7ee967008e27c65cfd475cc84337728 +"is-map@npm:^2.0.1, is-map@npm:^2.0.2": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: e6ce5f6380f32b141b3153e6ba9074892bbbbd655e92e7ba5ff195239777e767a976dcd4e22f864accaf30e53ebf961ab1995424aef91af68788f0591b7396cc languageName: node linkType: hard @@ -30542,10 +30649,10 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.1": - version: 2.0.2 - resolution: "is-set@npm:2.0.2" - checksum: b64343faf45e9387b97a6fd32be632ee7b269bd8183701f3b3f5b71a7cf00d04450ed8669d0bd08753e08b968beda96fca73a10fd0ff56a32603f64deba55a57 +"is-set@npm:^2.0.1, is-set@npm:^2.0.2": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 36e3f8c44bdbe9496c9689762cc4110f6a6a12b767c5d74c0398176aa2678d4467e3bf07595556f2dba897751bde1422480212b97d973c7b08a343100b0c0dfe languageName: node linkType: hard @@ -35646,13 +35753,13 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.0.1": - version: 1.1.5 - resolution: "object-is@npm:1.1.5" +"object-is@npm:^1.0.1, object-is@npm:^1.1.5": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: 989b18c4cba258a6b74dc1d74a41805c1a1425bce29f6cabb50dcb1a6a651ea9104a1b07046739a49a5bb1bc49727bcb00efd5c55f932f6ea04ec8927a7901fe + call-bind: ^1.0.7 + define-properties: ^1.2.1 + checksum: 3ea22759967e6f2380a2cbbd0f737b42dc9ddb2dfefdb159a1b927fea57335e1b058b564bfa94417db8ad58cddab33621a035de6f5e5ad56d89f2dd03e66c6a1 languageName: node linkType: hard @@ -39046,7 +39153,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.0.2": +"react@npm:^16.13.1 || ^17.0.0 || ^18.0.0, react@npm:^18.0.2": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -39359,7 +39466,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.2": +"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" dependencies: @@ -41411,6 +41518,15 @@ __metadata: languageName: node linkType: hard +"stop-iteration-iterator@npm:^1.0.0": + version: 1.0.0 + resolution: "stop-iteration-iterator@npm:1.0.0" + dependencies: + internal-slot: ^1.0.4 + checksum: d04173690b2efa40e24ab70e5e51a3ff31d56d699550cfad084104ab3381390daccb36652b25755e420245f3b0737de66c1879eaa2a8d4fc0a78f9bf892fcb42 + languageName: node + linkType: hard + "stoppable@npm:^1.1.0": version: 1.1.0 resolution: "stoppable@npm:1.1.0" @@ -44686,7 +44802,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: From 06448161aa1d75b1c927567e47f98c379ccde153 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 13:32:06 +0200 Subject: [PATCH 02/24] chore: move things around a little bit to the app plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Co-authored-by: Camila Belo Co-authored-by: Camila Belo Signed-off-by: blam --- .../frontend-app-api/src/wiring/createApp.tsx | 30 +++++++++++-------- plugins/app/package.json | 4 ++- .../app}/src/extensions/App.tsx | 0 .../app}/src/extensions/AppLanguageApi.ts | 2 +- .../app}/src/extensions/AppLayout.tsx | 0 .../app}/src/extensions/AppNav.tsx | 4 +-- .../app}/src/extensions/AppRoot.tsx | 11 ++++--- .../app}/src/extensions/AppRoutes.tsx | 0 .../app}/src/extensions/AppThemeApi.tsx | 0 .../app}/src/extensions/ComponentsApi.tsx | 3 +- .../app}/src/extensions/FeatureFlagsApi.ts | 2 +- .../app}/src/extensions/IconsApi.ts | 5 ++-- .../app}/src/extensions/TranslationsApi.tsx | 2 +- .../app}/src/extensions/components.tsx | 3 +- .../app}/src/extensions/elements.tsx | 0 yarn.lock | 2 ++ 16 files changed, 40 insertions(+), 28 deletions(-) rename {packages/frontend-app-api => plugins/app}/src/extensions/App.tsx (100%) rename {packages/frontend-app-api => plugins/app}/src/extensions/AppLanguageApi.ts (90%) rename {packages/frontend-app-api => plugins/app}/src/extensions/AppLayout.tsx (100%) rename {packages/frontend-app-api => plugins/app}/src/extensions/AppNav.tsx (95%) rename {packages/frontend-app-api => plugins/app}/src/extensions/AppRoot.tsx (90%) rename {packages/frontend-app-api => plugins/app}/src/extensions/AppRoutes.tsx (100%) rename {packages/frontend-app-api => plugins/app}/src/extensions/AppThemeApi.tsx (100%) rename {packages/frontend-app-api => plugins/app}/src/extensions/ComponentsApi.tsx (88%) rename {packages/frontend-app-api => plugins/app}/src/extensions/FeatureFlagsApi.ts (88%) rename {packages/frontend-app-api => plugins/app}/src/extensions/IconsApi.ts (84%) rename {packages/frontend-app-api => plugins/app}/src/extensions/TranslationsApi.tsx (92%) rename {packages/frontend-app-api => plugins/app}/src/extensions/components.tsx (92%) rename {packages/frontend-app-api => plugins/app}/src/extensions/elements.tsx (100%) diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 242709cdb2..1a6f58b71c 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -34,10 +34,10 @@ import { createApiFactory, routeResolutionApiRef, } from '@backstage/frontend-plugin-api'; -import { App } from '../extensions/App'; -import { AppRoutes } from '../extensions/AppRoutes'; -import { AppLayout } from '../extensions/AppLayout'; -import { AppNav } from '../extensions/AppNav'; +import { App } from '@backstage/plugin-app/src/extensions/App'; +import { AppRoutes } from '@backstage/plugin-app/src/extensions/AppRoutes'; +import { AppLayout } from '@backstage/plugin-app/src/extensions/AppLayout'; +import { AppNav } from '@backstage/plugin-app/src/extensions/AppNav'; import { AnyApiFactory, ApiHolder, @@ -75,7 +75,7 @@ import { apis as defaultApis } from '../../../app-defaults/src/defaults'; import { oauthRequestDialogAppRootElement, alertDisplayAppRootElement, -} from '../extensions/elements'; +} from '@backstage/plugin-app/src/extensions/elements'; import { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode'; import { CreateAppRouteBinder } from '../routing'; @@ -86,21 +86,25 @@ import { DefaultProgressComponent, DefaultErrorBoundaryComponent, DefaultNotFoundErrorPageComponent, -} from '../extensions/components'; +} from '@backstage/plugin-app/src/extensions/components'; import { InternalAppContext } from './InternalAppContext'; -import { AppRoot } from '../extensions/AppRoot'; +import { AppRoot } from '@backstage/plugin-app/src/extensions/AppRoot'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; import { stringifyError } from '@backstage/errors'; import { getBasePath } from '../routing/getBasePath'; -import { AppThemeApi, DarkTheme, LightTheme } from '../extensions/AppThemeApi'; -import { IconsApi } from '../extensions/IconsApi'; -import { TranslationsApi } from '../extensions/TranslationsApi'; -import { ComponentsApi } from '../extensions/ComponentsApi'; -import { AppLanguageApi } from '../extensions/AppLanguageApi'; -import { FeatureFlagsApi } from '../extensions/FeatureFlagsApi'; +import { + AppThemeApi, + DarkTheme, + LightTheme, +} from '@backstage/plugin-app/src/extensions/AppThemeApi'; +import { IconsApi } from '@backstage/plugin-app/src/extensions/IconsApi'; +import { TranslationsApi } from '@backstage/plugin-app/src/extensions/TranslationsApi'; +import { ComponentsApi } from '@backstage/plugin-app/src/extensions/ComponentsApi'; +import { AppLanguageApi } from '@backstage/plugin-app/src/extensions/AppLanguageApi'; +import { FeatureFlagsApi } from '@backstage/plugin-app/src/extensions/FeatureFlagsApi'; import { Root } from '../extensions/Root'; import { resolveAppTree } from '../tree/resolveAppTree'; import { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs'; diff --git a/plugins/app/package.json b/plugins/app/package.json index 72159d96f1..48db45714d 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -35,6 +35,7 @@ }, "dependencies": { "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", @@ -43,7 +44,8 @@ "react-use": "^17.2.4" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + "react": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/packages/frontend-app-api/src/extensions/App.tsx b/plugins/app/src/extensions/App.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/App.tsx rename to plugins/app/src/extensions/App.tsx diff --git a/packages/frontend-app-api/src/extensions/AppLanguageApi.ts b/plugins/app/src/extensions/AppLanguageApi.ts similarity index 90% rename from packages/frontend-app-api/src/extensions/AppLanguageApi.ts rename to plugins/app/src/extensions/AppLanguageApi.ts index ff12848d44..7a6c10db96 100644 --- a/packages/frontend-app-api/src/extensions/AppLanguageApi.ts +++ b/plugins/app/src/extensions/AppLanguageApi.ts @@ -15,7 +15,7 @@ */ // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppLanguageSelector } from '../../../core-app-api/src/apis/implementations/AppLanguageApi'; +import { AppLanguageSelector } from '../../../../packages/core-app-api/src/apis/implementations/AppLanguageApi'; import { appLanguageApiRef } from '@backstage/core-plugin-api/alpha'; import { ApiBlueprint, createApiFactory } from '@backstage/frontend-plugin-api'; diff --git a/packages/frontend-app-api/src/extensions/AppLayout.tsx b/plugins/app/src/extensions/AppLayout.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/AppLayout.tsx rename to plugins/app/src/extensions/AppLayout.tsx diff --git a/packages/frontend-app-api/src/extensions/AppNav.tsx b/plugins/app/src/extensions/AppNav.tsx similarity index 95% rename from packages/frontend-app-api/src/extensions/AppNav.tsx rename to plugins/app/src/extensions/AppNav.tsx index b80a537ef6..3659f474b2 100644 --- a/packages/frontend-app-api/src/extensions/AppNav.tsx +++ b/plugins/app/src/extensions/AppNav.tsx @@ -33,9 +33,9 @@ import { SidebarItem, } from '@backstage/core-components'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import LogoIcon from '../../../app/src/components/Root/LogoIcon'; +import LogoIcon from '../../../../packages/app/src/components/Root/LogoIcon'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import LogoFull from '../../../app/src/components/Root/LogoFull'; +import LogoFull from '../../../../packages/app/src/components/Root/LogoFull'; const useSidebarLogoStyles = makeStyles({ root: { diff --git a/packages/frontend-app-api/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx similarity index 90% rename from packages/frontend-app-api/src/extensions/AppRoot.tsx rename to plugins/app/src/extensions/AppRoot.tsx index a158386d04..e54699fac5 100644 --- a/packages/frontend-app-api/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -36,12 +36,15 @@ import { configApiRef, useApi, } from '@backstage/core-plugin-api'; -import { InternalAppContext } from '../wiring/InternalAppContext'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; +import { InternalAppContext } from '../../../../packages/frontend-app-api/src/wiring/InternalAppContext'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppIdentityProxy } from '../../../../packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; import { BrowserRouter } from 'react-router-dom'; -import { RouteTracker } from '../routing/RouteTracker'; -import { getBasePath } from '../routing/getBasePath'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { RouteTracker } from '../../../../packages/frontend-app-api/src/routing/RouteTracker'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { getBasePath } from '../../../../packages/frontend-app-api/src/routing/getBasePath'; export const AppRoot = createExtension({ namespace: 'app', diff --git a/packages/frontend-app-api/src/extensions/AppRoutes.tsx b/plugins/app/src/extensions/AppRoutes.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/AppRoutes.tsx rename to plugins/app/src/extensions/AppRoutes.tsx diff --git a/packages/frontend-app-api/src/extensions/AppThemeApi.tsx b/plugins/app/src/extensions/AppThemeApi.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/AppThemeApi.tsx rename to plugins/app/src/extensions/AppThemeApi.tsx diff --git a/packages/frontend-app-api/src/extensions/ComponentsApi.tsx b/plugins/app/src/extensions/ComponentsApi.tsx similarity index 88% rename from packages/frontend-app-api/src/extensions/ComponentsApi.tsx rename to plugins/app/src/extensions/ComponentsApi.tsx index 5489aa35a2..b578429253 100644 --- a/packages/frontend-app-api/src/extensions/ComponentsApi.tsx +++ b/plugins/app/src/extensions/ComponentsApi.tsx @@ -21,7 +21,8 @@ import { createApiFactory, componentsApiRef, } from '@backstage/frontend-plugin-api'; -import { DefaultComponentsApi } from '../apis/implementations/ComponentsApi'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { DefaultComponentsApi } from '../../../../packages/frontend-app-api/src/apis/implementations/ComponentsApi'; /** * Contains the shareable components installed into the app. diff --git a/packages/frontend-app-api/src/extensions/FeatureFlagsApi.ts b/plugins/app/src/extensions/FeatureFlagsApi.ts similarity index 88% rename from packages/frontend-app-api/src/extensions/FeatureFlagsApi.ts rename to plugins/app/src/extensions/FeatureFlagsApi.ts index 8016521539..40a44d1348 100644 --- a/packages/frontend-app-api/src/extensions/FeatureFlagsApi.ts +++ b/plugins/app/src/extensions/FeatureFlagsApi.ts @@ -20,7 +20,7 @@ import { featureFlagsApiRef, } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { LocalStorageFeatureFlags } from '../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags'; +import { LocalStorageFeatureFlags } from '../../../../packages/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags'; /** * Contains the shareable icons installed into the app. diff --git a/packages/frontend-app-api/src/extensions/IconsApi.ts b/plugins/app/src/extensions/IconsApi.ts similarity index 84% rename from packages/frontend-app-api/src/extensions/IconsApi.ts rename to plugins/app/src/extensions/IconsApi.ts index dc2acac629..e73a74132a 100644 --- a/packages/frontend-app-api/src/extensions/IconsApi.ts +++ b/plugins/app/src/extensions/IconsApi.ts @@ -21,9 +21,10 @@ import { createApiFactory, iconsApiRef, } from '@backstage/frontend-plugin-api'; -import { DefaultIconsApi } from '../apis/implementations/IconsApi'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { icons as defaultIcons } from '../../../app-defaults/src/defaults'; +import { DefaultIconsApi } from '../../../../packages/frontend-app-api/src/apis/implementations/IconsApi'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { icons as defaultIcons } from '../../../../packages/app-defaults/src/defaults'; /** * Contains the shareable icons installed into the app. diff --git a/packages/frontend-app-api/src/extensions/TranslationsApi.tsx b/plugins/app/src/extensions/TranslationsApi.tsx similarity index 92% rename from packages/frontend-app-api/src/extensions/TranslationsApi.tsx rename to plugins/app/src/extensions/TranslationsApi.tsx index 1fa4fadaae..ed7ab1abdb 100644 --- a/packages/frontend-app-api/src/extensions/TranslationsApi.tsx +++ b/plugins/app/src/extensions/TranslationsApi.tsx @@ -25,7 +25,7 @@ import { } from '@backstage/core-plugin-api/alpha'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { I18nextTranslationApi } from '../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi'; +import { I18nextTranslationApi } from '../../../../packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi'; /** * Contains translations that are installed in the app. diff --git a/packages/frontend-app-api/src/extensions/components.tsx b/plugins/app/src/extensions/components.tsx similarity index 92% rename from packages/frontend-app-api/src/extensions/components.tsx rename to plugins/app/src/extensions/components.tsx index 4f6574ab3e..829cb99ea2 100644 --- a/packages/frontend-app-api/src/extensions/components.tsx +++ b/plugins/app/src/extensions/components.tsx @@ -24,8 +24,7 @@ import { } from '@backstage/frontend-plugin-api'; import { ErrorPanel } from '@backstage/core-components'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { components as defaultComponents } from '../../../app-defaults/src/defaults'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { components as defaultComponents } from '../../../../packages/app-defaults/src/defaults'; export const DefaultProgressComponent = createComponentExtension({ ref: coreComponentRefs.progress, diff --git a/packages/frontend-app-api/src/extensions/elements.tsx b/plugins/app/src/extensions/elements.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/elements.tsx rename to plugins/app/src/extensions/elements.tsx diff --git a/yarn.lock b/yarn.lock index c745d980d3..f1ed9a0f9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4690,6 +4690,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/frontend-test-utils": "workspace:^" @@ -4705,6 +4706,7 @@ __metadata: react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 languageName: unknown linkType: soft From 1deb3f8948a03a4872a8b6b42e8c1800bc71dd63 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 13:37:50 +0200 Subject: [PATCH 03/24] chore: move over the API definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Co-authored-by: Camila Belo Co-authored-by: Camila Belo Signed-off-by: blam --- .../frontend-app-api/src/wiring/createApp.tsx | 58 +-- plugins/app/package.json | 1 + plugins/app/src/defaultApis.ts | 382 ++++++++++++++++++ yarn.lock | 1 + 4 files changed, 386 insertions(+), 56 deletions(-) create mode 100644 plugins/app/src/defaultApis.ts diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 1a6f58b71c..83afc2b7a9 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -34,10 +34,7 @@ import { createApiFactory, routeResolutionApiRef, } from '@backstage/frontend-plugin-api'; -import { App } from '@backstage/plugin-app/src/extensions/App'; -import { AppRoutes } from '@backstage/plugin-app/src/extensions/AppRoutes'; -import { AppLayout } from '@backstage/plugin-app/src/extensions/AppLayout'; -import { AppNav } from '@backstage/plugin-app/src/extensions/AppNav'; + import { AnyApiFactory, ApiHolder, @@ -69,42 +66,20 @@ import { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultCo import { overrideBaseUrlConfigs } from '../../../core-app-api/src/app/overrideBaseUrlConfigs'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { apis as defaultApis } from '../../../app-defaults/src/defaults'; -import { - oauthRequestDialogAppRootElement, - alertDisplayAppRootElement, -} from '@backstage/plugin-app/src/extensions/elements'; import { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode'; import { CreateAppRouteBinder } from '../routing'; import { RouteResolver } from '../routing/RouteResolver'; import { resolveRouteBindings } from '../routing/resolveRouteBindings'; import { collectRouteIds } from '../routing/collectRouteIds'; -import { - DefaultProgressComponent, - DefaultErrorBoundaryComponent, - DefaultNotFoundErrorPageComponent, -} from '@backstage/plugin-app/src/extensions/components'; import { InternalAppContext } from './InternalAppContext'; -import { AppRoot } from '@backstage/plugin-app/src/extensions/AppRoot'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; import { stringifyError } from '@backstage/errors'; import { getBasePath } from '../routing/getBasePath'; -import { - AppThemeApi, - DarkTheme, - LightTheme, -} from '@backstage/plugin-app/src/extensions/AppThemeApi'; -import { IconsApi } from '@backstage/plugin-app/src/extensions/IconsApi'; -import { TranslationsApi } from '@backstage/plugin-app/src/extensions/TranslationsApi'; -import { ComponentsApi } from '@backstage/plugin-app/src/extensions/ComponentsApi'; -import { AppLanguageApi } from '@backstage/plugin-app/src/extensions/AppLanguageApi'; -import { FeatureFlagsApi } from '@backstage/plugin-app/src/extensions/FeatureFlagsApi'; import { Root } from '../extensions/Root'; import { resolveAppTree } from '../tree/resolveAppTree'; import { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs'; @@ -113,35 +88,6 @@ import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry'; -const DefaultApis = defaultApis.map(factory => - ApiBlueprint.make({ namespace: factory.api.id, params: { factory } }), -); - -export const builtinExtensions = ( - [ - Root, - App, - AppRoot, - AppRoutes, - AppNav, - AppLayout, - DefaultProgressComponent, - DefaultErrorBoundaryComponent, - DefaultNotFoundErrorPageComponent, - LightTheme, - DarkTheme, - oauthRequestDialogAppRootElement, - alertDisplayAppRootElement, - AppThemeApi, - AppLanguageApi, - IconsApi, - TranslationsApi, - ComponentsApi, - FeatureFlagsApi, - ...DefaultApis, - ] as ExtensionDefinition[] -).map(def => resolveExtensionDefinition(def)); - function deduplicateFeatures( allFeatures: FrontendFeature[], ): FrontendFeature[] { @@ -338,7 +284,7 @@ export function createSpecializedApp(options?: { 'root', resolveAppNodeSpecs({ features, - builtinExtensions, + builtinExtensions: [resolveExtensionDefinition(Root)], parameters: readAppExtensionsConfig(config), forbidden: new Set(['root']), }), diff --git a/plugins/app/package.json b/plugins/app/package.json index 48db45714d..5c5f631c54 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -37,6 +37,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", diff --git a/plugins/app/src/defaultApis.ts b/plugins/app/src/defaultApis.ts new file mode 100644 index 0000000000..fd46ecf596 --- /dev/null +++ b/plugins/app/src/defaultApis.ts @@ -0,0 +1,382 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { + AlertApiForwarder, + NoOpAnalyticsApi, + ErrorApiForwarder, + ErrorAlerter, + GoogleAuth, + GithubAuth, + OktaAuth, + GitlabAuth, + MicrosoftAuth, + BitbucketAuth, + BitbucketServerAuth, + OAuthRequestManager, + WebStorage, + UrlPatternDiscovery, + OneLoginAuth, + UnhandledErrorForwarder, + AtlassianAuth, + createFetchApi, + FetchMiddlewares, + VMwareCloudAuth, +} from '../../../packages/core-app-api/src/apis/implementations'; + +import { + createApiFactory, + alertApiRef, + analyticsApiRef, + errorApiRef, + discoveryApiRef, + fetchApiRef, + identityApiRef, + oauthRequestApiRef, + googleAuthApiRef, + githubAuthApiRef, + oktaAuthApiRef, + gitlabAuthApiRef, + microsoftAuthApiRef, + storageApiRef, + configApiRef, + oneloginAuthApiRef, + bitbucketAuthApiRef, + bitbucketServerAuthApiRef, + atlassianAuthApiRef, + vmwareCloudAuthApiRef, +} from '@backstage/core-plugin-api'; +import { ApiBlueprint } from '@backstage/frontend-plugin-api'; +import { + permissionApiRef, + IdentityPermissionApi, +} from '@backstage/plugin-permission-react'; + +export const apis = [ + ApiBlueprint.make({ + name: 'discovery', + params: { + factory: createApiFactory({ + api: discoveryApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => + UrlPatternDiscovery.compile( + `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, + ), + }), + }, + }), + ApiBlueprint.make({ + name: 'alert', + params: { + factory: createApiFactory({ + api: alertApiRef, + deps: {}, + factory: () => new AlertApiForwarder(), + }), + }, + }), + ApiBlueprint.make({ + name: 'analytics', + params: { + factory: createApiFactory({ + api: analyticsApiRef, + deps: {}, + factory: () => new NoOpAnalyticsApi(), + }), + }, + }), + ApiBlueprint.make({ + name: 'error', + params: { + factory: createApiFactory({ + api: errorApiRef, + deps: { alertApi: alertApiRef }, + factory: ({ alertApi }) => { + const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); + UnhandledErrorForwarder.forward(errorApi, { hidden: false }); + return errorApi; + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'storage', + params: { + factory: createApiFactory({ + api: storageApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => WebStorage.create({ errorApi }), + }), + }, + }), + ApiBlueprint.make({ + name: 'fetch', + params: { + factory: createApiFactory({ + api: fetchApiRef, + deps: { + configApi: configApiRef, + identityApi: identityApiRef, + discoveryApi: discoveryApiRef, + }, + factory: ({ configApi, identityApi, discoveryApi }) => { + return createFetchApi({ + middleware: [ + FetchMiddlewares.resolvePluginProtocol({ + discoveryApi, + }), + FetchMiddlewares.injectIdentityAuth({ + identityApi, + config: configApi, + }), + ], + }); + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'oauth-request', + params: { + factory: createApiFactory({ + api: oauthRequestApiRef, + deps: {}, + factory: () => new OAuthRequestManager(), + }), + }, + }), + ApiBlueprint.make({ + name: 'google-auth', + params: { + factory: createApiFactory({ + api: googleAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GoogleAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'microsoft-auth', + params: { + factory: createApiFactory({ + api: microsoftAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + MicrosoftAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'github-auth', + params: { + factory: createApiFactory({ + api: githubAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GithubAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'okta-auth', + params: { + factory: createApiFactory({ + api: oktaAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OktaAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'gitlab-auth', + params: { + factory: createApiFactory({ + api: gitlabAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GitlabAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'onelogin-auth', + params: { + factory: createApiFactory({ + api: oneloginAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OneLoginAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'bitbucket-auth', + params: { + factory: createApiFactory({ + api: bitbucketAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + BitbucketAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + defaultScopes: ['account'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'bitbucket-server-auth', + params: { + factory: createApiFactory({ + api: bitbucketServerAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + BitbucketServerAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + defaultScopes: ['REPO_READ'], + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'atlassian-auth', + params: { + factory: createApiFactory({ + api: atlassianAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return AtlassianAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'vmware-cloud-auth', + params: { + factory: createApiFactory({ + api: vmwareCloudAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return VMwareCloudAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'permission', + params: { + factory: createApiFactory({ + api: permissionApiRef, + deps: { + discovery: discoveryApiRef, + identity: identityApiRef, + config: configApiRef, + }, + factory: ({ config, discovery, identity }) => + IdentityPermissionApi.create({ config, discovery, identity }), + }), + }, + }), +] as const; diff --git a/yarn.lock b/yarn.lock index f1ed9a0f9c..07c9b607b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4694,6 +4694,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/frontend-test-utils": "workspace:^" + "@backstage/plugin-permission-react": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.9.13 "@material-ui/icons": ^4.9.1 From 175201bdf03b90c4675bb51504f0ffa728893376 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 13:43:57 +0200 Subject: [PATCH 04/24] chore: added all the app extensions to the app plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Fredrik Adelöw Signed-off-by: blam You are currently bisecting, started from branch 'master'. --- plugins/app/src/extensions/index.ts | 35 +++++++++++++++++++++ plugins/app/src/plugin.ts | 47 +++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 plugins/app/src/extensions/index.ts diff --git a/plugins/app/src/extensions/index.ts b/plugins/app/src/extensions/index.ts new file mode 100644 index 0000000000..85efe58515 --- /dev/null +++ b/plugins/app/src/extensions/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { App } from './App'; +export { AppLanguageApi } from './AppLanguageApi'; +export { AppLayout } from './AppLayout'; +export { AppNav } from './AppNav'; +export { AppRoot } from './AppRoot'; +export { AppRoutes } from './AppRoutes'; +export { AppThemeApi } from './AppThemeApi'; +export { ComponentsApi } from './ComponentsApi'; +export { IconsApi } from './IconsApi'; +export { FeatureFlagsApi } from './FeatureFlagsApi'; +export { TranslationsApi } from './TranslationsApi'; +export { + DefaultProgressComponent, + DefaultErrorBoundaryComponent, + DefaultNotFoundErrorPageComponent, +} from './components'; +export { + oauthRequestDialogAppRootElement, + alertDisplayAppRootElement, +} from './elements'; diff --git a/plugins/app/src/plugin.ts b/plugins/app/src/plugin.ts index 2c032bcb5e..ff0c69faec 100644 --- a/plugins/app/src/plugin.ts +++ b/plugins/app/src/plugin.ts @@ -13,9 +13,52 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { + App, + AppLanguageApi, + AppLayout, + AppNav, + AppRoot, + AppRoutes, + AppThemeApi, + DarkTheme, + LightTheme, + ComponentsApi, + IconsApi, + FeatureFlagsApi, + TranslationsApi, + DefaultProgressComponent, + DefaultNotFoundErrorPageComponent, + DefaultErrorBoundaryComponent, + oauthRequestDialogAppRootElement, + alertDisplayAppRootElement, +} from './extensions'; +import { apis } from './defaultApis'; + +/** @public */ export const appPlugin = createFrontendPlugin({ id: 'app', - extensions: [], + extensions: [ + ...apis, + App, + AppLanguageApi, + AppLayout, + AppNav, + AppRoot, + AppRoutes, + AppThemeApi, + DarkTheme, + LightTheme, + ComponentsApi, + IconsApi, + FeatureFlagsApi, + TranslationsApi, + DefaultProgressComponent, + DefaultNotFoundErrorPageComponent, + DefaultErrorBoundaryComponent, + oauthRequestDialogAppRootElement, + alertDisplayAppRootElement, + ], }); From 5433a07fa24c101762b5fab381d32a54f3ce385e Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 13:49:07 +0200 Subject: [PATCH 05/24] chore: generate api-reports and fix bad import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Patrik Oldsberg Signed-off-by: blam --- plugins/app/api-report.md | 665 +++++++++++++++++++++ plugins/app/src/extensions/AppThemeApi.tsx | 3 +- plugins/app/src/extensions/index.ts | 2 +- 3 files changed, 668 insertions(+), 2 deletions(-) create mode 100644 plugins/app/api-report.md diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md new file mode 100644 index 0000000000..9618474641 --- /dev/null +++ b/plugins/app/api-report.md @@ -0,0 +1,665 @@ +## API Report File for "@backstage/plugin-app" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { AnyExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { AppTheme } from '@backstage/frontend-plugin-api'; +import { BackstagePlugin } from '@backstage/frontend-plugin-api'; +import { ComponentRef } from '@backstage/frontend-plugin-api'; +import { ComponentType } from 'react'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; +import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { ReactNode } from 'react'; +import { RouteRef } from '@backstage/frontend-plugin-api'; +import { SignInPageProps } from '@backstage/core-plugin-api'; +import { TranslationMessages } from '@backstage/frontend-plugin-api'; +import { TranslationResource } from '@backstage/frontend-plugin-api'; + +// @public (undocumented) +const appPlugin: BackstagePlugin< + {}, + {}, + { + [x: `component:app/${string}`]: ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef< + { + ref: ComponentRef; + impl: ComponentType; + }, + 'core.component.component', + {} + >, + { + [x: string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }, + { + kind: 'component'; + namespace: string; + name: string; + } + >; + 'api:app/discovery': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'discovery'; + } + >; + 'api:app/alert': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'alert'; + } + >; + 'api:app/analytics': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'analytics'; + } + >; + 'api:app/error': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'error'; + } + >; + 'api:app/storage': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'storage'; + } + >; + 'api:app/fetch': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'fetch'; + } + >; + 'api:app/oauth-request': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'oauth-request'; + } + >; + 'api:app/google-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'google-auth'; + } + >; + 'api:app/microsoft-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'microsoft-auth'; + } + >; + 'api:app/github-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'github-auth'; + } + >; + 'api:app/okta-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'okta-auth'; + } + >; + 'api:app/gitlab-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'gitlab-auth'; + } + >; + 'api:app/onelogin-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'onelogin-auth'; + } + >; + 'api:app/bitbucket-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'bitbucket-auth'; + } + >; + 'api:app/bitbucket-server-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'bitbucket-server-auth'; + } + >; + 'api:app/atlassian-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'atlassian-auth'; + } + >; + 'api:app/vmware-cloud-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'vmware-cloud-auth'; + } + >; + 'api:app/permission': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'permission'; + } + >; + app: ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + root: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + }, + { + kind: undefined; + namespace: 'app'; + name: undefined; + } + >; + 'api:app/app-language': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'app-language'; + } + >; + 'app/layout': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + nav: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + content: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + }, + { + kind: undefined; + namespace: 'app'; + name: 'layout'; + } + >; + 'app/nav': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + items: ExtensionInput< + ConfigurableExtensionDataRef< + { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }, + 'core.nav-item.target', + {} + >, + { + singleton: false; + optional: false; + } + >; + logos: ExtensionInput< + ConfigurableExtensionDataRef< + { + logoIcon?: JSX.Element | undefined; + logoFull?: JSX.Element | undefined; + }, + 'core.nav-logo.logo-elements', + {} + >, + { + singleton: true; + optional: true; + } + >; + }, + { + kind: undefined; + namespace: 'app'; + name: 'nav'; + } + >; + 'app/root': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + router: ExtensionInput< + ConfigurableExtensionDataRef< + ComponentType<{ + children?: ReactNode; + }>, + 'app.router.wrapper', + {} + >, + { + singleton: true; + optional: true; + } + >; + signInPage: ExtensionInput< + ConfigurableExtensionDataRef< + ComponentType, + 'core.sign-in-page.component', + {} + >, + { + singleton: true; + optional: true; + } + >; + children: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + elements: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: false; + optional: false; + } + >; + wrappers: ExtensionInput< + ConfigurableExtensionDataRef< + ComponentType<{ + children?: ReactNode; + }>, + 'app.root.wrapper', + {} + >, + { + singleton: false; + optional: false; + } + >; + }, + { + kind: undefined; + namespace: 'app'; + name: 'root'; + } + >; + 'app/routes': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + routes: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + } + >; + }, + { + kind: undefined; + namespace: 'app'; + name: 'routes'; + } + >; + 'api:app/app-theme': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + themes: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: false; + optional: false; + } + >; + }, + { + kind: 'api'; + namespace: undefined; + name: 'app-theme'; + } + >; + 'theme:app/light': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'theme'; + namespace: 'app'; + name: 'light'; + } + >; + 'theme:app/dark': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'theme'; + namespace: 'app'; + name: 'dark'; + } + >; + 'api:app/components': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + components: ExtensionInput< + ConfigurableExtensionDataRef< + { + ref: ComponentRef; + impl: ComponentType; + }, + 'core.component.component', + {} + >, + { + singleton: false; + optional: false; + } + >; + }, + { + kind: 'api'; + namespace: undefined; + name: 'components'; + } + >; + 'api:app/icons': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + icons: ExtensionInput< + ConfigurableExtensionDataRef< + { + [x: string]: IconComponent_2; + }, + 'core.icons', + {} + >, + { + singleton: false; + optional: false; + } + >; + }, + { + kind: 'api'; + namespace: undefined; + name: 'icons'; + } + >; + 'api:app/feature-flags': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'feature-flags'; + } + >; + 'api:app/translations': ExtensionDefinition< + { + [x: string]: any; + }, + { + [x: string]: any; + }, + ConfigurableExtensionDataRef, + { + translations: ExtensionInput< + ConfigurableExtensionDataRef< + | TranslationResource + | TranslationMessages< + string, + { + [x: string]: string; + }, + boolean + >, + 'core.translation.translation', + {} + >, + { + singleton: false; + optional: false; + } + >; + }, + { + kind: 'api'; + namespace: undefined; + name: 'translations'; + } + >; + 'app-root-element:app/oauth-request-dialog': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'app-root-element'; + namespace: 'app'; + name: 'oauth-request-dialog'; + } + >; + 'app-root-element:app/alert-display': ExtensionDefinition< + { + transientTimeoutMs: number; + anchorOrigin: { + horizontal: 'center' | 'left' | 'right'; + vertical: 'top' | 'bottom'; + }; + }, + { + anchorOrigin?: + | { + horizontal?: 'center' | 'left' | 'right' | undefined; + vertical?: 'top' | 'bottom' | undefined; + } + | undefined; + transientTimeoutMs?: number | undefined; + }, + ConfigurableExtensionDataRef, + { + [x: string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }, + { + kind: 'app-root-element'; + namespace: 'app'; + name: 'alert-display'; + } + >; + } +>; +export default appPlugin; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/app/src/extensions/AppThemeApi.tsx b/plugins/app/src/extensions/AppThemeApi.tsx index fbbb9957ff..a31e29e58b 100644 --- a/plugins/app/src/extensions/AppThemeApi.tsx +++ b/plugins/app/src/extensions/AppThemeApi.tsx @@ -28,7 +28,8 @@ import { createApiFactory, appThemeApiRef, } from '@backstage/frontend-plugin-api'; -import { AppThemeSelector } from '@backstage/core-app-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppThemeSelector } from '../../../../packages/core-app-api/src/apis/implementations'; /** * Contains the themes installed into the app. diff --git a/plugins/app/src/extensions/index.ts b/plugins/app/src/extensions/index.ts index 85efe58515..157f7ddb62 100644 --- a/plugins/app/src/extensions/index.ts +++ b/plugins/app/src/extensions/index.ts @@ -19,7 +19,7 @@ export { AppLayout } from './AppLayout'; export { AppNav } from './AppNav'; export { AppRoot } from './AppRoot'; export { AppRoutes } from './AppRoutes'; -export { AppThemeApi } from './AppThemeApi'; +export { AppThemeApi, DarkTheme, LightTheme } from './AppThemeApi'; export { ComponentsApi } from './ComponentsApi'; export { IconsApi } from './IconsApi'; export { FeatureFlagsApi } from './FeatureFlagsApi'; From 1a76c4c9cabd52baaeeab580847b04d026c298c6 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 14:01:17 +0200 Subject: [PATCH 06/24] chore: app runs correctly :tada: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: blam --- packages/app-next/package.json | 1 + .../frontend-app-api/src/wiring/createApp.tsx | 22 +++++------------ .../src/blueprints/IconBundleBlueprint.ts | 2 +- .../src/blueprints/ThemeBlueprint.ts | 2 +- .../src/blueprints/TranslationBlueprint.ts | 2 +- .../extensions/createComponentExtension.tsx | 2 +- plugins/app/src/extensions/App.tsx | 24 +++++++++++++------ yarn.lock | 3 ++- 8 files changed, 30 insertions(+), 28 deletions(-) diff --git a/packages/app-next/package.json b/packages/app-next/package.json index fa37cce5c8..1aeaccbf97 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -24,6 +24,7 @@ "@backstage/frontend-plugin-api": "workspace:^", "@backstage/integration-react": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/plugin-app-visualizer": "workspace:^", "@backstage/plugin-auth-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 83afc2b7a9..51d824ba67 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -47,18 +47,12 @@ import { fetchApiRef, } from '@backstage/core-plugin-api'; import { getAvailableFeatures } from './discovery'; -import { - ApiFactoryRegistry, - ApiProvider, - ApiResolver, -} from '@backstage/core-app-api'; +import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api'; // TODO: Get rid of all of these // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { isProtectedApp } from '../../../core-app-api/src/app/isProtectedApp'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppThemeProvider } from '../../../core-app-api/src/app/AppThemeProvider'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader'; @@ -361,15 +355,11 @@ export function createSpecializedApp(options?: { .instance!.getData(coreExtensionData.reactElement); const AppComponent = () => ( - - - - {rootEl} - - - + + {rootEl} + ); return { diff --git a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts index de2df761ff..369ac052f5 100644 --- a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts @@ -25,7 +25,7 @@ const iconsDataRef = createExtensionDataRef<{ export const IconBundleBlueprint = createExtensionBlueprint({ kind: 'icon-bundle', namespace: 'app', - attachTo: { id: 'api:icons', input: 'icons' }, + attachTo: { id: 'api:app/icons', input: 'icons' }, output: [iconsDataRef], config: { schema: { diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts index c06078b122..ca33970125 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts @@ -29,7 +29,7 @@ const themeDataRef = createExtensionDataRef().with({ export const ThemeBlueprint = createExtensionBlueprint({ kind: 'theme', namespace: 'app', - attachTo: { id: 'api:app-theme', input: 'themes' }, + attachTo: { id: 'api:app/app-theme', input: 'themes' }, output: [themeDataRef], dataRefs: { theme: themeDataRef, diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts index ea7aa64713..dd4a9ddc09 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts @@ -28,7 +28,7 @@ const translationDataRef = createExtensionDataRef< */ export const TranslationBlueprint = createExtensionBlueprint({ kind: 'translation', - attachTo: { id: 'api:translations', input: 'translations' }, + attachTo: { id: 'api:app/translations', input: 'translations' }, output: [translationDataRef], dataRefs: { translation: translationDataRef, diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx index 35d003e99b..c8fa907bc8 100644 --- a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -35,7 +35,7 @@ export function createComponentExtension(options: { kind: 'component', namespace: options.ref.id, name: options.name, - attachTo: { id: 'api:components', input: 'components' }, + attachTo: { id: 'api:app/components', input: 'components' }, disabled: options.disabled, output: [createComponentExtension.componentDataRef], factory() { diff --git a/plugins/app/src/extensions/App.tsx b/plugins/app/src/extensions/App.tsx index 1eeba30f64..5c6d95bfbc 100644 --- a/plugins/app/src/extensions/App.tsx +++ b/plugins/app/src/extensions/App.tsx @@ -21,6 +21,10 @@ import { createExtension, createExtensionInput, } from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { ApiProvider } from '../../../../packages/core-app-api/src'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppThemeProvider } from '../../../../packages/core-app-api/src/app/AppThemeProvider'; export const App = createExtension({ namespace: 'app', @@ -31,11 +35,17 @@ export const App = createExtension({ }), }, output: [coreExtensionData.reactElement], - factory: ({ node, inputs }) => [ - coreExtensionData.reactElement( - - {inputs.root.get(coreExtensionData.reactElement)} - , - ), - ], + factory: ({ node, apis, inputs }) => { + return [ + coreExtensionData.reactElement( + + + + {inputs.root.get(coreExtensionData.reactElement)} + + + , + ), + ]; + }, }); diff --git a/yarn.lock b/yarn.lock index 07c9b607b7..f870a2b07d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4684,7 +4684,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-app@^0.0.0, @backstage/plugin-app@workspace:plugins/app": +"@backstage/plugin-app@^0.0.0, @backstage/plugin-app@workspace:^, @backstage/plugin-app@workspace:plugins/app": version: 0.0.0-use.local resolution: "@backstage/plugin-app@workspace:plugins/app" dependencies: @@ -26842,6 +26842,7 @@ __metadata: "@backstage/frontend-plugin-api": "workspace:^" "@backstage/integration-react": "workspace:^" "@backstage/plugin-api-docs": "workspace:^" + "@backstage/plugin-app": "workspace:^" "@backstage/plugin-app-visualizer": "workspace:^" "@backstage/plugin-auth-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" From 97a077583a82f54904254c477388708a121898d0 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 14:28:48 +0200 Subject: [PATCH 07/24] chore: move the appIdentityProxy across MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: blam --- .../frontend-app-api/src/wiring/createApp.tsx | 45 +++-------- plugins/app/src/extensions/AppRoot.tsx | 76 ++++++++++++++++--- 2 files changed, 79 insertions(+), 42 deletions(-) diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 51d824ba67..537c6c1d91 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -42,18 +42,12 @@ import { configApiRef, featureFlagsApiRef, identityApiRef, - errorApiRef, - discoveryApiRef, - fetchApiRef, } from '@backstage/core-plugin-api'; import { getAvailableFeatures } from './discovery'; import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api'; // TODO: Get rid of all of these -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { isProtectedApp } from '../../../core-app-api/src/app/isProtectedApp'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; + // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports @@ -67,7 +61,6 @@ import { CreateAppRouteBinder } from '../routing'; import { RouteResolver } from '../routing/RouteResolver'; import { resolveRouteBindings } from '../routing/resolveRouteBindings'; import { collectRouteIds } from '../routing/collectRouteIds'; -import { InternalAppContext } from './InternalAppContext'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports @@ -81,6 +74,9 @@ import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig'; import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; +import { BackstageRouteObject } from '../routing/types'; function deduplicateFeatures( allFeatures: FrontendFeature[], @@ -215,6 +211,7 @@ class AppTreeApiProxy implements AppTreeApi { // Helps delay callers from reaching out to the API before the app tree has been materialized class RouteResolutionApiProxy implements RouteResolutionApi { #delegate: RouteResolutionApi | undefined; + #routeObjects: BackstageRouteObject[] | undefined; constructor( private readonly tree: AppTree, @@ -251,9 +248,14 @@ class RouteResolutionApiProxy implements RouteResolutionApi { this.routeBindings, this.basePath, ); + this.#routeObjects = routeInfo.routeObjects; return routeInfo; } + + getRouteObjects() { + return this.#routeObjects; + } } /** @@ -285,7 +287,6 @@ export function createSpecializedApp(options?: { ); const factories = createApiFactories({ tree }); - const appTreeApi = new AppTreeApiProxy(tree); const routeResolutionApi = new RouteResolutionApiProxy( tree, @@ -312,25 +313,9 @@ export function createSpecializedApp(options?: { instantiateAppNodeTree(appNode, apiHolder); } - const routeInfo = routeResolutionApi.initialize(); + routeResolutionApi.initialize(); appTreeApi.initialize(); - if (isProtectedApp()) { - const discoveryApi = apiHolder.get(discoveryApiRef); - const errorApi = apiHolder.get(errorApiRef); - const fetchApi = apiHolder.get(fetchApiRef); - if (!discoveryApi || !errorApi || !fetchApi) { - throw new Error( - 'App is running in protected mode but missing required APIs', - ); - } - appIdentityProxy.enableCookieAuth({ - discoveryApi, - errorApi, - fetchApi, - }); - } - const featureFlagApi = apiHolder.get(featureFlagsApiRef); if (featureFlagApi) { for (const feature of features) { @@ -354,13 +339,7 @@ export function createSpecializedApp(options?: { .get('app')![0] .instance!.getData(coreExtensionData.reactElement); - const AppComponent = () => ( - - {rootEl} - - ); + const AppComponent = () => rootEl; return { createRoot() { diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx index e54699fac5..9906480ad6 100644 --- a/plugins/app/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -19,7 +19,6 @@ import React, { Fragment, PropsWithChildren, ReactNode, - useContext, useState, } from 'react'; import { @@ -27,19 +26,26 @@ import { RouterBlueprint, SignInPageBlueprint, coreExtensionData, + discoveryApiRef, + fetchApiRef, + errorApiRef, createExtension, createExtensionInput, + routeResolutionApiRef, } from '@backstage/frontend-plugin-api'; import { + DiscoveryApi, + ErrorApi, + FetchApi, IdentityApi, + ProfileInfo, SignInPageProps, configApiRef, + identityApiRef, useApi, } from '@backstage/core-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { InternalAppContext } from '../../../../packages/frontend-app-api/src/wiring/InternalAppContext'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppIdentityProxy } from '../../../../packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; +import { isProtectedApp } from '../../../../packages/core-app-api/src/app/isProtectedApp'; import { BrowserRouter } from 'react-router-dom'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { RouteTracker } from '../../../../packages/frontend-app-api/src/routing/RouteTracker'; @@ -68,7 +74,32 @@ export const AppRoot = createExtension({ ]), }, output: [coreExtensionData.reactElement], - factory({ inputs }) { + factory({ inputs, apis }) { + const identityApi = apis.get(identityApiRef); + if (!identityApi) { + throw new Error('App requires an Identity API implementation'); + } + if (!('enableCookieAuth' in identityApi)) { + throw new Error('Unexpected Identity API implementation'); + } + const appIdentityProxy = identityApi as AppIdentityProxy; + + if (isProtectedApp()) { + const discoveryApi = apis.get(discoveryApiRef); + const errorApi = apis.get(errorApiRef); + const fetchApi = apis.get(fetchApiRef); + if (!discoveryApi || !errorApi || !fetchApi) { + throw new Error( + 'App is running in protected mode but missing required APIs', + ); + } + appIdentityProxy.enableCookieAuth({ + discoveryApi, + errorApi, + fetchApi, + }); + } + let content: React.ReactNode = ( <> {inputs.elements.map(el => ( @@ -88,6 +119,7 @@ export const AppRoot = createExtension({ return [ coreExtensionData.reactElement( {children}; } +type AppIdentityProxy = IdentityApi & { + enableCookieAuth(ctx: { + errorApi: ErrorApi; + fetchApi: FetchApi; + discoveryApi: DiscoveryApi; + }): void; + setTarget( + impl: IdentityApi & /* backwards compat stuff */ { + getUserId?(): string; + getIdToken?(): Promise; + getProfile?(): ProfileInfo; + }, + options: { signOutTargetUrl: string }, + ): void; +}; + +type RouteResolverProxy = { + getRouteObjects(): any[]; +}; + /** * Props for the {@link AppRouter} component. * @public */ export interface AppRouterProps { children?: ReactNode; + appIdentityProxy: AppIdentityProxy; SignInPageComponent?: ComponentType; RouterComponent?: ComponentType>; } @@ -155,17 +208,22 @@ function DefaultRouter(props: PropsWithChildren<{}>) { export function AppRouter(props: AppRouterProps) { const { children, + appIdentityProxy, SignInPageComponent, RouterComponent = DefaultRouter, } = props; const configApi = useApi(configApiRef); + const routeResolutionsApi = useApi(routeResolutionApiRef); const basePath = getBasePath(configApi); - const internalAppContext = useContext(InternalAppContext); - if (!internalAppContext) { - throw new Error('AppRouter must be rendered within the AppProvider'); + + // TODO: Private access for now, probably replace with path -> node lookup method on the API + if (!('getRouteObjects' in routeResolutionsApi)) { + throw new Error('Unexpected route resolution API implementation'); } - const { routeObjects, appIdentityProxy } = internalAppContext; + const routeObjects = ( + routeResolutionsApi as RouteResolverProxy + ).getRouteObjects(); // If the app hasn't configured a sign-in page, we just continue as guest. if (!SignInPageComponent) { From 7e9af96f7c7fe7db3ff03a3bae6a2f48f8f7ebc6 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 14:33:06 +0200 Subject: [PATCH 08/24] chore: make the tree.root output a react element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: blam --- packages/frontend-app-api/src/extensions/Root.ts | 4 ++-- packages/frontend-app-api/src/wiring/createApp.tsx | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/frontend-app-api/src/extensions/Root.ts b/packages/frontend-app-api/src/extensions/Root.ts index a613a8dcfa..3b1c237e7a 100644 --- a/packages/frontend-app-api/src/extensions/Root.ts +++ b/packages/frontend-app-api/src/extensions/Root.ts @@ -32,6 +32,6 @@ export const Root = createExtension({ replaces: [{ id: 'app', input: 'apis' }], }), }, - output: [], - factory: () => [], + output: [coreExtensionData.reactElement], + factory: ({ inputs }) => inputs.app, }); diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 537c6c1d91..109bb0b0ed 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -309,9 +309,8 @@ export function createSpecializedApp(options?: { ], }); - for (const appNode of tree.root.edges.attachments.get('app') ?? []) { - instantiateAppNodeTree(appNode, apiHolder); - } + // Now instantiate the entire tree, which will skip anything that's already been instantiated + instantiateAppNodeTree(tree.root, apiHolder); routeResolutionApi.initialize(); appTreeApi.initialize(); @@ -335,9 +334,7 @@ export function createSpecializedApp(options?: { } } - const rootEl = tree.root.edges.attachments - .get('app')![0] - .instance!.getData(coreExtensionData.reactElement); + const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement); const AppComponent = () => rootEl; From f66f603ef57525e6502b4ab54f76487a8766b030 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 15:06:16 +0200 Subject: [PATCH 09/24] chore: fix some tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: blam --- packages/app/package.json | 1 - packages/app/src/App.tsx | 2 - packages/frontend-app-api/package.json | 1 + .../extractRouteInfoFromAppNode.test.ts | 13 +-- .../src/wiring/createApp.test.tsx | 89 ++++++++++--------- packages/frontend-plugin-api/package.json | 1 + .../src/blueprints/RouterBlueprint.test.tsx | 3 + .../src/blueprints/ThemeBlueprint.test.ts | 2 +- .../blueprints/TranslationBlueprint.test.ts | 2 +- .../src/wiring/createFrontendPlugin.test.ts | 3 +- packages/frontend-test-utils/package.json | 1 + .../src/app/renderInTestApp.tsx | 64 +++++++------ plugins/app/src/extensions/AppRoot.tsx | 25 +++--- 13 files changed, 109 insertions(+), 98 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 8ef8a54221..c27540f63e 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -45,7 +45,6 @@ "@backstage/frontend-app-api": "workspace:^", "@backstage/integration-react": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", - "@backstage/plugin-app": "^0.0.0", "@backstage/plugin-auth-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index cc7512464a..ba556e8616 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -82,7 +82,6 @@ import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; import { DevToolsPage } from '@backstage/plugin-devtools'; import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unprocessed-entities'; import { NotificationsPage } from '@backstage/plugin-notifications'; -import { AppPage } from '@backstage/plugin-app'; const app = createApp({ apis, @@ -209,7 +208,6 @@ const routes = ( {customDevToolsPage} } /> - } /> ); diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 3f13b4ad93..43cd9e53dd 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -48,6 +48,7 @@ }, "devDependencies": { "@backstage/cli": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^15.0.0" diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts index 4d450f0ae4..4e1c37e9a0 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts @@ -29,12 +29,15 @@ import { createRouteRef, } from '@backstage/frontend-plugin-api'; import { MockConfigApi, TestApiRegistry } from '@backstage/test-utils'; +import appPlugin from '@backstage/plugin-app'; -import { builtinExtensions } from '../wiring/createApp'; import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig'; import { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs'; import { resolveAppTree } from '../tree/resolveAppTree'; import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree'; +import { Root } from '../extensions/Root'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; const ref1 = createRouteRef(); const ref2 = createRouteRef(); @@ -83,12 +86,12 @@ function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { }); const tree = resolveAppTree( - 'app', + 'root', resolveAppNodeSpecs({ - features: [plugin], - builtinExtensions, + features: [appPlugin, plugin], + builtinExtensions: [resolveExtensionDefinition(Root)], parameters: readAppExtensionsConfig(new MockConfigApi({})), - forbidden: new Set(['app']), + forbidden: new Set(['root']), }), ); diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index e29a3afe94..2e54aadf58 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -29,6 +29,7 @@ import { CreateAppFeatureLoader, createApp } from './createApp'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import React from 'react'; import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api'; +import appPlugin from '@backstage/plugin-app'; describe('createApp', () => { it('should allow themes to be installed', async () => { @@ -44,6 +45,7 @@ describe('createApp', () => { }), }), features: [ + appPlugin, createFrontendPlugin({ id: 'test', extensions: [ @@ -73,6 +75,7 @@ describe('createApp', () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), features: [ + appPlugin, createFrontendPlugin({ id: duplicatedFeatureId, extensions: [ @@ -136,7 +139,7 @@ describe('createApp', () => { configLoader: async () => ({ config: new MockConfigApi({ key: 'config-value' }), }), - features: [loader], + features: [appPlugin, loader], }); await renderWithEffects(app.createRoot()); @@ -174,6 +177,7 @@ describe('createApp', () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), features: [ + appPlugin, createFrontendPlugin({ id: 'test', featureFlags: [{ name: 'test-1' }], @@ -229,6 +233,7 @@ describe('createApp', () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), features: [ + appPlugin, createFrontendPlugin({ id: 'my-plugin', extensions: [ @@ -255,13 +260,53 @@ describe('createApp', () => { const { tree } = appTreeApi!.getTree(); expect(String(tree.root)).toMatchInlineSnapshot(` - " + " + apis [ + + + + + + + + + + + + + + + + + + + + + themes [ + + + ] + + + components [ + + + + ] + + + + + ] app [ root [ children [ + nav [ + + ] content [ routes [ @@ -269,9 +314,6 @@ describe('createApp', () => { ] ] - nav [ - - ] ] elements [ @@ -282,43 +324,6 @@ describe('createApp', () => { ] ] - apis [ - - themes [ - - - ] - - - - - - components [ - - - - ] - - - - - - - - - - - - - - - - - - - - - ] " `); }); diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index c76bae094e..1983ed54d2 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -45,6 +45,7 @@ "@backstage/cli": "workspace:^", "@backstage/frontend-app-api": "workspace:^", "@backstage/frontend-test-utils": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^15.0.0", diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index 405c673d60..62b55ec4a6 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -26,6 +26,7 @@ import { } from '../wiring'; import { MockConfigApi } from '@backstage/test-utils'; import { PageBlueprint } from './PageBlueprint'; +import appPlugin from '@backstage/plugin-app'; describe('RouterBlueprint', () => { it('should return an extension when calling make with sensible defaults', () => { @@ -74,6 +75,7 @@ describe('RouterBlueprint', () => { const app = createSpecializedApp({ features: [ + appPlugin, createExtensionOverrides({ extensions: [ extension, @@ -126,6 +128,7 @@ describe('RouterBlueprint', () => { const app = createSpecializedApp({ features: [ + appPlugin, createExtensionOverrides({ extensions: [ extension, diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts index 71c5f9b918..6d3f6280a3 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts @@ -33,7 +33,7 @@ describe('ThemeBlueprint', () => { "$$type": "@backstage/ExtensionDefinition", "T": undefined, "attachTo": { - "id": "api:app-theme", + "id": "api:app/app-theme", "input": "themes", }, "configSchema": undefined, diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts index 7d045b5f6c..6bccd68c53 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts @@ -48,7 +48,7 @@ describe('TranslationBlueprint', () => { "$$type": "@backstage/ExtensionDefinition", "T": undefined, "attachTo": { - "id": "api:translations", + "id": "api:app/translations", "input": "translations", }, "configSchema": undefined, diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts index 63256d9a3a..df393b9807 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts @@ -25,6 +25,7 @@ import { coreExtensionData } from './coreExtensionData'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import { createExtensionInput } from './createExtensionInput'; import { BackstagePlugin } from './types'; +import appPlugin from '@backstage/plugin-app'; const nameExtensionDataRef = createExtensionDataRef().with({ id: 'name', @@ -127,7 +128,7 @@ function createTestAppRoot({ config: JsonObject; }) { return createApp({ - features, + features: [...features, appPlugin], configLoader: async () => ({ config: new MockConfigApi(config) }), }).createRoot(); } diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 526e8b1713..ed53d7c1f8 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -34,6 +34,7 @@ "@backstage/config": "workspace:^", "@backstage/frontend-app-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@backstage/types": "workspace:^" }, diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index cc919f3f3d..7923050050 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -27,12 +27,12 @@ import { coreExtensionData, RouteRef, useRouteRef, - createExtensionInput, IconComponent, RouterBlueprint, NavItemBlueprint, FrontendFeature, } from '@backstage/frontend-plugin-api'; +import appPlugin from '@backstage/plugin-app'; /** * Options to customize the behavior of the test app. @@ -92,38 +92,36 @@ const NavItem = (props: { ); }; -export const TestAppNavExtension = createExtension({ - namespace: 'app', - name: 'nav', - attachTo: { id: 'app/layout', input: 'nav' }, - inputs: { - items: createExtensionInput([NavItemBlueprint.dataRefs.target]), - }, - output: [coreExtensionData.reactElement], - factory({ inputs }) { - return [ - coreExtensionData.reactElement( - , + ), + ]; + }, + }), + ], }); /** @@ -152,7 +150,6 @@ export function renderInTestApp( Component: ({ children }) => {children}, }, }), - TestAppNavExtension, ]; if (options?.mountedRoutes) { @@ -183,6 +180,7 @@ export function renderInTestApp( } const features: FrontendFeature[] = [ + appPluginOverride, createExtensionOverrides({ extensions, }), diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx index 9906480ad6..bd786765f9 100644 --- a/plugins/app/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -75,16 +75,12 @@ export const AppRoot = createExtension({ }, output: [coreExtensionData.reactElement], factory({ inputs, apis }) { - const identityApi = apis.get(identityApiRef); - if (!identityApi) { - throw new Error('App requires an Identity API implementation'); - } - if (!('enableCookieAuth' in identityApi)) { - throw new Error('Unexpected Identity API implementation'); - } - const appIdentityProxy = identityApi as AppIdentityProxy; - if (isProtectedApp()) { + const identityApi = apis.get(identityApiRef); + if (!identityApi) { + throw new Error('App requires an Identity API implementation'); + } + const appIdentityProxy = toAppIdentityProxy(identityApi); const discoveryApi = apis.get(discoveryApiRef); const errorApi = apis.get(errorApiRef); const fetchApi = apis.get(fetchApiRef); @@ -119,7 +115,6 @@ export const AppRoot = createExtension({ return [ coreExtensionData.reactElement( ; RouterComponent?: ComponentType>; } @@ -208,12 +209,12 @@ function DefaultRouter(props: PropsWithChildren<{}>) { export function AppRouter(props: AppRouterProps) { const { children, - appIdentityProxy, SignInPageComponent, RouterComponent = DefaultRouter, } = props; const configApi = useApi(configApiRef); + const appIdentityProxy = toAppIdentityProxy(useApi(identityApiRef)); const routeResolutionsApi = useApi(routeResolutionApiRef); const basePath = getBasePath(configApi); From ea6e94a83a3b172e62f83c5141049997973e5b80 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 15:09:15 +0200 Subject: [PATCH 10/24] chore: fix api-reports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: blam --- plugins/app/api-report.md | 396 +++++++++++++++++++------------------- 1 file changed, 198 insertions(+), 198 deletions(-) diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md index 9618474641..32c72d724b 100644 --- a/plugins/app/api-report.md +++ b/plugins/app/api-report.md @@ -59,204 +59,6 @@ const appPlugin: BackstagePlugin< name: string; } >; - 'api:app/discovery': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'discovery'; - } - >; - 'api:app/alert': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'alert'; - } - >; - 'api:app/analytics': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'analytics'; - } - >; - 'api:app/error': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'error'; - } - >; - 'api:app/storage': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'storage'; - } - >; - 'api:app/fetch': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'fetch'; - } - >; - 'api:app/oauth-request': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'oauth-request'; - } - >; - 'api:app/google-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'google-auth'; - } - >; - 'api:app/microsoft-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'microsoft-auth'; - } - >; - 'api:app/github-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'github-auth'; - } - >; - 'api:app/okta-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'okta-auth'; - } - >; - 'api:app/gitlab-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'gitlab-auth'; - } - >; - 'api:app/onelogin-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'onelogin-auth'; - } - >; - 'api:app/bitbucket-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'bitbucket-auth'; - } - >; - 'api:app/bitbucket-server-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'bitbucket-server-auth'; - } - >; - 'api:app/atlassian-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'atlassian-auth'; - } - >; - 'api:app/vmware-cloud-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'vmware-cloud-auth'; - } - >; - 'api:app/permission': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'permission'; - } - >; app: ExtensionDefinition< { [x: string]: any; @@ -657,6 +459,204 @@ const appPlugin: BackstagePlugin< name: 'alert-display'; } >; + 'api:app/discovery': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'discovery'; + } + >; + 'api:app/alert': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'alert'; + } + >; + 'api:app/analytics': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'analytics'; + } + >; + 'api:app/error': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'error'; + } + >; + 'api:app/storage': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'storage'; + } + >; + 'api:app/fetch': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'fetch'; + } + >; + 'api:app/oauth-request': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'oauth-request'; + } + >; + 'api:app/google-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'google-auth'; + } + >; + 'api:app/microsoft-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'microsoft-auth'; + } + >; + 'api:app/github-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'github-auth'; + } + >; + 'api:app/okta-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'okta-auth'; + } + >; + 'api:app/gitlab-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'gitlab-auth'; + } + >; + 'api:app/onelogin-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'onelogin-auth'; + } + >; + 'api:app/bitbucket-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'bitbucket-auth'; + } + >; + 'api:app/bitbucket-server-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'bitbucket-server-auth'; + } + >; + 'api:app/atlassian-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'atlassian-auth'; + } + >; + 'api:app/vmware-cloud-auth': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'vmware-cloud-auth'; + } + >; + 'api:app/permission': ExtensionDefinition< + {}, + {}, + ConfigurableExtensionDataRef, + {}, + { + kind: 'api'; + namespace: undefined; + name: 'permission'; + } + >; } >; export default appPlugin; From acac2a208f9cd5f1e4e2114d6d1598282309aacc Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 26 Aug 2024 15:20:46 +0200 Subject: [PATCH 11/24] chore: yarn.lock Signed-off-by: blam --- yarn.lock | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index f870a2b07d..1dcd4aed4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4440,6 +4440,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/plugin-app": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@backstage/types": "workspace:^" @@ -4465,6 +4466,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/frontend-app-api": "workspace:^" "@backstage/frontend-test-utils": "workspace:^" + "@backstage/plugin-app": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@backstage/version-bridge": "workspace:^" @@ -4490,6 +4492,7 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/frontend-app-api": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/plugin-app": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@testing-library/jest-dom": ^6.0.0 @@ -4684,7 +4687,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-app@^0.0.0, @backstage/plugin-app@workspace:^, @backstage/plugin-app@workspace:plugins/app": +"@backstage/plugin-app@workspace:^, @backstage/plugin-app@workspace:plugins/app": version: 0.0.0-use.local resolution: "@backstage/plugin-app@workspace:plugins/app" dependencies: @@ -26922,7 +26925,6 @@ __metadata: "@backstage/frontend-app-api": "workspace:^" "@backstage/integration-react": "workspace:^" "@backstage/plugin-api-docs": "workspace:^" - "@backstage/plugin-app": ^0.0.0 "@backstage/plugin-auth-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" From 1ca243ddc41db9774357bc45af885d52ca9aa923 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 10:57:42 +0200 Subject: [PATCH 12/24] chore: fixing tests for AppRootWrapperBlueprint Co-authored-by: Patrik Oldsberg Signed-off-by: blam --- .../frontend-app-api/src/wiring/createApp.tsx | 1 - .../AppRootWrapperBlueprint.test.tsx | 48 ++++++------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 109bb0b0ed..63dae872b9 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -22,7 +22,6 @@ import { AppTreeApi, appTreeApiRef, coreExtensionData, - ExtensionDefinition, FrontendFeature, RouteRef, ExternalRouteRef, diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx index 6e725324a1..cda0085fec 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx @@ -16,15 +16,13 @@ import React from 'react'; import { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint'; -import { render, screen, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { coreExtensionData, createExtension, createExtensionInput, - createFrontendPlugin, } from '../wiring'; -import { createSpecializedApp } from '@backstage/frontend-app-api'; -import { MockConfigApi } from '@backstage/test-utils'; +import { renderInTestApp } from '@backstage/frontend-test-utils'; describe('AppRootWrapperBlueprint', () => { it('should return an extension with sensible defaults', () => { @@ -61,27 +59,20 @@ describe('AppRootWrapperBlueprint', () => { it('should render the simple component wrapper', async () => { const extension = AppRootWrapperBlueprint.make({ + name: 'test', params: { Component: () =>
Hello
, }, }); - const app = createSpecializedApp({ - features: [ - createFrontendPlugin({ - id: 'test', - extensions: [extension], - }), - ], - }); - - render(app.createRoot()); + renderInTestApp(
, { extensions: [extension] }); await waitFor(() => expect(screen.getByText('Hello')).toBeInTheDocument()); }); it('should render the complex component wrapper', async () => { const extension = AppRootWrapperBlueprint.makeWithOverrides({ + name: 'test', config: { schema: { name: z => z.string(), @@ -104,24 +95,17 @@ describe('AppRootWrapperBlueprint', () => { }, }); - const app = createSpecializedApp({ - features: [ - createFrontendPlugin({ - id: 'test', - extensions: [ - extension, - createExtension({ - name: 'test-child', - attachTo: { id: 'app-root-wrapper:test', input: 'children' }, - output: [coreExtensionData.reactElement], - factory: () => [ - coreExtensionData.reactElement(
Its Me
), - ], - }), - ], + renderInTestApp(
, { + extensions: [ + extension, + createExtension({ + name: 'test-child', + attachTo: { id: 'app-root-wrapper:test', input: 'children' }, + output: [coreExtensionData.reactElement], + factory: () => [coreExtensionData.reactElement(
Its Me
)], }), ], - config: new MockConfigApi({ + config: { app: { extensions: [ { @@ -129,11 +113,9 @@ describe('AppRootWrapperBlueprint', () => { }, ], }, - }), + }, }); - render(app.createRoot()); - await waitFor(() => { expect(screen.getByTestId('Robin-1')).toBeInTheDocument(); expect(screen.getByText('Its Me')).toBeInTheDocument(); From e1ea702eca82dd31e285be07f287c2dd8349d1df Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:10:49 +0200 Subject: [PATCH 13/24] chore: fix some tests Signed-off-by: blam --- .../src/blueprints/RouterBlueprint.test.tsx | 83 +- plugins/app/api-report.md | 872 +++++++++--------- 2 files changed, 477 insertions(+), 478 deletions(-) diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index 62b55ec4a6..0ce21b5d6d 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -16,17 +16,14 @@ import React from 'react'; import { RouterBlueprint } from './RouterBlueprint'; import { MemoryRouter } from 'react-router-dom'; -import { render, waitFor } from '@testing-library/react'; -import { createSpecializedApp } from '@backstage/frontend-app-api'; +import { waitFor } from '@testing-library/react'; import { coreExtensionData, createExtension, createExtensionInput, - createExtensionOverrides, } from '../wiring'; -import { MockConfigApi } from '@backstage/test-utils'; import { PageBlueprint } from './PageBlueprint'; -import appPlugin from '@backstage/plugin-app'; +import { renderInTestApp } from '@backstage/frontend-test-utils'; describe('RouterBlueprint', () => { it('should return an extension when calling make with sensible defaults', () => { @@ -73,26 +70,19 @@ describe('RouterBlueprint', () => { }, }); - const app = createSpecializedApp({ - features: [ - appPlugin, - createExtensionOverrides({ - extensions: [ - extension, - PageBlueprint.make({ - namespace: 'test', - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ], + const { getByTestId } = renderInTestApp(
, { + extensions: [ + extension, + PageBlueprint.make({ + namespace: 'test', + params: { + defaultPath: '/', + loader: async () =>
, + }, }), ], }); - const { getByTestId } = render(app.createRoot()); - await waitFor(() => { expect(getByTestId('test-contents')).toBeInTheDocument(); expect(getByTestId('test-router')).toBeInTheDocument(); @@ -126,34 +116,29 @@ describe('RouterBlueprint', () => { }, }); - const app = createSpecializedApp({ - features: [ - appPlugin, - createExtensionOverrides({ - extensions: [ - extension, - createExtension({ - namespace: 'test', - attachTo: { - id: 'app-router-component:test/test', - input: 'children', - }, - output: [coreExtensionData.reactElement], - *factory() { - yield coreExtensionData.reactElement(
); - }, - }), - PageBlueprint.make({ - namespace: 'test', - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ], + const { getByTestId } = renderInTestApp(
, { + extensions: [ + extension, + createExtension({ + namespace: 'test', + attachTo: { + id: 'app-router-component:test/test', + input: 'children', + }, + output: [coreExtensionData.reactElement], + *factory() { + yield coreExtensionData.reactElement(
); + }, + }), + PageBlueprint.make({ + namespace: 'test', + params: { + defaultPath: '/', + loader: async () =>
, + }, }), ], - config: new MockConfigApi({ + config: { app: { extensions: [ { @@ -161,11 +146,9 @@ describe('RouterBlueprint', () => { }, ], }, - }), + }, }); - const { getByTestId } = render(app.createRoot()); - await waitFor(() => { expect(getByTestId('test-contents')).toBeInTheDocument(); expect(getByTestId('test-router-Robin-1')).toBeInTheDocument(); diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md index 32c72d724b..73f5076ef3 100644 --- a/plugins/app/api-report.md +++ b/plugins/app/api-report.md @@ -29,22 +29,18 @@ const appPlugin: BackstagePlugin< {}, {}, { - [x: `component:app/${string}`]: ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef< + [x: `component:app/${string}`]: ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { ref: ComponentRef; impl: ComponentType; }, 'core.component.component', {} - >, - { + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -52,22 +48,20 @@ const appPlugin: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'component'; - namespace: string; - name: string; - } - >; - app: ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: 'component'; + namespace: string; + name: string; + }>; + app: ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { root: ExtensionInput< ConfigurableExtensionDataRef, { @@ -75,33 +69,33 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: undefined; - namespace: 'app'; - name: undefined; - } - >; - 'api:app/app-language': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'app-language'; - } - >; - 'app/layout': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: undefined; + namespace: 'app'; + name: undefined; + }>; + 'api:app/app-language': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'app-language'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'app/layout': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { nav: ExtensionInput< ConfigurableExtensionDataRef, { @@ -116,22 +110,20 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: undefined; - namespace: 'app'; - name: 'layout'; - } - >; - 'app/nav': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: undefined; + namespace: 'app'; + name: 'layout'; + }>; + 'app/nav': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { items: ExtensionInput< ConfigurableExtensionDataRef< { @@ -161,22 +153,20 @@ const appPlugin: BackstagePlugin< optional: true; } >; - }, - { - kind: undefined; - namespace: 'app'; - name: 'nav'; - } - >; - 'app/root': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: undefined; + namespace: 'app'; + name: 'nav'; + }>; + 'app/root': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { router: ExtensionInput< ConfigurableExtensionDataRef< ComponentType<{ @@ -228,22 +218,20 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: undefined; - namespace: 'app'; - name: 'root'; - } - >; - 'app/routes': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: undefined; + namespace: 'app'; + name: 'root'; + }>; + 'app/routes': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { routes: ExtensionInput< | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef @@ -259,22 +247,20 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: undefined; - namespace: 'app'; - name: 'routes'; - } - >; - 'api:app/app-theme': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: undefined; + namespace: 'app'; + name: 'routes'; + }>; + 'api:app/app-theme': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { themes: ExtensionInput< ConfigurableExtensionDataRef, { @@ -282,44 +268,38 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: 'api'; - namespace: undefined; - name: 'app-theme'; - } - >; - 'theme:app/light': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'theme'; - namespace: 'app'; - name: 'light'; - } - >; - 'theme:app/dark': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'theme'; - namespace: 'app'; - name: 'dark'; - } - >; - 'api:app/components': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: 'api'; + namespace: undefined; + name: 'app-theme'; + }>; + 'theme:app/light': ExtensionDefinition<{ + kind: 'theme'; + namespace: 'app'; + name: 'light'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef; + inputs: {}; + }>; + 'theme:app/dark': ExtensionDefinition<{ + kind: 'theme'; + namespace: 'app'; + name: 'dark'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef; + inputs: {}; + }>; + 'api:app/components': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { components: ExtensionInput< ConfigurableExtensionDataRef< { @@ -334,22 +314,20 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: 'api'; - namespace: undefined; - name: 'components'; - } - >; - 'api:app/icons': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: 'api'; + namespace: undefined; + name: 'components'; + }>; + 'api:app/icons': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { icons: ExtensionInput< ConfigurableExtensionDataRef< { @@ -363,33 +341,33 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: 'api'; - namespace: undefined; - name: 'icons'; - } - >; - 'api:app/feature-flags': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'feature-flags'; - } - >; - 'api:app/translations': ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef, - { + }; + kind: 'api'; + namespace: undefined; + name: 'icons'; + }>; + 'api:app/feature-flags': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'feature-flags'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/translations': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { translations: ExtensionInput< ConfigurableExtensionDataRef< | TranslationResource @@ -408,33 +386,33 @@ const appPlugin: BackstagePlugin< optional: false; } >; - }, - { - kind: 'api'; - namespace: undefined; - name: 'translations'; - } - >; - 'app-root-element:app/oauth-request-dialog': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'app-root-element'; - namespace: 'app'; - name: 'oauth-request-dialog'; - } - >; - 'app-root-element:app/alert-display': ExtensionDefinition< - { + }; + kind: 'api'; + namespace: undefined; + name: 'translations'; + }>; + 'app-root-element:app/oauth-request-dialog': ExtensionDefinition<{ + kind: 'app-root-element'; + namespace: 'app'; + name: 'oauth-request-dialog'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'app-root-element:app/alert-display': ExtensionDefinition<{ + config: { transientTimeoutMs: number; anchorOrigin: { horizontal: 'center' | 'left' | 'right'; vertical: 'top' | 'bottom'; }; - }, - { + }; + configInput: { anchorOrigin?: | { horizontal?: 'center' | 'left' | 'right' | undefined; @@ -442,9 +420,13 @@ const appPlugin: BackstagePlugin< } | undefined; transientTimeoutMs?: number | undefined; - }, - ConfigurableExtensionDataRef, - { + }; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -452,211 +434,245 @@ const appPlugin: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'app-root-element'; - namespace: 'app'; - name: 'alert-display'; - } - >; - 'api:app/discovery': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'discovery'; - } - >; - 'api:app/alert': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'alert'; - } - >; - 'api:app/analytics': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'analytics'; - } - >; - 'api:app/error': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'error'; - } - >; - 'api:app/storage': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'storage'; - } - >; - 'api:app/fetch': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'fetch'; - } - >; - 'api:app/oauth-request': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'oauth-request'; - } - >; - 'api:app/google-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'google-auth'; - } - >; - 'api:app/microsoft-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'microsoft-auth'; - } - >; - 'api:app/github-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'github-auth'; - } - >; - 'api:app/okta-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'okta-auth'; - } - >; - 'api:app/gitlab-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'gitlab-auth'; - } - >; - 'api:app/onelogin-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'onelogin-auth'; - } - >; - 'api:app/bitbucket-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'bitbucket-auth'; - } - >; - 'api:app/bitbucket-server-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'bitbucket-server-auth'; - } - >; - 'api:app/atlassian-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'atlassian-auth'; - } - >; - 'api:app/vmware-cloud-auth': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'vmware-cloud-auth'; - } - >; - 'api:app/permission': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'permission'; - } - >; + }; + kind: 'app-root-element'; + namespace: 'app'; + name: 'alert-display'; + }>; + 'api:app/discovery': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'discovery'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/alert': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'alert'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/analytics': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'analytics'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/error': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'error'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/storage': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'storage'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/fetch': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'fetch'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/oauth-request': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'oauth-request'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/google-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'google-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/microsoft-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'microsoft-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/github-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'github-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/okta-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'okta-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/gitlab-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'gitlab-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/onelogin-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'onelogin-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/bitbucket-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'bitbucket-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/bitbucket-server-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'bitbucket-server-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/atlassian-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'atlassian-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/vmware-cloud-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'vmware-cloud-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/permission': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'permission'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; } >; export default appPlugin; From 99b799e65a692ff19e2d0e76a53d9625618cc89a Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:14:22 +0200 Subject: [PATCH 14/24] chore: fixing dependency duplication ] Signed-off-by: blam Signed-off-by: blam --- plugins/app/package.json | 2 +- yarn.lock | 113 ++++----------------------------------- 2 files changed, 12 insertions(+), 103 deletions(-) diff --git a/plugins/app/package.json b/plugins/app/package.json index 5c5f631c54..b729645449 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -53,7 +53,7 @@ "@backstage/dev-utils": "workspace:^", "@backstage/frontend-test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^14.0.0", + "@testing-library/react": "^15.0.0", "@testing-library/user-event": "^14.0.0", "msw": "^1.0.0", "react": "^16.13.1 || ^17.0.0 || ^18.0.0" diff --git a/yarn.lock b/yarn.lock index 1dcd4aed4d..c343506584 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4703,7 +4703,7 @@ __metadata: "@material-ui/icons": ^4.9.1 "@material-ui/lab": ^4.0.0-alpha.61 "@testing-library/jest-dom": ^6.0.0 - "@testing-library/react": ^14.0.0 + "@testing-library/react": ^15.0.0 "@testing-library/user-event": ^14.0.0 msw: ^1.0.0 react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -16984,22 +16984,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:^9.0.0": - version: 9.3.4 - resolution: "@testing-library/dom@npm:9.3.4" - dependencies: - "@babel/code-frame": ^7.10.4 - "@babel/runtime": ^7.12.5 - "@types/aria-query": ^5.0.1 - aria-query: 5.1.3 - chalk: ^4.1.0 - dom-accessibility-api: ^0.5.9 - lz-string: ^1.5.0 - pretty-format: ^27.0.2 - checksum: dfd6fb0d6c7b4dd716ba3c47309bc9541b4a55772cb61758b4f396b3785efe2dbc75dc63423545c039078c7ffcc5e4b8c67c2db1b6af4799580466036f70026f - languageName: node - linkType: hard - "@testing-library/jest-dom@npm:^6.0.0": version: 6.4.2 resolution: "@testing-library/jest-dom@npm:6.4.2" @@ -17055,20 +17039,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:^14.0.0": - version: 14.3.1 - resolution: "@testing-library/react@npm:14.3.1" - dependencies: - "@babel/runtime": ^7.12.5 - "@testing-library/dom": ^9.0.0 - "@types/react-dom": ^18.0.0 - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - checksum: b057d4c9db5a523acfc24d7bc4665a924ab8d6f252c7f51eecf7dd30f1239413e1134925fd5cc9cbdef80496af64c04e6719b2081f89fe05ba87e8c6305bcc16 - languageName: node - linkType: hard - "@testing-library/react@npm:^15.0.0": version: 15.0.2 resolution: "@testing-library/react@npm:15.0.2" @@ -21030,15 +21000,6 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:5.1.3": - version: 5.1.3 - resolution: "aria-query@npm:5.1.3" - dependencies: - deep-equal: ^2.0.5 - checksum: 929ff95f02857b650fb4cbcd2f41072eee2f46159a6605ea03bf63aa572e35ffdff43d69e815ddc462e16e07de8faba3978afc2813650b4448ee18c9895d982b - languageName: node - linkType: hard - "aria-query@npm:5.3.0, aria-query@npm:^5.0.0, aria-query@npm:^5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" @@ -21055,7 +21016,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1": +"array-buffer-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "array-buffer-byte-length@npm:1.0.1" dependencies: @@ -24764,32 +24725,6 @@ __metadata: languageName: node linkType: hard -"deep-equal@npm:^2.0.5": - version: 2.2.3 - resolution: "deep-equal@npm:2.2.3" - dependencies: - array-buffer-byte-length: ^1.0.0 - call-bind: ^1.0.5 - es-get-iterator: ^1.1.3 - get-intrinsic: ^1.2.2 - is-arguments: ^1.1.1 - is-array-buffer: ^3.0.2 - is-date-object: ^1.0.5 - is-regex: ^1.1.4 - is-shared-array-buffer: ^1.0.2 - isarray: ^2.0.5 - object-is: ^1.1.5 - object-keys: ^1.1.1 - object.assign: ^4.1.4 - regexp.prototype.flags: ^1.5.1 - side-channel: ^1.0.4 - which-boxed-primitive: ^1.0.2 - which-collection: ^1.0.1 - which-typed-array: ^1.1.13 - checksum: ee8852f23e4d20a5626c13b02f415ba443a1b30b4b3d39eaf366d59c4a85e6545d7ec917db44d476a85ae5a86064f7e5f7af7479f38f113995ba869f3a1ddc53 - languageName: node - linkType: hard - "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -25866,23 +25801,6 @@ __metadata: languageName: node linkType: hard -"es-get-iterator@npm:^1.1.3": - version: 1.1.3 - resolution: "es-get-iterator@npm:1.1.3" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 - has-symbols: ^1.0.3 - is-arguments: ^1.1.1 - is-map: ^2.0.2 - is-set: ^2.0.2 - is-string: ^1.0.7 - isarray: ^2.0.5 - stop-iteration-iterator: ^1.0.0 - checksum: 8fa118da42667a01a7c7529f8a8cca514feeff243feec1ce0bb73baaa3514560bd09d2b3438873cf8a5aaec5d52da248131de153b28e2638a061b6e4df13267d - languageName: node - linkType: hard - "es-iterator-helpers@npm:^1.0.15, es-iterator-helpers@npm:^1.0.19": version: 1.0.19 resolution: "es-iterator-helpers@npm:1.0.19" @@ -28328,7 +28246,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": +"get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" dependencies: @@ -29991,7 +29909,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.7": +"internal-slot@npm:^1.0.7": version: 1.0.7 resolution: "internal-slot@npm:1.0.7" dependencies: @@ -30130,7 +30048,7 @@ __metadata: languageName: node linkType: hard -"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": +"is-arguments@npm:^1.0.4": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" dependencies: @@ -30140,7 +30058,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4": +"is-array-buffer@npm:^3.0.4": version: 3.0.4 resolution: "is-array-buffer@npm:3.0.4" dependencies: @@ -30438,7 +30356,7 @@ __metadata: languageName: node linkType: hard -"is-map@npm:^2.0.1, is-map@npm:^2.0.2": +"is-map@npm:^2.0.1": version: 2.0.3 resolution: "is-map@npm:2.0.3" checksum: e6ce5f6380f32b141b3153e6ba9074892bbbbd655e92e7ba5ff195239777e767a976dcd4e22f864accaf30e53ebf961ab1995424aef91af68788f0591b7396cc @@ -30655,7 +30573,7 @@ __metadata: languageName: node linkType: hard -"is-set@npm:^2.0.1, is-set@npm:^2.0.2": +"is-set@npm:^2.0.1": version: 2.0.3 resolution: "is-set@npm:2.0.3" checksum: 36e3f8c44bdbe9496c9689762cc4110f6a6a12b767c5d74c0398176aa2678d4467e3bf07595556f2dba897751bde1422480212b97d973c7b08a343100b0c0dfe @@ -35759,7 +35677,7 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.0.1, object-is@npm:^1.1.5": +"object-is@npm:^1.0.1": version: 1.1.6 resolution: "object-is@npm:1.1.6" dependencies: @@ -39472,7 +39390,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.2": +"regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" dependencies: @@ -41524,15 +41442,6 @@ __metadata: languageName: node linkType: hard -"stop-iteration-iterator@npm:^1.0.0": - version: 1.0.0 - resolution: "stop-iteration-iterator@npm:1.0.0" - dependencies: - internal-slot: ^1.0.4 - checksum: d04173690b2efa40e24ab70e5e51a3ff31d56d699550cfad084104ab3381390daccb36652b25755e420245f3b0737de66c1879eaa2a8d4fc0a78f9bf892fcb42 - languageName: node - linkType: hard - "stoppable@npm:^1.1.0": version: 1.1.0 resolution: "stoppable@npm:1.1.0" @@ -44808,7 +44717,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": +"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: From 2bb9517d4d22203a3766bb4d02e26e4f3f6ca6a7 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:23:51 +0200 Subject: [PATCH 15/24] chore: install the `@backstage/plugin-app` by default now Signed-off-by: blam --- .changeset/fresh-apes-dress.md | 8 +++++++ packages/frontend-app-api/package.json | 2 +- .../src/wiring/createApp.test.tsx | 23 +++++++++++++++++++ .../frontend-app-api/src/wiring/createApp.tsx | 5 +++- 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 .changeset/fresh-apes-dress.md diff --git a/.changeset/fresh-apes-dress.md b/.changeset/fresh-apes-dress.md new file mode 100644 index 0000000000..6e20e53bcd --- /dev/null +++ b/.changeset/fresh-apes-dress.md @@ -0,0 +1,8 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-test-utils': patch +'@backstage/frontend-app-api': patch +'@backstage/plugin-app': minor +--- + +Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 43cd9e53dd..b901896b6a 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -38,6 +38,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/theme": "workspace:^", "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", @@ -48,7 +49,6 @@ }, "devDependencies": { "@backstage/cli": "workspace:^", - "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^15.0.0" diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index 2e54aadf58..91bbde2991 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -359,4 +359,27 @@ describe('createApp', () => { expect(screen.queryByText('Custom loading message')).toBeNull(); }); + + it('should allow overriding the app plugin', async () => { + const app = createApp({ + configLoader: () => new Promise(() => {}), + features: [ + appPlugin.withOverrides({ + extensions: [ + appPlugin.getExtension('app/root').override({ + factory: () => [ + coreExtensionData.reactElement( +
Custom app root element
, + ), + ], + }), + ], + }), + ], + }); + + await renderWithEffects(app.createRoot()); + + expect(screen.queryByText('Custom app root element')).toBeNull(); + }); }); diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 63dae872b9..9148ffe527 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -76,6 +76,7 @@ import { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; import { BackstageRouteObject } from '../routing/types'; +import appPlugin from '@backstage/plugin-app'; function deduplicateFeatures( allFeatures: FrontendFeature[], @@ -269,10 +270,12 @@ export function createSpecializedApp(options?: { bindRoutes?(context: { bind: CreateAppRouteBinder }): void; }): { createRoot(): JSX.Element } { const { - features: duplicatedFeatures = [], + features: featuresWithoutApp = [], config = new ConfigReader({}, 'empty-config'), } = options ?? {}; + const duplicatedFeatures = [appPlugin, ...featuresWithoutApp]; + const features = deduplicateFeatures(duplicatedFeatures); const tree = resolveAppTree( From 85d86bcbc45694e69afe4845cd60354fa78b15eb Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:28:22 +0200 Subject: [PATCH 16/24] chore: small final cleanup Signed-off-by: blam --- packages/frontend-app-api/src/wiring/createApp.test.tsx | 3 --- packages/frontend-plugin-api/package.json | 1 - .../src/wiring/createFrontendPlugin.test.ts | 3 +-- yarn.lock | 1 - 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index 91bbde2991..908298cb9a 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -45,7 +45,6 @@ describe('createApp', () => { }), }), features: [ - appPlugin, createFrontendPlugin({ id: 'test', extensions: [ @@ -75,7 +74,6 @@ describe('createApp', () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), features: [ - appPlugin, createFrontendPlugin({ id: duplicatedFeatureId, extensions: [ @@ -233,7 +231,6 @@ describe('createApp', () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), features: [ - appPlugin, createFrontendPlugin({ id: 'my-plugin', extensions: [ diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 1983ed54d2..c76bae094e 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -45,7 +45,6 @@ "@backstage/cli": "workspace:^", "@backstage/frontend-app-api": "workspace:^", "@backstage/frontend-test-utils": "workspace:^", - "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^15.0.0", diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts index df393b9807..83ae085856 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts @@ -25,7 +25,6 @@ import { coreExtensionData } from './coreExtensionData'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import { createExtensionInput } from './createExtensionInput'; import { BackstagePlugin } from './types'; -import appPlugin from '@backstage/plugin-app'; const nameExtensionDataRef = createExtensionDataRef().with({ id: 'name', @@ -128,7 +127,7 @@ function createTestAppRoot({ config: JsonObject; }) { return createApp({ - features: [...features, appPlugin], + features: [...features], configLoader: async () => ({ config: new MockConfigApi(config) }), }).createRoot(); } diff --git a/yarn.lock b/yarn.lock index c343506584..061a144056 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4466,7 +4466,6 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/frontend-app-api": "workspace:^" "@backstage/frontend-test-utils": "workspace:^" - "@backstage/plugin-app": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@backstage/version-bridge": "workspace:^" From f83f0136dc2eeae98aca0373530898f15f8c66a4 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:38:04 +0200 Subject: [PATCH 17/24] chore: update the scripts to make frontned-app-api Signed-off-by: blam --- scripts/verify-local-dependencies.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/verify-local-dependencies.js b/scripts/verify-local-dependencies.js index 963bc40c9c..d3d76396e5 100755 --- a/scripts/verify-local-dependencies.js +++ b/scripts/verify-local-dependencies.js @@ -60,6 +60,8 @@ const roleRules = [ targetRole: 'frontend-plugin', except: [ // TODO(freben): Address these + '@backstage/frontend-app-api', + '@backstage/frontend-test-utils', '@backstage/plugin-api-docs', '@backstage/plugin-techdocs-addons-test-utils', ], From d3f79d13bc087ea8a590a22ae3eabb12482d6d84 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:40:03 +0200 Subject: [PATCH 18/24] chore: changeset for other packages Signed-off-by: blam --- .changeset/fresh-pumas-clean.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fresh-pumas-clean.md diff --git a/.changeset/fresh-pumas-clean.md b/.changeset/fresh-pumas-clean.md new file mode 100644 index 0000000000..2140f62651 --- /dev/null +++ b/.changeset/fresh-pumas-clean.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-app-backend': patch +'@backstage/plugin-app-node': patch +--- + +Fixing dependency metadata with the new `@backstage/plugin-app` package From b13351e38d658678ebfa2fdfcccc8038ecff5904 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 11:52:54 +0200 Subject: [PATCH 19/24] chore: some doc links Signed-off-by: blam --- docs/frontend-system/building-apps/08-migrating.md | 2 +- plugins/app/README.md | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md index ceb3f34c35..ddec37a4ea 100644 --- a/docs/frontend-system/building-apps/08-migrating.md +++ b/docs/frontend-system/building-apps/08-migrating.md @@ -495,7 +495,7 @@ The entity pages are typically defined in `packages/app/src/components/catalog` New apps feature a built-in sidebar extension (`app/nav`) that will render all nav item extensions provided by plugins. This is a placeholder implementation and not intended as a long-term solution. In the future we will aim to provide a more flexible sidebar extension that allows for more customization out of the box. -Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/25-extension-overrides.md): +Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/plugins/app/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/25-extension-overrides.md): ```tsx const nav = createExtension({ diff --git a/plugins/app/README.md b/plugins/app/README.md index e258a1671a..f54a8e0a4c 100644 --- a/plugins/app/README.md +++ b/plugins/app/README.md @@ -7,7 +7,3 @@ _This plugin was created through the Backstage CLI_ ## Getting started Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/app](http://localhost:3000/app). - -You can also serve the plugin in isolation by running `yarn start` in the plugin directory. -This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. -It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. From ff438f7b6673be3adf163af168c3f11183d53aab Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 12:11:08 +0200 Subject: [PATCH 20/24] chore: fixing issue with type deps Signed-off-by: blam --- plugins/app/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/app/package.json b/plugins/app/package.json index b729645449..acaaa9fe93 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -42,6 +42,7 @@ "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.61", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/yarn.lock b/yarn.lock index 061a144056..3b3f185038 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4704,6 +4704,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 "@testing-library/user-event": ^14.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react: ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 From 51046f5d27ac31307025cfe4b6b74b5877373d25 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 27 Aug 2024 13:07:54 +0200 Subject: [PATCH 21/24] core-compat-api: update compat wrapper test snapshot Signed-off-by: Patrik Oldsberg --- .../core-compat-api/src/compatWrapper/compatWrapper.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx index 595c49cf73..bee379a792 100644 --- a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx +++ b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx @@ -52,7 +52,7 @@ describe('BackwardsCompatProvider', () => { const app = useApp(); return (
- plugins: + plugins:{' '} {app .getPlugins() .map(p => p.getId()) @@ -74,7 +74,7 @@ describe('BackwardsCompatProvider', () => { ); expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(` - "plugins: + "plugins: app components: NotFoundErrorPage, BootErrorPage, Progress, Router, ErrorBoundaryFallback icons: brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, user, warning" `); From 03e9125c0edfe1c4e9e172ce6b28f72ab4b77a78 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 13:11:00 +0200 Subject: [PATCH 22/24] chore: fixing tests for RouterBlueprint Signed-off-by: blam --- .../frontend-app-api/src/wiring/createApp.tsx | 4 +- .../src/blueprints/RouterBlueprint.test.tsx | 79 ++++++++----------- 2 files changed, 35 insertions(+), 48 deletions(-) diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 9148ffe527..37b7d2758a 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -274,9 +274,7 @@ export function createSpecializedApp(options?: { config = new ConfigReader({}, 'empty-config'), } = options ?? {}; - const duplicatedFeatures = [appPlugin, ...featuresWithoutApp]; - - const features = deduplicateFeatures(duplicatedFeatures); + const features = deduplicateFeatures([appPlugin, ...featuresWithoutApp]); const tree = resolveAppTree( 'root', diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index 0ce21b5d6d..e8be1cc0ae 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -16,14 +16,17 @@ import React from 'react'; import { RouterBlueprint } from './RouterBlueprint'; import { MemoryRouter } from 'react-router-dom'; -import { waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { coreExtensionData, createExtension, createExtensionInput, } from '../wiring'; import { PageBlueprint } from './PageBlueprint'; -import { renderInTestApp } from '@backstage/frontend-test-utils'; +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; describe('RouterBlueprint', () => { it('should return an extension when calling make with sensible defaults', () => { @@ -70,18 +73,14 @@ describe('RouterBlueprint', () => { }, }); - const { getByTestId } = renderInTestApp(
, { - extensions: [ - extension, - PageBlueprint.make({ - namespace: 'test', - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ], - }); + const tester = createExtensionTester(extension); + const Component = tester.get(RouterBlueprint.dataRefs.component); + + const { getByTestId } = render( + +
+ , + ); await waitFor(() => { expect(getByTestId('test-contents')).toBeInTheDocument(); @@ -116,38 +115,28 @@ describe('RouterBlueprint', () => { }, }); - const { getByTestId } = renderInTestApp(
, { - extensions: [ - extension, - createExtension({ - namespace: 'test', - attachTo: { - id: 'app-router-component:test/test', - input: 'children', - }, - output: [coreExtensionData.reactElement], - *factory() { - yield coreExtensionData.reactElement(
); - }, - }), - PageBlueprint.make({ - namespace: 'test', - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ], - config: { - app: { - extensions: [ - { - 'app-router-component:test/test': { config: { name: 'Robin' } }, - }, - ], + const tester = createExtensionTester(extension, { + config: { name: 'Robin' }, + }).add( + createExtension({ + namespace: 'test', + attachTo: { + id: 'app-router-component:test/test', + input: 'children', }, - }, - }); + output: [coreExtensionData.reactElement], + *factory() { + yield coreExtensionData.reactElement(
); + }, + }), + ); + const Component = tester.get(RouterBlueprint.dataRefs.component); + + const { getByTestId } = render( + +
+ , + ); await waitFor(() => { expect(getByTestId('test-contents')).toBeInTheDocument(); From 49a463c5775692c337f390632edd225d8cee8892 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 27 Aug 2024 13:12:27 +0200 Subject: [PATCH 23/24] core-compat-api: update collectLegacyRoutes test to include app plugin Signed-off-by: Patrik Oldsberg --- .../core-compat-api/src/collectLegacyRoutes.test.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index f3e2baee38..b7b9f6781e 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -262,7 +262,15 @@ describe('collectLegacyRoutes', () => { component: () => Promise.resolve(() => { const app = useApp(); - return
plugins: {app.getPlugins().map(p => p.getId())}
; + return ( +
+ plugins:{' '} + {app + .getPlugins() + .map(p => p.getId()) + .join(', ')} +
+ ); }), }), ); @@ -276,7 +284,7 @@ describe('collectLegacyRoutes', () => { render(createSpecializedApp({ features }).createRoot()); await expect( - screen.findByText('plugins: test'), + screen.findByText('plugins: app, test'), ).resolves.toBeInTheDocument(); }); From 1f8012619fcc029962f4db9a497b80042e3992e0 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Aug 2024 13:16:42 +0200 Subject: [PATCH 24/24] chore: remove un-used imports Signed-off-by: blam --- .../src/blueprints/RouterBlueprint.test.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index e8be1cc0ae..3062d6d9de 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -22,11 +22,7 @@ import { createExtension, createExtensionInput, } from '../wiring'; -import { PageBlueprint } from './PageBlueprint'; -import { - createExtensionTester, - renderInTestApp, -} from '@backstage/frontend-test-utils'; +import { createExtensionTester } from '@backstage/frontend-test-utils'; describe('RouterBlueprint', () => { it('should return an extension when calling make with sensible defaults', () => {