@backstage/cli 0.28.0-next.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/config/jest.js +9 -1
- package/config/jestCacheResultProcessor.cjs +23 -0
- package/config/jestSwcTransform.js +4 -1
- package/dist/commands/build/buildBackend.cjs.js +65 -0
- package/dist/commands/build/buildFrontend.cjs.js +57 -0
- package/dist/commands/build/command.cjs.js +75 -0
- package/dist/commands/build/index.cjs.js +8 -0
- package/dist/commands/buildWorkspace.cjs.js +24 -0
- package/dist/commands/clean/clean.cjs.js +19 -0
- package/dist/{cjs/docs-BGyA6jwW.cjs.js → commands/config/docs.cjs.js} +4 -12
- package/dist/{cjs/print-Dd6aChXU.cjs.js → commands/config/print.cjs.js} +4 -12
- package/dist/{cjs/schema-D93FRhBL.cjs.js → commands/config/schema.cjs.js} +4 -12
- package/dist/commands/config/validate.cjs.js +19 -0
- package/dist/{cjs/index-j193pV_Y.cjs.js → commands/create-github-app/GithubCreateAppServer.cjs.js} +2 -110
- package/dist/commands/create-github-app/index.cjs.js +117 -0
- package/dist/commands/index.cjs.js +231 -0
- package/dist/{cjs/info-DuAv1Tsx.cjs.js → commands/info.cjs.js} +13 -17
- package/dist/{cjs/lint-BwiDJkjE.cjs.js → commands/lint.cjs.js} +10 -10
- package/dist/commands/migrate/packageExports.cjs.js +17 -0
- package/dist/{cjs/packageLintConfigs-DeUGBP17.cjs.js → commands/migrate/packageLintConfigs.cjs.js} +2 -10
- package/dist/{cjs/packageRole-Iuv9NRii.cjs.js → commands/migrate/packageRole.cjs.js} +5 -8
- package/dist/{cjs/packageScripts-DX6dilK6.cjs.js → commands/migrate/packageScripts.cjs.js} +1 -1
- package/dist/{cjs/reactRouterDeps-CR-hjviw.cjs.js → commands/migrate/reactRouterDeps.cjs.js} +1 -1
- package/dist/commands/new/new.cjs.js +101 -0
- package/dist/commands/pack.cjs.js +29 -0
- package/dist/commands/repo/build.cjs.js +113 -0
- package/dist/{cjs/clean-a6Q4k9Vm.cjs.js → commands/repo/clean.cjs.js} +5 -10
- package/dist/{cjs/fix-COitqgqm.cjs.js → commands/repo/fix.cjs.js} +8 -13
- package/dist/commands/repo/lint.cjs.js +205 -0
- package/dist/{cjs/list-deprecations-CtUaQgaP.cjs.js → commands/repo/list-deprecations.cjs.js} +7 -12
- package/dist/commands/repo/optionsParser.cjs.js +37 -0
- package/dist/commands/repo/test.cjs.js +277 -0
- package/dist/commands/start/command.cjs.js +48 -0
- package/dist/commands/start/index.cjs.js +8 -0
- package/dist/commands/start/startBackend.cjs.js +112 -0
- package/dist/commands/start/startFrontend.cjs.js +47 -0
- package/dist/{cjs/test-COxIko8N.cjs.js → commands/test.cjs.js} +6 -12
- package/dist/{cjs/bump-BHEh5ytx.cjs.js → commands/versions/bump.cjs.js} +21 -190
- package/dist/commands/versions/migrate.cjs.js +112 -0
- package/dist/index.cjs.js +29 -7
- package/dist/lib/builder/config.cjs.js +199 -0
- package/dist/lib/builder/packager.cjs.js +131 -0
- package/dist/lib/builder/plugins.cjs.js +71 -0
- package/dist/lib/builder/types.cjs.js +11 -0
- package/dist/lib/bundler/LinkedPackageResolvePlugin.cjs.js +47 -0
- package/dist/lib/bundler/backend.cjs.js +36 -0
- package/dist/{cjs/buildBackend-CkhZWCz1.cjs.js → lib/bundler/bundle.cjs.js} +31 -117
- package/dist/lib/bundler/config.cjs.js +491 -0
- package/dist/lib/bundler/hasReactDomClient.cjs.js +17 -0
- package/dist/lib/bundler/moduleFederation.cjs.js +28 -0
- package/dist/lib/bundler/optimization.cjs.js +67 -0
- package/dist/lib/bundler/packageDetection.cjs.js +117 -0
- package/dist/lib/bundler/paths.cjs.js +60 -0
- package/dist/lib/bundler/server.cjs.js +286 -0
- package/dist/lib/bundler/transforms.cjs.js +172 -0
- package/dist/lib/codeowners/codeowners.cjs.js +92 -0
- package/dist/{cjs/config-DBpmZirN.cjs.js → lib/config.cjs.js} +6 -6
- package/dist/{cjs/entryPoints-coip0t-x.cjs.js → lib/entryPoints.cjs.js} +1 -1
- package/dist/lib/errors.cjs.js +45 -0
- package/dist/lib/experimental/IpcServer.cjs.js +60 -0
- package/dist/lib/experimental/ServerDataStore.cjs.js +36 -0
- package/dist/lib/experimental/startBackendExperimental.cjs.js +128 -0
- package/dist/lib/new/FactoryRegistry.cjs.js +96 -0
- package/dist/lib/new/factories/backendModule.cjs.js +82 -0
- package/dist/lib/new/factories/backendPlugin.cjs.js +78 -0
- package/dist/lib/new/factories/common/prompts.cjs.js +57 -0
- package/dist/lib/new/factories/common/tasks.cjs.js +66 -0
- package/dist/lib/new/factories/common/util.cjs.js +16 -0
- package/dist/lib/new/factories/frontendPlugin.cjs.js +107 -0
- package/dist/lib/new/factories/index.cjs.js +24 -0
- package/dist/lib/new/factories/nodeLibraryPackage.cjs.js +57 -0
- package/dist/lib/new/factories/pluginCommon.cjs.js +58 -0
- package/dist/lib/new/factories/pluginNode.cjs.js +58 -0
- package/dist/lib/new/factories/pluginWeb.cjs.js +58 -0
- package/dist/lib/new/factories/scaffolderModule.cjs.js +90 -0
- package/dist/lib/new/factories/webLibraryPackage.cjs.js +57 -0
- package/dist/lib/new/types.cjs.js +8 -0
- package/dist/lib/packager/createDistWorkspace.cjs.js +219 -0
- package/dist/{cjs/productionPack-BxoMbBkH.cjs.js → lib/packager/productionPack.cjs.js} +8 -96
- package/dist/{cjs/parallel-BszNaKyc.cjs.js → lib/parallel.cjs.js} +2 -1
- package/dist/lib/paths.cjs.js +8 -0
- package/dist/{cjs/publishing-DQtsKTbc.cjs.js → lib/publishing.cjs.js} +1 -1
- package/dist/{cjs/role-BjiBExhi.cjs.js → lib/role.cjs.js} +3 -3
- package/dist/{cjs/run-CpZGNJQr.cjs.js → lib/run.cjs.js} +6 -5
- package/dist/{cjs/svgrTemplate-BTjBQ3by.cjs.js → lib/svgrTemplate.cjs.js} +1 -1
- package/dist/lib/tasks.cjs.js +188 -0
- package/dist/lib/typeDistProject.cjs.js +94 -0
- package/dist/lib/urls.cjs.js +13 -0
- package/dist/lib/version.cjs.js +94 -0
- package/dist/{cjs/yarn-6FNAgNBK.cjs.js → lib/versioning/Lockfile.cjs.js} +1 -31
- package/dist/lib/versioning/packages.cjs.js +75 -0
- package/dist/lib/yarn.cjs.js +34 -0
- package/dist/packages/backend-defaults/package.json.cjs.js +6 -0
- package/dist/packages/backend-plugin-api/package.json.cjs.js +6 -0
- package/dist/packages/backend-test-utils/package.json.cjs.js +6 -0
- package/dist/packages/catalog-client/package.json.cjs.js +6 -0
- package/dist/packages/cli/package.json.cjs.js +171 -0
- package/dist/packages/config/package.json.cjs.js +6 -0
- package/dist/packages/core-app-api/package.json.cjs.js +6 -0
- package/dist/packages/core-components/package.json.cjs.js +6 -0
- package/dist/packages/core-plugin-api/package.json.cjs.js +6 -0
- package/dist/packages/dev-utils/package.json.cjs.js +6 -0
- package/dist/packages/errors/package.json.cjs.js +6 -0
- package/dist/packages/test-utils/package.json.cjs.js +6 -0
- package/dist/packages/theme/package.json.cjs.js +6 -0
- package/dist/plugins/auth-backend/package.json.cjs.js +6 -0
- package/dist/plugins/auth-backend-module-guest-provider/package.json.cjs.js +6 -0
- package/dist/plugins/catalog-node/package.json.cjs.js +6 -0
- package/dist/plugins/scaffolder-node/package.json.cjs.js +6 -0
- package/dist/plugins/scaffolder-node-test-utils/package.json.cjs.js +6 -0
- package/package.json +55 -25
- package/templates/default-backend-plugin/README.md.hbs +22 -8
- package/templates/default-backend-plugin/dev/index.ts.hbs +60 -0
- package/templates/default-backend-plugin/package.json.hbs +5 -5
- package/templates/default-backend-plugin/src/index.ts.hbs +0 -1
- package/templates/default-backend-plugin/src/plugin.test.ts.hbs +85 -0
- package/templates/default-backend-plugin/src/plugin.ts.hbs +16 -14
- package/templates/default-backend-plugin/src/router.test.ts +67 -0
- package/templates/default-backend-plugin/src/router.ts +51 -0
- package/templates/default-backend-plugin/src/services/TodoListService/createTodoListService.ts +100 -0
- package/templates/default-backend-plugin/src/services/TodoListService/index.ts +1 -0
- package/templates/default-backend-plugin/src/services/TodoListService/types.ts +27 -0
- package/templates/scaffolder-module/package.json.hbs +3 -1
- package/templates/scaffolder-module/src/actions/example.test.ts +24 -0
- package/templates/scaffolder-module/src/actions/{example/example.ts → example.ts} +7 -3
- package/templates/scaffolder-module/src/index.ts.hbs +1 -1
- package/templates/scaffolder-module/src/{actions/example/module.ts → module.ts} +3 -3
- package/dist/cjs/build-CQdcGuBr.cjs.js +0 -194
- package/dist/cjs/buildWorkspace-CZPp9oRm.cjs.js +0 -53
- package/dist/cjs/clean-W6nxsHeK.cjs.js +0 -22
- package/dist/cjs/createDistWorkspace-DdHPGSMS.cjs.js +0 -576
- package/dist/cjs/index-BXv4Xa2e.cjs.js +0 -625
- package/dist/cjs/index-CGuAP7nv.cjs.js +0 -131
- package/dist/cjs/index-b1ouG3q6.cjs.js +0 -518
- package/dist/cjs/lint-Dsiocf9K.cjs.js +0 -91
- package/dist/cjs/moduleFederation-DmStnvEg.cjs.js +0 -910
- package/dist/cjs/new-CEnFhTT-.cjs.js +0 -1043
- package/dist/cjs/pack-XLRcGJqH.cjs.js +0 -34
- package/dist/cjs/packageExports-BJBwdvUH.cjs.js +0 -27
- package/dist/cjs/test-JcLI2pPM.cjs.js +0 -126
- package/dist/cjs/validate-CELljsEX.cjs.js +0 -28
- package/templates/default-backend-plugin/dev/index.ts +0 -9
- package/templates/default-backend-plugin/src/service/router.test.ts +0 -30
- package/templates/default-backend-plugin/src/service/router.ts +0 -28
- package/templates/scaffolder-module/src/actions/example/example.test.ts +0 -32
- package/templates/scaffolder-module/src/actions/example/index.ts +0 -7
- package/templates/scaffolder-module/src/actions/index.ts +0 -1
- /package/templates/default-backend-module/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/default-backend-plugin/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/default-common-plugin-package/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/default-node-plugin-package/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/default-plugin/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/default-react-plugin-package/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/node-library-package/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/scaffolder-module/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
- /package/templates/web-library-package/{.eslintrc.js → .eslintrc.js.hbs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/cli",
|
|
3
|
-
"version": "0.28.0
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "CLI for developing Backstage plugins and apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "cli"
|
|
@@ -42,16 +42,16 @@
|
|
|
42
42
|
"watch": "./src"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@backstage/catalog-model": "1.7.0",
|
|
46
|
-
"@backstage/cli-common": "0.1.14",
|
|
47
|
-
"@backstage/cli-node": "0.2.
|
|
48
|
-
"@backstage/config": "1.2.0",
|
|
49
|
-
"@backstage/config-loader": "1.9.1",
|
|
50
|
-
"@backstage/errors": "1.2.4",
|
|
51
|
-
"@backstage/eslint-plugin": "0.1.10
|
|
52
|
-
"@backstage/integration": "1.15.1
|
|
53
|
-
"@backstage/release-manifests": "0.0.11",
|
|
54
|
-
"@backstage/types": "1.1.1",
|
|
45
|
+
"@backstage/catalog-model": "^1.7.0",
|
|
46
|
+
"@backstage/cli-common": "^0.1.14",
|
|
47
|
+
"@backstage/cli-node": "^0.2.9",
|
|
48
|
+
"@backstage/config": "^1.2.0",
|
|
49
|
+
"@backstage/config-loader": "^1.9.1",
|
|
50
|
+
"@backstage/errors": "^1.2.4",
|
|
51
|
+
"@backstage/eslint-plugin": "^0.1.10",
|
|
52
|
+
"@backstage/integration": "^1.15.1",
|
|
53
|
+
"@backstage/release-manifests": "^0.0.11",
|
|
54
|
+
"@backstage/types": "^1.1.1",
|
|
55
55
|
"@manypkg/get-packages": "^1.1.3",
|
|
56
56
|
"@module-federation/enhanced": "^0.6.0",
|
|
57
57
|
"@octokit/graphql": "^5.0.0",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"cross-spawn": "^7.0.3",
|
|
91
91
|
"css-loader": "^6.5.1",
|
|
92
92
|
"ctrlc-windows": "^2.1.0",
|
|
93
|
-
"esbuild": "^0.
|
|
93
|
+
"esbuild": "^0.24.0",
|
|
94
94
|
"esbuild-loader": "^4.0.0",
|
|
95
95
|
"eslint": "^8.6.0",
|
|
96
96
|
"eslint-config-prettier": "^9.0.0",
|
|
@@ -106,13 +106,15 @@
|
|
|
106
106
|
"express": "^4.17.1",
|
|
107
107
|
"fork-ts-checker-webpack-plugin": "^9.0.0",
|
|
108
108
|
"fs-extra": "^11.2.0",
|
|
109
|
-
"git-url-parse": "^
|
|
109
|
+
"git-url-parse": "^15.0.0",
|
|
110
110
|
"glob": "^7.1.7",
|
|
111
111
|
"global-agent": "^3.0.0",
|
|
112
|
+
"globby": "^11.1.0",
|
|
112
113
|
"handlebars": "^4.7.3",
|
|
113
114
|
"html-webpack-plugin": "^5.3.1",
|
|
114
115
|
"inquirer": "^8.2.0",
|
|
115
116
|
"jest": "^29.7.0",
|
|
117
|
+
"jest-cli": "^29.7.0",
|
|
116
118
|
"jest-css-modules": "^2.1.0",
|
|
117
119
|
"jest-environment-jsdom": "^29.0.2",
|
|
118
120
|
"jest-runtime": "^29.0.2",
|
|
@@ -152,21 +154,32 @@
|
|
|
152
154
|
"webpack-dev-server": "^5.0.0",
|
|
153
155
|
"webpack-node-externals": "^3.0.0",
|
|
154
156
|
"yaml": "^2.0.0",
|
|
157
|
+
"yargs": "^16.2.0",
|
|
155
158
|
"yml-loader": "^2.1.0",
|
|
156
159
|
"yn": "^4.0.0",
|
|
157
160
|
"zod": "^3.22.4"
|
|
158
161
|
},
|
|
159
162
|
"devDependencies": {
|
|
160
163
|
"@backstage/backend-common": "^0.25.0",
|
|
161
|
-
"@backstage/backend-plugin-api": "1.0.1
|
|
162
|
-
"@backstage/backend-test-utils": "1.0.1
|
|
163
|
-
"@backstage/
|
|
164
|
-
"@backstage/
|
|
165
|
-
"@backstage/core-
|
|
166
|
-
"@backstage/core-
|
|
167
|
-
"@backstage/
|
|
168
|
-
"@backstage/
|
|
169
|
-
"@backstage/
|
|
164
|
+
"@backstage/backend-plugin-api": "^1.0.1",
|
|
165
|
+
"@backstage/backend-test-utils": "^1.0.1",
|
|
166
|
+
"@backstage/catalog-client": "^1.7.1",
|
|
167
|
+
"@backstage/config": "^1.2.0",
|
|
168
|
+
"@backstage/core-app-api": "^1.15.1",
|
|
169
|
+
"@backstage/core-components": "^0.15.1",
|
|
170
|
+
"@backstage/core-plugin-api": "^1.10.0",
|
|
171
|
+
"@backstage/dev-utils": "^1.1.2",
|
|
172
|
+
"@backstage/errors": "^1.2.4",
|
|
173
|
+
"@backstage/plugin-auth-backend": "^0.23.1",
|
|
174
|
+
"@backstage/plugin-auth-backend-module-guest-provider": "^0.2.1",
|
|
175
|
+
"@backstage/plugin-catalog-node": "^1.13.1",
|
|
176
|
+
"@backstage/plugin-scaffolder-node": "^0.5.0",
|
|
177
|
+
"@backstage/plugin-scaffolder-node-test-utils": "^0.1.13",
|
|
178
|
+
"@backstage/test-utils": "^1.7.0",
|
|
179
|
+
"@backstage/theme": "^0.6.0",
|
|
180
|
+
"@rspack/core": "^1.0.10",
|
|
181
|
+
"@rspack/dev-server": "^1.0.9",
|
|
182
|
+
"@rspack/plugin-react-refresh": "^1.0.0",
|
|
170
183
|
"@types/cross-spawn": "^6.0.2",
|
|
171
184
|
"@types/ejs": "^3.1.3",
|
|
172
185
|
"@types/express": "^4.17.6",
|
|
@@ -181,6 +194,7 @@
|
|
|
181
194
|
"@types/svgo": "^2.6.2",
|
|
182
195
|
"@types/tar": "^6.1.1",
|
|
183
196
|
"@types/terser-webpack-plugin": "^5.0.4",
|
|
197
|
+
"@types/webpack-sources": "^3.2.3",
|
|
184
198
|
"@types/yarnpkg__lockfile": "^1.1.4",
|
|
185
199
|
"@vitejs/plugin-react": "^4.3.1",
|
|
186
200
|
"del": "^7.0.0",
|
|
@@ -191,12 +205,28 @@
|
|
|
191
205
|
"vite-plugin-node-polyfills": "^0.22.0"
|
|
192
206
|
},
|
|
193
207
|
"peerDependencies": {
|
|
194
|
-
"@
|
|
195
|
-
"
|
|
196
|
-
"
|
|
208
|
+
"@modyfi/vite-plugin-yaml": "^1.1.0",
|
|
209
|
+
"@rspack/core": "^1.0.10",
|
|
210
|
+
"@rspack/dev-server": "^1.0.9",
|
|
211
|
+
"@rspack/plugin-react-refresh": "^1.0.0",
|
|
212
|
+
"@vitejs/plugin-react": "^4.0.4",
|
|
213
|
+
"vite": "^4.4.9",
|
|
214
|
+
"vite-plugin-html": "^3.2.0",
|
|
197
215
|
"vite-plugin-node-polyfills": "^0.22.0"
|
|
198
216
|
},
|
|
199
217
|
"peerDependenciesMeta": {
|
|
218
|
+
"@modyfi/vite-plugin-yaml": {
|
|
219
|
+
"optional": true
|
|
220
|
+
},
|
|
221
|
+
"@rspack/core": {
|
|
222
|
+
"optional": true
|
|
223
|
+
},
|
|
224
|
+
"@rspack/dev-server": {
|
|
225
|
+
"optional": true
|
|
226
|
+
},
|
|
227
|
+
"@rspack/plugin-react-refresh": {
|
|
228
|
+
"optional": true
|
|
229
|
+
},
|
|
200
230
|
"@vitejs/plugin-react": {
|
|
201
231
|
"optional": true
|
|
202
232
|
},
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
# {{id}}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This plugin backend was templated using the Backstage CLI. You should replace this text with a description of your plugin backend.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This plugin is installed via the `{{name}}` package. To install it to your backend package, run the following command:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
```bash
|
|
10
|
+
# From your root directory
|
|
11
|
+
yarn --cwd packages/backend add {{name}}
|
|
12
|
+
```
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
Then add the plugin to your backend in `packages/backend/src/index.ts`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
const backend = createBackend();
|
|
18
|
+
// ...
|
|
19
|
+
backend.add(import('{{name}}'));
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Development
|
|
23
|
+
|
|
24
|
+
This plugin backend can be started in a standalone mode from directly in this
|
|
25
|
+
package with `yarn start`. It is a limited setup that is most convenient when
|
|
26
|
+
developing the plugin backend itself.
|
|
27
|
+
|
|
28
|
+
If you want to run the entire project, including the frontend, run `yarn dev` from the root directory.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createBackend } from '@backstage/backend-defaults';
|
|
2
|
+
import { mockServices } from '@backstage/backend-test-utils';
|
|
3
|
+
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
|
|
4
|
+
|
|
5
|
+
// TEMPLATE NOTE:
|
|
6
|
+
// This is the development setup for your plugin that wires up a
|
|
7
|
+
// minimal backend that can use both real and mocked plugins and services.
|
|
8
|
+
//
|
|
9
|
+
// Start up the backend by running `yarn start` in the package directory.
|
|
10
|
+
// Once it's up and running, try out the following requests:
|
|
11
|
+
//
|
|
12
|
+
// Create a new todo item, standalone or for the sample component:
|
|
13
|
+
//
|
|
14
|
+
// curl http://localhost:7007/api/{{id}}/todos -H 'Content-Type: application/json' -d '{"title": "My Todo"}'
|
|
15
|
+
// curl http://localhost:7007/api/{{id}}/todos -H 'Content-Type: application/json' -d '{"title": "My Todo", "entityRef": "component:default/sample"}'
|
|
16
|
+
//
|
|
17
|
+
// List TODOs:
|
|
18
|
+
//
|
|
19
|
+
// curl http://localhost:7007/api/{{id}}/todos
|
|
20
|
+
//
|
|
21
|
+
// Explicitly make an unauthenticated request, or with service auth:
|
|
22
|
+
//
|
|
23
|
+
// curl http://localhost:7007/api/{{id}}/todos -H 'Authorization: Bearer mock-none-token'
|
|
24
|
+
// curl http://localhost:7007/api/{{id}}/todos -H 'Authorization: Bearer mock-service-token'
|
|
25
|
+
|
|
26
|
+
const backend = createBackend();
|
|
27
|
+
|
|
28
|
+
// TEMPLATE NOTE:
|
|
29
|
+
// Mocking the auth and httpAuth service allows you to call your plugin API without
|
|
30
|
+
// having to authenticate.
|
|
31
|
+
//
|
|
32
|
+
// If you want to use real auth, you can install the following instead:
|
|
33
|
+
// backend.add(import('@backstage/plugin-auth-backend'));
|
|
34
|
+
// backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
|
|
35
|
+
backend.add(mockServices.auth.factory());
|
|
36
|
+
backend.add(mockServices.httpAuth.factory());
|
|
37
|
+
|
|
38
|
+
// TEMPLATE NOTE:
|
|
39
|
+
// Rather than using a real catalog you can use a mock with a fixed set of entities.
|
|
40
|
+
backend.add(
|
|
41
|
+
catalogServiceMock.factory({
|
|
42
|
+
entities: [
|
|
43
|
+
{
|
|
44
|
+
apiVersion: 'backstage.io/v1alpha1',
|
|
45
|
+
kind: 'Component',
|
|
46
|
+
metadata: {
|
|
47
|
+
name: 'sample',
|
|
48
|
+
title: 'Sample Component',
|
|
49
|
+
},
|
|
50
|
+
spec: {
|
|
51
|
+
type: 'service',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
backend.add(import('../src'));
|
|
59
|
+
|
|
60
|
+
backend.start();
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@backstage/backend-defaults": "{{versionQuery '@backstage/backend-defaults'}}",
|
|
32
32
|
"@backstage/backend-plugin-api": "{{versionQuery '@backstage/backend-plugin-api'}}",
|
|
33
|
+
"@backstage/catalog-client": "{{versionQuery '@backstage/catalog-client'}}",
|
|
34
|
+
"@backstage/errors": "{{versionQuery '@backstage/errors'}}",
|
|
35
|
+
"@backstage/plugin-catalog-node": "{{versionQuery '@backstage/plugin-catalog-node'}}",
|
|
33
36
|
"express": "{{versionQuery 'express' '4.17.1'}}",
|
|
34
37
|
"express-promise-router": "{{versionQuery 'express-promise-router' '4.1.0'}}",
|
|
35
|
-
"
|
|
38
|
+
"zod": "{{versionQuery 'zod' '3.22.4'}}"
|
|
36
39
|
},
|
|
37
40
|
"devDependencies": {
|
|
38
41
|
"@backstage/backend-test-utils": "{{versionQuery '@backstage/backend-test-utils'}}",
|
|
39
42
|
"@backstage/cli": "{{versionQuery '@backstage/cli'}}",
|
|
40
|
-
"@backstage/plugin-auth-backend": "{{versionQuery '@backstage/plugin-auth-backend'}}",
|
|
41
|
-
"@backstage/plugin-auth-backend-module-guest-provider": "{{versionQuery '@backstage/plugin-auth-backend-module-guest-provider'}}",
|
|
42
43
|
"@types/express": "{{versionQuery '@types/express' '4.17.6'}}",
|
|
43
44
|
"@types/supertest": "{{versionQuery '@types/supertest' '2.0.12'}}",
|
|
44
|
-
"supertest": "{{versionQuery 'supertest' '6.2.4'}}"
|
|
45
|
-
"msw": "{{versionQuery 'msw' '2.3.1'}}"
|
|
45
|
+
"supertest": "{{versionQuery 'supertest' '6.2.4'}}"
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
48
48
|
"dist"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mockCredentials,
|
|
3
|
+
startTestBackend,
|
|
4
|
+
} from '@backstage/backend-test-utils';
|
|
5
|
+
import { {{pluginVar}} } from './plugin';
|
|
6
|
+
import request from 'supertest';
|
|
7
|
+
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
|
|
8
|
+
|
|
9
|
+
// TEMPLATE NOTE:
|
|
10
|
+
// Plugin tests are integration tests for your plugin, ensuring that all pieces
|
|
11
|
+
// work together end-to-end. You can still mock injected backend services
|
|
12
|
+
// however, just like anyone who installs your plugin might replace the
|
|
13
|
+
// services with their own implementations.
|
|
14
|
+
describe('plugin', () => {
|
|
15
|
+
it('should create and read TODO items', async () => {
|
|
16
|
+
const { server } = await startTestBackend({
|
|
17
|
+
features: [{{pluginVar}}],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await request(server).get('/api/{{id}}/todos').expect(200, {
|
|
21
|
+
items: [],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const createRes = await request(server)
|
|
25
|
+
.post('/api/{{id}}/todos')
|
|
26
|
+
.send({ title: 'My Todo' });
|
|
27
|
+
|
|
28
|
+
expect(createRes.status).toBe(201);
|
|
29
|
+
expect(createRes.body).toEqual({
|
|
30
|
+
id: expect.any(String),
|
|
31
|
+
title: 'My Todo',
|
|
32
|
+
createdBy: mockCredentials.user().principal.userEntityRef,
|
|
33
|
+
createdAt: expect.any(String),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const createdTodoItem = createRes.body;
|
|
37
|
+
|
|
38
|
+
await request(server)
|
|
39
|
+
.get('/api/{{id}}/todos')
|
|
40
|
+
.expect(200, {
|
|
41
|
+
items: [createdTodoItem],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await request(server)
|
|
45
|
+
.get(`/api/{{id}}/todos/${createdTodoItem.id}`)
|
|
46
|
+
.expect(200, createdTodoItem);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should create TODO item with catalog information', async () => {
|
|
50
|
+
const { server } = await startTestBackend({
|
|
51
|
+
features: [
|
|
52
|
+
{{pluginVar}},
|
|
53
|
+
catalogServiceMock.factory({
|
|
54
|
+
entities: [
|
|
55
|
+
{
|
|
56
|
+
apiVersion: 'backstage.io/v1alpha1',
|
|
57
|
+
kind: 'Component',
|
|
58
|
+
metadata: {
|
|
59
|
+
name: 'my-component',
|
|
60
|
+
namespace: 'default',
|
|
61
|
+
title: 'My Component',
|
|
62
|
+
},
|
|
63
|
+
spec: {
|
|
64
|
+
type: 'service',
|
|
65
|
+
owner: 'me',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}),
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const createRes = await request(server)
|
|
74
|
+
.post('/api/{{id}}/todos')
|
|
75
|
+
.send({ title: 'My Todo', entityRef: 'component:default/my-component' });
|
|
76
|
+
|
|
77
|
+
expect(createRes.status).toBe(201);
|
|
78
|
+
expect(createRes.body).toEqual({
|
|
79
|
+
id: expect.any(String),
|
|
80
|
+
title: '[My Component] My Todo',
|
|
81
|
+
createdBy: mockCredentials.user().principal.userEntityRef,
|
|
82
|
+
createdAt: expect.any(String),
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -2,7 +2,9 @@ import {
|
|
|
2
2
|
coreServices,
|
|
3
3
|
createBackendPlugin,
|
|
4
4
|
} from '@backstage/backend-plugin-api';
|
|
5
|
-
import { createRouter } from './
|
|
5
|
+
import { createRouter } from './router';
|
|
6
|
+
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
|
|
7
|
+
import { createTodoListService } from './services/TodoListService';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* {{pluginVar}} backend plugin
|
|
@@ -14,25 +16,25 @@ export const {{pluginVar}} = createBackendPlugin({
|
|
|
14
16
|
register(env) {
|
|
15
17
|
env.registerInit({
|
|
16
18
|
deps: {
|
|
17
|
-
httpRouter: coreServices.httpRouter,
|
|
18
19
|
logger: coreServices.logger,
|
|
19
|
-
|
|
20
|
+
auth: coreServices.auth,
|
|
21
|
+
httpAuth: coreServices.httpAuth,
|
|
22
|
+
httpRouter: coreServices.httpRouter,
|
|
23
|
+
catalog: catalogServiceRef,
|
|
20
24
|
},
|
|
21
|
-
async init({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
async init({ logger, auth, httpAuth, httpRouter, catalog }) {
|
|
26
|
+
const todoListService = await createTodoListService({
|
|
27
|
+
logger,
|
|
28
|
+
auth,
|
|
29
|
+
catalog,
|
|
30
|
+
});
|
|
31
|
+
|
|
26
32
|
httpRouter.use(
|
|
27
33
|
await createRouter({
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
httpAuth,
|
|
35
|
+
todoListService,
|
|
30
36
|
}),
|
|
31
37
|
);
|
|
32
|
-
httpRouter.addAuthPolicy({
|
|
33
|
-
path: '/health',
|
|
34
|
-
allow: 'unauthenticated',
|
|
35
|
-
});
|
|
36
38
|
},
|
|
37
39
|
});
|
|
38
40
|
},
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mockCredentials,
|
|
3
|
+
mockErrorHandler,
|
|
4
|
+
mockServices,
|
|
5
|
+
} from '@backstage/backend-test-utils';
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import request from 'supertest';
|
|
8
|
+
|
|
9
|
+
import { createRouter } from './router';
|
|
10
|
+
import { TodoListService } from './services/TodoListService/types';
|
|
11
|
+
|
|
12
|
+
const mockTodoItem = {
|
|
13
|
+
title: 'Do the thing',
|
|
14
|
+
id: '123',
|
|
15
|
+
createdBy: mockCredentials.user().principal.userEntityRef,
|
|
16
|
+
createdAt: new Date().toISOString(),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// TEMPLATE NOTE:
|
|
20
|
+
// Testing the router directly allows you to write a unit test that mocks the provided options.
|
|
21
|
+
describe('createRouter', () => {
|
|
22
|
+
let app: express.Express;
|
|
23
|
+
let todoListService: jest.Mocked<TodoListService>;
|
|
24
|
+
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
todoListService = {
|
|
27
|
+
createTodo: jest.fn(),
|
|
28
|
+
listTodos: jest.fn(),
|
|
29
|
+
getTodo: jest.fn(),
|
|
30
|
+
};
|
|
31
|
+
const router = await createRouter({
|
|
32
|
+
httpAuth: mockServices.httpAuth(),
|
|
33
|
+
todoListService,
|
|
34
|
+
});
|
|
35
|
+
app = express();
|
|
36
|
+
app.use(router);
|
|
37
|
+
app.use(mockErrorHandler());
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should create a TODO', async () => {
|
|
41
|
+
todoListService.createTodo.mockResolvedValue(mockTodoItem);
|
|
42
|
+
|
|
43
|
+
const response = await request(app).post('/todos').send({
|
|
44
|
+
title: 'Do the thing',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(response.status).toBe(201);
|
|
48
|
+
expect(response.body).toEqual(mockTodoItem);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should not allow unauthenticated requests to create a TODO', async () => {
|
|
52
|
+
todoListService.createTodo.mockResolvedValue(mockTodoItem);
|
|
53
|
+
|
|
54
|
+
// TEMPLATE NOTE:
|
|
55
|
+
// The HttpAuth mock service considers all requests to be authenticated as a
|
|
56
|
+
// mock user by default. In order to test other cases we need to explicitly
|
|
57
|
+
// pass an authorization header with mock credentials.
|
|
58
|
+
const response = await request(app)
|
|
59
|
+
.post('/todos')
|
|
60
|
+
.set('Authorization', mockCredentials.none.header())
|
|
61
|
+
.send({
|
|
62
|
+
title: 'Do the thing',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(response.status).toBe(401);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { HttpAuthService } from '@backstage/backend-plugin-api';
|
|
2
|
+
import { InputError } from '@backstage/errors';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import Router from 'express-promise-router';
|
|
6
|
+
import { TodoListService } from './services/TodoListService/types';
|
|
7
|
+
|
|
8
|
+
export async function createRouter({
|
|
9
|
+
httpAuth,
|
|
10
|
+
todoListService,
|
|
11
|
+
}: {
|
|
12
|
+
httpAuth: HttpAuthService;
|
|
13
|
+
todoListService: TodoListService;
|
|
14
|
+
}): Promise<express.Router> {
|
|
15
|
+
const router = Router();
|
|
16
|
+
router.use(express.json());
|
|
17
|
+
|
|
18
|
+
// TEMPLATE NOTE:
|
|
19
|
+
// Zod is a powerful library for data validation and recommended in particular
|
|
20
|
+
// for user-defined schemas. In this case we use it for input validation too.
|
|
21
|
+
//
|
|
22
|
+
// If you want to define a schema for your API we recommend using Backstage's
|
|
23
|
+
// OpenAPI tooling: https://backstage.io/docs/next/openapi/01-getting-started
|
|
24
|
+
const todoSchema = z.object({
|
|
25
|
+
title: z.string(),
|
|
26
|
+
entityRef: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
router.post('/todos', async (req, res) => {
|
|
30
|
+
const parsed = todoSchema.safeParse(req.body);
|
|
31
|
+
if (!parsed.success) {
|
|
32
|
+
throw new InputError(parsed.error.toString());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const result = await todoListService.createTodo(parsed.data, {
|
|
36
|
+
credentials: await httpAuth.credentials(req, { allow: ['user'] }),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
res.status(201).json(result);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
router.get('/todos', async (_req, res) => {
|
|
43
|
+
res.json(await todoListService.listTodos());
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.get('/todos/:id', async (req, res) => {
|
|
47
|
+
res.json(await todoListService.getTodo({ id: req.params.id }));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return router;
|
|
51
|
+
}
|
package/templates/default-backend-plugin/src/services/TodoListService/createTodoListService.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
|
|
2
|
+
import { NotFoundError } from '@backstage/errors';
|
|
3
|
+
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { TodoItem, TodoListService } from './types';
|
|
6
|
+
|
|
7
|
+
// TEMPLATE NOTE:
|
|
8
|
+
// This is a simple in-memory todo list store. It is recommended to use a
|
|
9
|
+
// database to store data in a real application. See the database service
|
|
10
|
+
// documentation for more information on how to do this:
|
|
11
|
+
// https://backstage.io/docs/backend-system/core-services/database
|
|
12
|
+
export async function createTodoListService({
|
|
13
|
+
auth,
|
|
14
|
+
logger,
|
|
15
|
+
catalog,
|
|
16
|
+
}: {
|
|
17
|
+
auth: AuthService;
|
|
18
|
+
logger: LoggerService;
|
|
19
|
+
catalog: typeof catalogServiceRef.T;
|
|
20
|
+
}): Promise<TodoListService> {
|
|
21
|
+
logger.info('Initializing TodoListService');
|
|
22
|
+
|
|
23
|
+
const storedTodos = new Array<TodoItem>();
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
async createTodo(input, options) {
|
|
27
|
+
let title = input.title;
|
|
28
|
+
|
|
29
|
+
// TEMPLATE NOTE:
|
|
30
|
+
// A common pattern for Backstage plugins is to pass an entity reference
|
|
31
|
+
// from the frontend to then fetch the entire entity from the catalog in the
|
|
32
|
+
// backend plugin.
|
|
33
|
+
if (input.entityRef) {
|
|
34
|
+
// TEMPLATE NOTE:
|
|
35
|
+
// Cross-plugin communication uses service-to-service authentication. The
|
|
36
|
+
// `AuthService` lets you generate a token that is valid for communication
|
|
37
|
+
// with the target plugin only. You must also provide credentials for the
|
|
38
|
+
// identity that you are making the request on behalf of.
|
|
39
|
+
//
|
|
40
|
+
// If you want to make a request using the plugin backend's own identity,
|
|
41
|
+
// you can access it via the `auth.getOwnServiceCredentials()` method.
|
|
42
|
+
// Beware that this bypasses any user permission checks.
|
|
43
|
+
const { token } = await auth.getPluginRequestToken({
|
|
44
|
+
onBehalfOf: options.credentials,
|
|
45
|
+
targetPluginId: 'catalog',
|
|
46
|
+
});
|
|
47
|
+
const entity = await catalog.getEntityByRef(input.entityRef, {
|
|
48
|
+
token,
|
|
49
|
+
});
|
|
50
|
+
if (!entity) {
|
|
51
|
+
throw new NotFoundError(
|
|
52
|
+
`No entity found for ref '${input.entityRef}'`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// TEMPLATE NOTE:
|
|
57
|
+
// Here you could read any form of data from the entity. A common use case
|
|
58
|
+
// is to read the value of a custom annotation for your plugin. You can
|
|
59
|
+
// read more about how to add custom annotations here:
|
|
60
|
+
// https://backstage.io/docs/features/software-catalog/extending-the-model#adding-a-new-annotation
|
|
61
|
+
//
|
|
62
|
+
// In this example we just use the entity title to decorate the todo item.
|
|
63
|
+
|
|
64
|
+
const entityDisplay = entity.metadata.title ?? input.entityRef;
|
|
65
|
+
title = `[${entityDisplay}] ${input.title}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const id = crypto.randomUUID();
|
|
69
|
+
const createdBy = options.credentials.principal.userEntityRef;
|
|
70
|
+
const newTodo = {
|
|
71
|
+
title,
|
|
72
|
+
id,
|
|
73
|
+
createdBy,
|
|
74
|
+
createdAt: new Date().toISOString(),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
storedTodos.push(newTodo);
|
|
78
|
+
|
|
79
|
+
// TEMPLATE NOTE:
|
|
80
|
+
// The second argument of the logger methods can be used to pass
|
|
81
|
+
// structured metadata. You can read more about the logger service here:
|
|
82
|
+
// https://backstage.io/docs/backend-system/core-services/logger
|
|
83
|
+
logger.info('Created new todo item', { id, title, createdBy });
|
|
84
|
+
|
|
85
|
+
return newTodo;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
async listTodos() {
|
|
89
|
+
return { items: Array.from(storedTodos) };
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
async getTodo(request: { id: string }) {
|
|
93
|
+
const todo = storedTodos.find(item => item.id === request.id);
|
|
94
|
+
if (!todo) {
|
|
95
|
+
throw new NotFoundError(`No todo found with id '${request.id}'`);
|
|
96
|
+
}
|
|
97
|
+
return todo;
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createTodoListService } from './createTodoListService';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BackstageCredentials,
|
|
3
|
+
BackstageUserPrincipal,
|
|
4
|
+
} from '@backstage/backend-plugin-api';
|
|
5
|
+
|
|
6
|
+
export interface TodoItem {
|
|
7
|
+
title: string;
|
|
8
|
+
id: string;
|
|
9
|
+
createdBy: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TodoListService {
|
|
14
|
+
createTodo(
|
|
15
|
+
input: {
|
|
16
|
+
title: string;
|
|
17
|
+
entityRef?: string;
|
|
18
|
+
},
|
|
19
|
+
options: {
|
|
20
|
+
credentials: BackstageCredentials<BackstageUserPrincipal>;
|
|
21
|
+
},
|
|
22
|
+
): Promise<TodoItem>;
|
|
23
|
+
|
|
24
|
+
listTodos(): Promise<{ items: TodoItem[] }>;
|
|
25
|
+
|
|
26
|
+
getTodo(request: { id: string }): Promise<TodoItem>;
|
|
27
|
+
}
|
|
@@ -29,10 +29,12 @@
|
|
|
29
29
|
"postpack": "backstage-cli package postpack"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"@backstage/backend-plugin-api": "{{versionQuery '@backstage/backend-plugin-api'}}",
|
|
32
33
|
"@backstage/plugin-scaffolder-node": "{{versionQuery '@backstage/plugin-scaffolder-node'}}"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@backstage/cli": "{{versionQuery '@backstage/cli'}}"
|
|
36
|
+
"@backstage/cli": "{{versionQuery '@backstage/cli'}}",
|
|
37
|
+
"@backstage/plugin-scaffolder-node-test-utils": "{{versionQuery '@backstage/plugin-scaffolder-node-test-utils'}}"
|
|
36
38
|
},
|
|
37
39
|
"files": [
|
|
38
40
|
"dist"
|