diff --git a/.changeset/beige-queens-crash.md b/.changeset/beige-queens-crash.md deleted file mode 100644 index 6253717673..0000000000 --- a/.changeset/beige-queens-crash.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Optimized the `yarn install` step in the backend `Dockerfile`. - -To apply these changes to an existing app, make the following changes to `packages/backend/Dockerfile`: - -Replace the `RUN yarn install ...` line with the following: - -```bash -RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" -``` diff --git a/.changeset/chilly-chefs-protect.md b/.changeset/chilly-chefs-protect.md deleted file mode 100644 index 22e2ea9b7e..0000000000 --- a/.changeset/chilly-chefs-protect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-api-docs': patch ---- - -Update swagger-ui-react to 3.37.2 diff --git a/.changeset/cost-insights-cyan-nails-film.md b/.changeset/cost-insights-cyan-nails-film.md deleted file mode 100644 index 69e21d87fe..0000000000 --- a/.changeset/cost-insights-cyan-nails-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -Add breakdown view to the Cost Overview panel diff --git a/.changeset/famous-items-travel.md b/.changeset/famous-items-travel.md deleted file mode 100644 index a0aa02732d..0000000000 --- a/.changeset/famous-items-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -Update URL auth format for Gitlab clone diff --git a/.changeset/five-lies-smile.md b/.changeset/five-lies-smile.md new file mode 100644 index 0000000000..a35a2ca528 --- /dev/null +++ b/.changeset/five-lies-smile.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-circleci': patch +'@backstage/plugin-jenkins': patch +--- + +Refactor to support ADR004 module exporting. + +For more information, see https://backstage.io/docs/architecture-decisions/adrs-adr004. diff --git a/.changeset/four-plants-happen.md b/.changeset/four-plants-happen.md deleted file mode 100644 index b273d8ec41..0000000000 --- a/.changeset/four-plants-happen.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-sentry': patch -'@backstage/plugin-welcome': patch ---- - -Refactor route registration to remove deprecating code diff --git a/.changeset/grumpy-crews-build.md b/.changeset/grumpy-crews-build.md deleted file mode 100644 index 502210c257..0000000000 --- a/.changeset/grumpy-crews-build.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/config-loader': minor ---- - -Fix typo of "visibility" in config schema reference - -If you have defined a config element named `visiblity`, you -will need to fix the spelling to `visibility`. For more info, -see https://backstage.io/docs/conf/defining#visibility. diff --git a/.changeset/light-bulldogs-guess.md b/.changeset/light-bulldogs-guess.md deleted file mode 100644 index d7b5a5aef5..0000000000 --- a/.changeset/light-bulldogs-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Allow the `backend.listen.port` config to be both a number or a string. diff --git a/.changeset/light-nails-crash.md b/.changeset/light-nails-crash.md deleted file mode 100644 index c7791d1027..0000000000 --- a/.changeset/light-nails-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Bump versions of `esbuild` and `rollup-plugin-esbuild` diff --git a/.changeset/new-nails-thank.md b/.changeset/new-nails-thank.md deleted file mode 100644 index 4fc7ccac68..0000000000 --- a/.changeset/new-nails-thank.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': patch ---- - -Filter the headers that are sent from the proxied-targed back to the frontend to not forwarded unwanted authentication or -monitoring contexts from other origins (like `Set-Cookie` with e.g. a google analytics context). The implementation reuses -the `allowedHeaders` configuration that now controls both directions `frontend->target` and `target->frontend`. diff --git a/.changeset/perfect-dryers-sell.md b/.changeset/perfect-dryers-sell.md deleted file mode 100644 index e1c5592162..0000000000 --- a/.changeset/perfect-dryers-sell.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Removed `"resolutions"` entry for `esbuild` in the root `package.json` in order to use the version specified by `@backstage/cli`. - -To apply this change to an existing app, remove the following from your root `package.json`: - -```json -"resolutions": { - "esbuild": "0.6.3" -}, -``` diff --git a/.changeset/slow-insects-fail.md b/.changeset/slow-insects-fail.md deleted file mode 100644 index a07e025f34..0000000000 --- a/.changeset/slow-insects-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Add [API docs plugin](https://github.com/backstage/backstage/tree/master/plugins/api-docs) to new apps being created through the CLI. diff --git a/.changeset/smart-turkeys-bathe.md b/.changeset/smart-turkeys-bathe.md deleted file mode 100644 index 5f429b5b37..0000000000 --- a/.changeset/smart-turkeys-bathe.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor -'@backstage/plugin-catalog-import': minor -'@backstage/catalog-model': patch -'@backstage/plugin-scaffolder': patch ---- - -Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file: - -```ts -export default async function createPlugin(env: PluginEnvironment) { - const builder = new CatalogBuilder(env); - const { - entitiesCatalog, - locationsCatalog, - higherOrderOperation, - locationAnalyzer, //<-- - } = await builder.build(); - - return await createRouter({ - entitiesCatalog, - locationsCatalog, - higherOrderOperation, - locationAnalyzer, //<-- - logger: env.logger, - }); -} -``` diff --git a/.changeset/sour-eels-dream.md b/.changeset/sour-eels-dream.md deleted file mode 100644 index 75b9fb3c8d..0000000000 --- a/.changeset/sour-eels-dream.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Gracefully handle missing codeowners. - -The CodeOwnersProcessor now also takes a logger as a parameter. diff --git a/.changeset/stupid-taxis-sneeze.md b/.changeset/stupid-taxis-sneeze.md deleted file mode 100644 index d8e0fb130a..0000000000 --- a/.changeset/stupid-taxis-sneeze.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/cli': minor -'@backstage/plugin-cost-insights': patch ---- - -sort product panels and navigation menu by greatest cost -update tsconfig.json to use ES2020 api diff --git a/.changeset/tidy-actors-repair.md b/.changeset/tidy-actors-repair.md deleted file mode 100644 index 4869400556..0000000000 --- a/.changeset/tidy-actors-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Use type EntityName from catalog-model for entities diff --git a/.changeset/twenty-trees-travel.md b/.changeset/twenty-trees-travel.md deleted file mode 100644 index 7f65084320..0000000000 --- a/.changeset/twenty-trees-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Use the OWNED_BY relation and compare it to the users MEMBER_OF relation. The user entity is searched by name, based on the userId of the identity. diff --git a/.changeset/weak-roses-search.md b/.changeset/weak-roses-search.md deleted file mode 100644 index 9f5074ce48..0000000000 --- a/.changeset/weak-roses-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': patch ---- - -Add configuration schema for the commonly used properties diff --git a/app-config.yaml b/app-config.yaml index bc01e5eab5..4ad6f5345b 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -294,6 +294,9 @@ costInsights: bigQuery: name: BigQuery icon: search + events: + name: Events + icon: data metrics: DAU: name: Daily Active Users diff --git a/docs/auth/index.md b/docs/auth/index.md index 3f95efaf61..cf468c7bb7 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -93,6 +93,6 @@ sign-in methods. More details are provided in dedicated sections of the documentation. - [OAuth](./oauth.md): Description of the generic OAuth flow implemented by the - [auth-backend](../../plugins/auth-backend). + [auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend). - [Glossary](./glossary.md): Glossary of some common terms related to the auth flows. diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 438f874990..19c5c74f68 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -31,6 +31,7 @@ we recommend that you name them `catalog-info.yaml`. - [Kind: Resource](#kind-resource) - [Kind: System](#kind-system) - [Kind: Domain](#kind-domain) +- [Kind: Location](#kind-location) ## Overall Shape Of An Entity @@ -884,3 +885,58 @@ This kind is not yet defined, but is reserved [for future use](system-model.md). ## Kind: Domain This kind is not yet defined, but is reserved [for future use](system-model.md). + +## Kind: Location + +Describes the following entity kind: + +| Field | Value | +| ------------ | ----------------------- | +| `apiVersion` | `backstage.io/v1alpha1` | +| `kind` | `Location` | + +A location is a marker that references other places to look for catalog data. + +Descriptor files for this kind may look as follows. + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: org-data +spec: + type: url + targets: + - http://github.com/myorg/myproject/org-data-dump/catalog-info-staff.yaml + - http://github.com/myorg/myproject/org-data-dump/catalog-info-consultants.yaml +``` + +In addition to the [common envelope metadata](#common-to-all-kinds-the-metadata) +shape, this kind has the following structure. + +### `apiVersion` and `kind` [required] + +Exactly equal to `backstage.io/v1alpha1` and `Location`, respectively. + +### `spec.type` [optional] + +The single location type, that's common to the targets specified in the spec. If +it is left out, it is inherited from the location type that originally read the +entity data. For example, if you have a `url` type location, that when read +results in a `Location` kind entity with no `spec.type`, then the referenced +targets in the entity will implicitly also be of `url` type. This is useful +because you can define a hierarchy of things in a directory structure using +relative target paths (see below), and it will work out no matter if it's +consumed locally on disk from a `file` location, or as uploaded on a VCS. + +### `spec.target` [optional] + +A single target as a string. Can be either an absolute path/URL (depending on +the type), or a relative path such as `./details/catalog-info.yaml` which is +resolved relative to the location of this Location entity itself. + +### `spec.targets` [optional] + +A list of targets as strings. They can all be either absolute paths/URLs +(depending on the type), or relative paths such as `./details/catalog-info.yaml` +which are resolved relative to the location of this Location entity itself. diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index a6c8da7fd9..1b6e5cf1e0 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,45 @@ # example-app +## 0.2.5 + +### Patch Changes + +- Updated dependencies [7eb8bfe4a] +- Updated dependencies [fe7257ff0] +- Updated dependencies [a2cfa311a] +- Updated dependencies [69f38457f] +- Updated dependencies [bec334b33] +- Updated dependencies [303c5ea17] +- Updated dependencies [b4488ddb0] +- Updated dependencies [4a655c89d] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [8a16e8af8] +- Updated dependencies [bcc211a08] +- Updated dependencies [00670a96e] +- Updated dependencies [da2ad65cb] +- Updated dependencies [ebf37bbae] + - @backstage/plugin-api-docs@0.3.1 + - @backstage/plugin-cost-insights@0.4.2 + - @backstage/plugin-sentry@0.2.4 + - @backstage/plugin-welcome@0.2.2 + - @backstage/cli@0.4.0 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog-import@0.3.0 + - @backstage/plugin-scaffolder@0.3.2 + - @backstage/plugin-kubernetes@0.3.1 + - @backstage/plugin-techdocs@0.3.1 + - @backstage/plugin-catalog@0.2.5 + - @backstage/test-utils@0.1.4 + - @backstage/plugin-circleci@0.2.3 + - @backstage/plugin-cloudbuild@0.2.3 + - @backstage/plugin-github-actions@0.2.3 + - @backstage/plugin-jenkins@0.3.2 + - @backstage/plugin-lighthouse@0.2.4 + - @backstage/plugin-register-component@0.2.3 + - @backstage/plugin-rollbar@0.2.5 + - @backstage/plugin-search@0.2.2 + ## 0.2.4 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index c176f74cf6..fd6667ac85 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,37 +1,37 @@ { "name": "example-app", - "version": "0.2.4", + "version": "0.2.5", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.3.1", - "@backstage/cli": "^0.3.2", + "@backstage/catalog-model": "^0.4.0", + "@backstage/cli": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-api-docs": "^0.3.0", - "@backstage/plugin-catalog": "^0.2.4", - "@backstage/plugin-catalog-import": "^0.2.0", - "@backstage/plugin-circleci": "^0.2.2", - "@backstage/plugin-cloudbuild": "^0.2.2", - "@backstage/plugin-cost-insights": "^0.4.1", + "@backstage/plugin-api-docs": "^0.3.1", + "@backstage/plugin-catalog": "^0.2.5", + "@backstage/plugin-catalog-import": "^0.3.0", + "@backstage/plugin-circleci": "^0.2.3", + "@backstage/plugin-cloudbuild": "^0.2.3", + "@backstage/plugin-cost-insights": "^0.4.2", "@backstage/plugin-explore": "^0.2.1", "@backstage/plugin-gcp-projects": "^0.2.1", - "@backstage/plugin-github-actions": "^0.2.2", + "@backstage/plugin-github-actions": "^0.2.3", "@backstage/plugin-gitops-profiles": "^0.2.1", "@backstage/plugin-graphiql": "^0.2.1", - "@backstage/plugin-jenkins": "^0.3.1", - "@backstage/plugin-kubernetes": "^0.3.0", - "@backstage/plugin-lighthouse": "^0.2.3", + "@backstage/plugin-jenkins": "^0.3.2", + "@backstage/plugin-kubernetes": "^0.3.1", + "@backstage/plugin-lighthouse": "^0.2.4", "@backstage/plugin-newrelic": "^0.2.1", - "@backstage/plugin-register-component": "^0.2.2", - "@backstage/plugin-rollbar": "^0.2.4", - "@backstage/plugin-scaffolder": "^0.3.1", - "@backstage/plugin-sentry": "^0.2.3", - "@backstage/plugin-search": "^0.2.1", + "@backstage/plugin-register-component": "^0.2.3", + "@backstage/plugin-rollbar": "^0.2.5", + "@backstage/plugin-scaffolder": "^0.3.2", + "@backstage/plugin-sentry": "^0.2.4", + "@backstage/plugin-search": "^0.2.2", "@backstage/plugin-tech-radar": "^0.3.0", - "@backstage/plugin-techdocs": "^0.3.0", + "@backstage/plugin-techdocs": "^0.3.1", "@backstage/plugin-user-settings": "^0.2.2", - "@backstage/plugin-welcome": "^0.2.1", - "@backstage/test-utils": "^0.1.3", + "@backstage/plugin-welcome": "^0.2.2", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index aec6c37a49..0c3741ec84 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-common +## 0.3.3 + +### Patch Changes + +- 612368274: Allow the `backend.listen.port` config to be both a number or a string. +- Updated dependencies [4e7091759] +- Updated dependencies [b4488ddb0] + - @backstage/config-loader@0.4.0 + ## 0.3.2 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index ca92f70e98..04e189abca 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -31,7 +31,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", - "@backstage/config-loader": "^0.3.0", + "@backstage/config-loader": "^0.4.0", "@backstage/integration": "^0.1.2", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", @@ -67,8 +67,8 @@ } }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/test-utils": "^0.1.4", "@types/archiver": "^3.1.1", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index eb87d339ea..abe8b4f640 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -235,6 +235,40 @@ describe('GithubUrlReader', () => { expect(indexMarkdownFile.toString()).toBe('# Test\n'); }); + it('includes the subdomain in the github url', async () => { + worker.resetHandlers(); + worker.use( + rest.get( + 'https://ghe.github.com/backstage/mock/archive/repo.tar.gz', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.body(repoBuffer), + ), + ), + ); + + const processor = new GithubUrlReader( + { + host: 'ghe.github.com', + apiBaseUrl: 'https://api.github.com', + }, + { treeResponseFactory }, + ); + + const response = await processor.readTree( + 'https://ghe.github.com/backstage/mock/tree/repo/docs', + ); + + const files = await response.files(); + + expect(files.length).toBe(1); + const indexMarkdownFile = await files[0].content(); + + expect(indexMarkdownFile.toString()).toBe('# Test\n'); + }); + it('must specify a branch', async () => { const processor = new GithubUrlReader( { diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 907f2ada7a..692a3c6dd8 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -179,7 +179,7 @@ export class GithubUrlReader implements UrlReader { name: repoName, ref, protocol, - source, + resource, full_name, filepath, } = parseGitUri(url); @@ -194,7 +194,7 @@ export class GithubUrlReader implements UrlReader { // TODO(Rugvip): use API to fetch URL instead const response = await fetch( new URL( - `${protocol}://${source}/${full_name}/archive/${ref}.tar.gz`, + `${protocol}://${resource}/${full_name}/archive/${ref}.tar.gz`, ).toString(), ); if (!response.ok) { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index d47e9003a5..838ca7d302 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,29 @@ # example-backend +## 0.2.5 + +### Patch Changes + +- Updated dependencies [ae95c7ff3] +- Updated dependencies [b4488ddb0] +- Updated dependencies [612368274] +- Updated dependencies [6a6c7c14e] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [e42402b47] +- Updated dependencies [bcc211a08] +- Updated dependencies [3619ea4c4] + - @backstage/plugin-techdocs-backend@0.3.1 + - @backstage/plugin-catalog-backend@0.3.0 + - @backstage/backend-common@0.3.3 + - @backstage/plugin-proxy-backend@0.2.2 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-kubernetes-backend@0.2.1 + - @backstage/plugin-app-backend@0.3.2 + - example-app@0.2.5 + - @backstage/plugin-auth-backend@0.2.5 + - @backstage/plugin-scaffolder-backend@0.3.3 + ## 0.2.4 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index beb2567bd7..9e635902e9 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.4", + "version": "0.2.5", "main": "dist/index.cjs.js", "types": "src/index.ts", "private": true, @@ -18,24 +18,24 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.3.2", - "@backstage/catalog-model": "^0.3.1", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", - "@backstage/plugin-app-backend": "^0.3.1", - "@backstage/plugin-auth-backend": "^0.2.4", - "@backstage/plugin-catalog-backend": "^0.2.3", + "@backstage/plugin-app-backend": "^0.3.2", + "@backstage/plugin-auth-backend": "^0.2.5", + "@backstage/plugin-catalog-backend": "^0.3.0", "@backstage/plugin-graphql-backend": "^0.1.3", - "@backstage/plugin-kubernetes-backend": "^0.2.0", - "@backstage/plugin-proxy-backend": "^0.2.1", + "@backstage/plugin-kubernetes-backend": "^0.2.1", + "@backstage/plugin-proxy-backend": "^0.2.2", "@backstage/plugin-rollbar-backend": "^0.1.4", - "@backstage/plugin-scaffolder-backend": "^0.3.2", + "@backstage/plugin-scaffolder-backend": "^0.3.3", "@backstage/plugin-sentry-backend": "^0.1.3", - "@backstage/plugin-techdocs-backend": "^0.3.0", + "@backstage/plugin-techdocs-backend": "^0.3.1", "@gitbeaker/node": "^25.2.0", "@octokit/rest": "^18.0.0", "azure-devops-node-api": "^10.1.1", "dockerode": "^3.2.0", - "example-app": "^0.2.4", + "example-app": "^0.2.5", "express": "^4.17.1", "express-promise-router": "^3.0.3", "knex": "^0.21.6", @@ -45,7 +45,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@types/dockerode": "^2.5.32", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 3454a6c8e0..3ea3b496d7 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/catalog-client +## 0.3.2 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/catalog-model@0.4.0 + ## 0.3.1 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 4509b7d547..a343cae716 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "0.3.1", + "version": "0.3.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,12 +20,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.3.1", + "@backstage/cli": "^0.4.0", "@types/jest": "^26.0.7", "msw": "^0.21.2" }, diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 630664e491..be8e4a1d70 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/catalog-model +## 0.4.0 + +### Minor Changes + +- bcc211a08: k8s-plugin: refactor approach to use annotation based label-selector + +### Patch Changes + +- 08835a61d: Add support for relative targets and implicit types in Location entities. +- a9fd599f7: Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file: + + ```ts + export default async function createPlugin(env: PluginEnvironment) { + const builder = new CatalogBuilder(env); + const { + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + } = await builder.build(); + + return await createRouter({ + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + logger: env.logger, + }); + } + ``` + ## 0.3.1 ### Patch Changes diff --git a/packages/catalog-model/examples-relative/README.md b/packages/catalog-model/examples-relative/README.md new file mode 100644 index 0000000000..3ed63a0e00 --- /dev/null +++ b/packages/catalog-model/examples-relative/README.md @@ -0,0 +1,10 @@ +# Examples with relative paths + +These examples use Location entities with relative paths +[see #3513](https://github.com/backstage/backstage/pull/3513). + +The `examples` sibling directory will be replaced with these contents no sooner +than December 16 2020, to give consumers time to adopt the above change. + +The contents of this directory is here for consumers who are on the edge and +want to use the new facility before December 16. diff --git a/packages/catalog-model/examples-relative/acme-corp.yaml b/packages/catalog-model/examples-relative/acme-corp.yaml new file mode 100644 index 0000000000..6449d548e0 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme-corp.yaml @@ -0,0 +1,8 @@ +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: acme-corp + description: A collection of all Backstage example Groups +spec: + targets: + - ./acme/org.yaml diff --git a/packages/catalog-model/examples-relative/acme/backstage-group.yaml b/packages/catalog-model/examples-relative/acme/backstage-group.yaml new file mode 100644 index 0000000000..f992d155bc --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/backstage-group.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: backstage + description: The backstage sub-department +spec: + type: sub-department + parent: infrastructure + ancestors: [infrastructure, acme-corp] + children: [team-a, team-b] + descendants: [team-a, team-b] diff --git a/packages/catalog-model/examples-relative/acme/boxoffice-group.yaml b/packages/catalog-model/examples-relative/acme/boxoffice-group.yaml new file mode 100644 index 0000000000..0be1fe58cb --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/boxoffice-group.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: boxoffice + description: The boxoffice sub-department +spec: + type: sub-department + parent: infrastructure + ancestors: [infrastructure, acme-corp] + children: [team-c, team-d] + descendants: [team-c, team-d] diff --git a/packages/catalog-model/examples-relative/acme/infrastructure-group.yaml b/packages/catalog-model/examples-relative/acme/infrastructure-group.yaml new file mode 100644 index 0000000000..2341782944 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/infrastructure-group.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: infrastructure + description: The infra department +spec: + type: department + parent: acme-corp + ancestors: [acme-corp] + children: [backstage, boxoffice] + descendants: [backstage, boxoffice, team-a, team-b, team-c, team-d] diff --git a/packages/catalog-model/examples-relative/acme/org.yaml b/packages/catalog-model/examples-relative/acme/org.yaml new file mode 100644 index 0000000000..fe368962b1 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/org.yaml @@ -0,0 +1,26 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: acme-corp + description: The acme-corp organization +spec: + type: organization + ancestors: [] + children: [infrastructure] + descendants: + [infrastructure, backstage, boxoffice, team-a, team-b, team-c, team-d] +--- +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: example-groups + description: A collection of all Backstage example Groups +spec: + targets: + - ./infrastructure-group.yaml + - ./boxoffice-group.yaml + - ./backstage-group.yaml + - ./team-a-group.yaml + - ./team-b-group.yaml + - ./team-c-group.yaml + - ./team-d-group.yaml diff --git a/packages/catalog-model/examples-relative/acme/team-a-group.yaml b/packages/catalog-model/examples-relative/acme/team-a-group.yaml new file mode 100644 index 0000000000..8dbf0bb839 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/team-a-group.yaml @@ -0,0 +1,58 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: team-a + description: Team A +spec: + type: team + parent: backstage + ancestors: [backstage, infrastructure, acme-corp] + children: [] + descendants: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: breanna.davison +spec: + profile: + displayName: Breanna Davison + email: breanna-davison@example.com + picture: https://example.com/staff/breanna.jpeg + memberOf: [team-a] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: janelle.dawe +spec: + profile: + displayName: Janelle Dawe + email: janelle-dawe@example.com + picture: https://example.com/staff/janelle.jpeg + memberOf: [team-a] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: nigel.manning +spec: + profile: + displayName: Nigel Manning + email: nigel-manning@example.com + picture: https://example.com/staff/nigel.jpeg + memberOf: [team-a] +--- +# This user is added as an example, to make it more easy for the "Guest" +# sign-in option to demonstrate some entities being owned. In a regular org, +# a guest user would probably not be registered like this. +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: guest +spec: + profile: + displayName: Guest User + email: guest@example.com + picture: https://example.com/staff/the-ceos-dog.jpeg + memberOf: [team-a] diff --git a/packages/catalog-model/examples-relative/acme/team-b-group.yaml b/packages/catalog-model/examples-relative/acme/team-b-group.yaml new file mode 100644 index 0000000000..00e9e41d80 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/team-b-group.yaml @@ -0,0 +1,66 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: team-b + description: Team B +spec: + type: team + parent: backstage + ancestors: [backstage, infrastructure, acme-corp] + children: [] + descendants: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: amelia.park +spec: + profile: + displayName: Amelia Park + email: amelia-park@example.com + picture: https://example.com/staff/amelia.jpeg + memberOf: [team-b] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: colette.brock +spec: + profile: + displayName: Colette Brock + email: colette-brock@example.com + picture: https://example.com/staff/colette.jpeg + memberOf: [team-b] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: jenny.doe +spec: + profile: + displayName: Jenny Doe + email: jenny-doe@example.com + picture: https://example.com/staff/jenny.jpeg + memberOf: [team-b] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: jonathon.page +spec: + profile: + displayName: Jonathon Page + email: jonathon-page@example.com + picture: https://example.com/staff/jonathon.jpeg + memberOf: [team-b] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: justine.barrow +spec: + profile: + displayName: Justine Barrow + email: justine-barrow@example.com + picture: https://example.com/staff/justine.jpeg + memberOf: [team-b] diff --git a/packages/catalog-model/examples-relative/acme/team-c-group.yaml b/packages/catalog-model/examples-relative/acme/team-c-group.yaml new file mode 100644 index 0000000000..0f97f69cb3 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/team-c-group.yaml @@ -0,0 +1,66 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: team-c + description: Team C +spec: + type: team + parent: boxoffice + ancestors: [boxoffice, infrastructure, acme-corp] + children: [] + descendants: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: calum.leavy +spec: + profile: + displayName: Calum Leavy + email: calum-leavy@example.com + picture: https://example.com/staff/calum.jpeg + memberOf: [team-c] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: frank.tiernan +spec: + profile: + displayName: Frank Tiernan + email: frank-tiernan@example.com + picture: https://example.com/staff/frank.jpeg + memberOf: [team-c] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: peadar.macmahon +spec: + profile: + displayName: Peadar MacMahon + email: peadar-macmahon@example.com + picture: https://example.com/staff/peadar.jpeg + memberOf: [team-c] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: sarah.gilroy +spec: + profile: + displayName: Sarah Gilroy + email: sarah-gilroy@example.com + picture: https://example.com/staff/sarah.jpeg + memberOf: [team-c] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: tara.macgovern +spec: + profile: + displayName: Tara MacGovern + email: tara-macgovern@example.com + picture: https://example.com/staff/tara.jpeg + memberOf: [team-c] diff --git a/packages/catalog-model/examples-relative/acme/team-d-group.yaml b/packages/catalog-model/examples-relative/acme/team-d-group.yaml new file mode 100644 index 0000000000..5a1a53a2e6 --- /dev/null +++ b/packages/catalog-model/examples-relative/acme/team-d-group.yaml @@ -0,0 +1,33 @@ +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: team-d + description: Team D +spec: + type: team + parent: boxoffice + ancestors: [boxoffice, infrastructure, acme-corp] + children: [] + descendants: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: eva.macdowell +spec: + profile: + displayName: Eva MacDowell + email: eva-macdowell@example.com + picture: https://example.com/staff/eva.jpeg + memberOf: [team-d] +--- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: lucy.sheehan +spec: + profile: + displayName: Lucy Sheehan + email: lucy-sheehan@example.com + picture: https://example.com/staff/lucy.jpeg + memberOf: [team-d] diff --git a/packages/catalog-model/examples-relative/all-apis.yaml b/packages/catalog-model/examples-relative/all-apis.yaml new file mode 100644 index 0000000000..20752faf97 --- /dev/null +++ b/packages/catalog-model/examples-relative/all-apis.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: example-apis + description: A collection of all Backstage example APIs +spec: + targets: + - ./apis/hello-world-api.yaml + - ./apis/petstore-api.yaml + - ./apis/spotify-api.yaml + - ./apis/streetlights-api.yaml + - ./apis/swapi-graphql.yaml diff --git a/packages/catalog-model/examples-relative/all-components.yaml b/packages/catalog-model/examples-relative/all-components.yaml new file mode 100644 index 0000000000..f29a7378a0 --- /dev/null +++ b/packages/catalog-model/examples-relative/all-components.yaml @@ -0,0 +1,16 @@ +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: example-components + description: A collection of all Backstage example components +spec: + targets: + - ./components/artist-lookup-component.yaml + - ./components/petstore-component.yaml + - ./components/playback-order-component.yaml + - ./components/podcast-api-component.yaml + - ./components/queue-proxy-component.yaml + - ./components/searcher-component.yaml + - ./components/playback-lib-component.yaml + - ./components/www-artist-component.yaml + - ./components/shuffle-api-component.yaml diff --git a/packages/catalog-model/examples-relative/apis/hello-world-api.yaml b/packages/catalog-model/examples-relative/apis/hello-world-api.yaml new file mode 100644 index 0000000000..0f9786329f --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/hello-world-api.yaml @@ -0,0 +1,48 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: hello-world + description: Hello World example for gRPC +spec: + type: grpc + lifecycle: deprecated + owner: team-c + definition: | + // Copyright 2015 gRPC 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. + + syntax = "proto3"; + + option java_multiple_files = true; + option java_package = "io.grpc.examples.helloworld"; + option java_outer_classname = "HelloWorldProto"; + option objc_class_prefix = "HLW"; + + package helloworld; + + // The greeting service definition. + service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + + // The request message containing the user's name. + message HelloRequest { + string name = 1; + } + + // The response message containing the greetings + message HelloReply { + string message = 1; + } diff --git a/packages/catalog-model/examples-relative/apis/petstore-api.yaml b/packages/catalog-model/examples-relative/apis/petstore-api.yaml new file mode 100644 index 0000000000..a314b4e255 --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/petstore-api.yaml @@ -0,0 +1,124 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: petstore + description: The petstore API + tags: + - store + - rest +spec: + type: openapi + lifecycle: experimental + owner: team-c + definition: | + openapi: "3.0.0" + info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + servers: + - url: http://petstore.swagger.io/v1 + paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/packages/catalog-model/examples-relative/apis/spotify-api.yaml b/packages/catalog-model/examples-relative/apis/spotify-api.yaml new file mode 100644 index 0000000000..0b8e80b86e --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/spotify-api.yaml @@ -0,0 +1,17 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: spotify + description: The Spotify web API + tags: + - spotify + - rest + annotations: + # The annotation is deprecated, we use placeholders (see below) instead, remove it later. + backstage.io/definition-at-location: 'url:https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml' +spec: + type: openapi + lifecycle: production + owner: team-a + definition: + $text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/spotify.com/v1/swagger.yaml diff --git a/packages/catalog-model/examples-relative/apis/streetlights-api.yaml b/packages/catalog-model/examples-relative/apis/streetlights-api.yaml new file mode 100644 index 0000000000..725811d0cc --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/streetlights-api.yaml @@ -0,0 +1,221 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: streetlights + description: The Smartylighting Streetlights API allows you to remotely manage the city lights. + tags: + - mqtt +spec: + type: asyncapi + lifecycle: production + owner: team-c + definition: | + asyncapi: 2.0.0 + info: + title: Streetlights API + version: '1.0.0' + description: | + The Smartylighting Streetlights API allows you to remotely manage the city lights. + + ### Check out its awesome features: + + * Turn a specific streetlight on/off 🌃 + * Dim a specific streetlight 😎 + * Receive real-time information about environmental lighting conditions 📈 + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + + servers: + production: + url: api.streetlights.smartylighting.com:{port} + protocol: mqtt + description: Test broker + variables: + port: + description: Secure connection (TLS) is available through port 8883. + default: '1883' + enum: + - '1883' + - '8883' + security: + - apiKey: [] + - supportedOauthFlows: + - streetlights:on + - streetlights:off + - streetlights:dim + - openIdConnectWellKnown: [] + + defaultContentType: application/json + + channels: + smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured: + description: The topic on which measured values may be produced and consumed. + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + subscribe: + summary: Receive information about environmental lighting conditions of a particular streetlight. + operationId: receiveLightMeasurement + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/lightMeasured' + + smartylighting/streetlights/1/0/action/{streetlightId}/turn/on: + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + publish: + operationId: turnOn + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/turnOnOff' + + smartylighting/streetlights/1/0/action/{streetlightId}/turn/off: + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + publish: + operationId: turnOff + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/turnOnOff' + + smartylighting/streetlights/1/0/action/{streetlightId}/dim: + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + publish: + operationId: dimLight + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/dimLight' + + components: + messages: + lightMeasured: + name: lightMeasured + title: Light measured + summary: Inform about environmental lighting conditions for a particular streetlight. + contentType: application/json + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: "#/components/schemas/lightMeasuredPayload" + turnOnOff: + name: turnOnOff + title: Turn on/off + summary: Command a particular streetlight to turn the lights on or off. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: "#/components/schemas/turnOnOffPayload" + dimLight: + name: dimLight + title: Dim light + summary: Command a particular streetlight to dim the lights. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: "#/components/schemas/dimLightPayload" + + schemas: + lightMeasuredPayload: + type: object + properties: + lumens: + type: integer + minimum: 0 + description: Light intensity measured in lumens. + sentAt: + $ref: "#/components/schemas/sentAt" + turnOnOffPayload: + type: object + properties: + command: + type: string + enum: + - on + - off + description: Whether to turn on or off the light. + sentAt: + $ref: "#/components/schemas/sentAt" + dimLightPayload: + type: object + properties: + percentage: + type: integer + description: Percentage to which the light should be dimmed to. + minimum: 0 + maximum: 100 + sentAt: + $ref: "#/components/schemas/sentAt" + sentAt: + type: string + format: date-time + description: Date and time when the message was sent. + + securitySchemes: + apiKey: + type: apiKey + in: user + description: Provide your API key as the user and leave the password empty. + supportedOauthFlows: + type: oauth2 + description: Flows to support OAuth 2.0 + flows: + implicit: + authorizationUrl: 'https://authserver.example/auth' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + password: + tokenUrl: 'https://authserver.example/token' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + clientCredentials: + tokenUrl: 'https://authserver.example/token' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + authorizationCode: + authorizationUrl: 'https://authserver.example/auth' + tokenUrl: 'https://authserver.example/token' + refreshUrl: 'https://authserver.example/refresh' + scopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + openIdConnectWellKnown: + type: openIdConnect + openIdConnectUrl: 'https://authserver.example/.well-known' + + parameters: + streetlightId: + description: The ID of the streetlight. + schema: + type: string + + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + + operationTraits: + kafka: + bindings: + kafka: + clientId: my-app-id diff --git a/packages/catalog-model/examples-relative/apis/swapi-graphql.yaml b/packages/catalog-model/examples-relative/apis/swapi-graphql.yaml new file mode 100644 index 0000000000..0c1f7af5a4 --- /dev/null +++ b/packages/catalog-model/examples-relative/apis/swapi-graphql.yaml @@ -0,0 +1,1176 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: starwars-graphql + description: SWAPI GraphQL Schema +spec: + type: graphql + lifecycle: production + owner: team-b + definition: | + schema { + query: Root + } + + """A single film.""" + type Film implements Node { + """The title of this film.""" + title: String + + """The episode number of this film.""" + episodeID: Int + + """The opening paragraphs at the beginning of this film.""" + openingCrawl: String + + """The name of the director of this film.""" + director: String + + """The name(s) of the producer(s) of this film.""" + producers: [String] + + """The ISO 8601 date format of film release at original creator country.""" + releaseDate: String + speciesConnection(after: String, first: Int, before: String, last: Int): FilmSpeciesConnection + starshipConnection(after: String, first: Int, before: String, last: Int): FilmStarshipsConnection + vehicleConnection(after: String, first: Int, before: String, last: Int): FilmVehiclesConnection + characterConnection(after: String, first: Int, before: String, last: Int): FilmCharactersConnection + planetConnection(after: String, first: Int, before: String, last: Int): FilmPlanetsConnection + + """The ISO 8601 date format of the time that this resource was created.""" + created: String + + """The ISO 8601 date format of the time that this resource was edited.""" + edited: String + + """The ID of an object""" + id: ID! + } + + """A connection to a list of items.""" + type FilmCharactersConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FilmCharactersEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + characters: [Person] + } + + """An edge in a connection.""" + type FilmCharactersEdge { + """The item at the end of the edge""" + node: Person + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type FilmPlanetsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FilmPlanetsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + planets: [Planet] + } + + """An edge in a connection.""" + type FilmPlanetsEdge { + """The item at the end of the edge""" + node: Planet + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type FilmsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FilmsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + films: [Film] + } + + """An edge in a connection.""" + type FilmsEdge { + """The item at the end of the edge""" + node: Film + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type FilmSpeciesConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FilmSpeciesEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + species: [Species] + } + + """An edge in a connection.""" + type FilmSpeciesEdge { + """The item at the end of the edge""" + node: Species + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type FilmStarshipsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FilmStarshipsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + starships: [Starship] + } + + """An edge in a connection.""" + type FilmStarshipsEdge { + """The item at the end of the edge""" + node: Starship + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type FilmVehiclesConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FilmVehiclesEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + vehicles: [Vehicle] + } + + """An edge in a connection.""" + type FilmVehiclesEdge { + """The item at the end of the edge""" + node: Vehicle + + """A cursor for use in pagination""" + cursor: String! + } + + """An object with an ID""" + interface Node { + """The id of the object.""" + id: ID! + } + + """Information about pagination in a connection.""" + type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String + } + + """A connection to a list of items.""" + type PeopleConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PeopleEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + people: [Person] + } + + """An edge in a connection.""" + type PeopleEdge { + """The item at the end of the edge""" + node: Person + + """A cursor for use in pagination""" + cursor: String! + } + + """An individual person or character within the Star Wars universe.""" + type Person implements Node { + """The name of this person.""" + name: String + + """ + The birth year of the person, using the in-universe standard of BBY or ABY - + Before the Battle of Yavin or After the Battle of Yavin. The Battle of Yavin is + a battle that occurs at the end of Star Wars episode IV: A New Hope. + """ + birthYear: String + + """ + The eye color of this person. Will be "unknown" if not known or "n/a" if the + person does not have an eye. + """ + eyeColor: String + + """ + The gender of this person. Either "Male", "Female" or "unknown", + "n/a" if the person does not have a gender. + """ + gender: String + + """ + The hair color of this person. Will be "unknown" if not known or "n/a" if the + person does not have hair. + """ + hairColor: String + + """The height of the person in centimeters.""" + height: Int + + """The mass of the person in kilograms.""" + mass: Float + + """The skin color of this person.""" + skinColor: String + + """A planet that this person was born on or inhabits.""" + homeworld: Planet + filmConnection(after: String, first: Int, before: String, last: Int): PersonFilmsConnection + + """The species that this person belongs to, or null if unknown.""" + species: Species + starshipConnection(after: String, first: Int, before: String, last: Int): PersonStarshipsConnection + vehicleConnection(after: String, first: Int, before: String, last: Int): PersonVehiclesConnection + + """The ISO 8601 date format of the time that this resource was created.""" + created: String + + """The ISO 8601 date format of the time that this resource was edited.""" + edited: String + + """The ID of an object""" + id: ID! + } + + """A connection to a list of items.""" + type PersonFilmsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PersonFilmsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + films: [Film] + } + + """An edge in a connection.""" + type PersonFilmsEdge { + """The item at the end of the edge""" + node: Film + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type PersonStarshipsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PersonStarshipsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + starships: [Starship] + } + + """An edge in a connection.""" + type PersonStarshipsEdge { + """The item at the end of the edge""" + node: Starship + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type PersonVehiclesConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PersonVehiclesEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + vehicles: [Vehicle] + } + + """An edge in a connection.""" + type PersonVehiclesEdge { + """The item at the end of the edge""" + node: Vehicle + + """A cursor for use in pagination""" + cursor: String! + } + + """ + A large mass, planet or planetoid in the Star Wars Universe, at the time of + 0 ABY. + """ + type Planet implements Node { + """The name of this planet.""" + name: String + + """The diameter of this planet in kilometers.""" + diameter: Int + + """ + The number of standard hours it takes for this planet to complete a single + rotation on its axis. + """ + rotationPeriod: Int + + """ + The number of standard days it takes for this planet to complete a single orbit + of its local star. + """ + orbitalPeriod: Int + + """ + A number denoting the gravity of this planet, where "1" is normal or 1 standard + G. "2" is twice or 2 standard Gs. "0.5" is half or 0.5 standard Gs. + """ + gravity: String + + """The average population of sentient beings inhabiting this planet.""" + population: Float + + """The climates of this planet.""" + climates: [String] + + """The terrains of this planet.""" + terrains: [String] + + """ + The percentage of the planet surface that is naturally occuring water or bodies + of water. + """ + surfaceWater: Float + residentConnection(after: String, first: Int, before: String, last: Int): PlanetResidentsConnection + filmConnection(after: String, first: Int, before: String, last: Int): PlanetFilmsConnection + + """The ISO 8601 date format of the time that this resource was created.""" + created: String + + """The ISO 8601 date format of the time that this resource was edited.""" + edited: String + + """The ID of an object""" + id: ID! + } + + """A connection to a list of items.""" + type PlanetFilmsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PlanetFilmsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + films: [Film] + } + + """An edge in a connection.""" + type PlanetFilmsEdge { + """The item at the end of the edge""" + node: Film + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type PlanetResidentsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PlanetResidentsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + residents: [Person] + } + + """An edge in a connection.""" + type PlanetResidentsEdge { + """The item at the end of the edge""" + node: Person + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type PlanetsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [PlanetsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + planets: [Planet] + } + + """An edge in a connection.""" + type PlanetsEdge { + """The item at the end of the edge""" + node: Planet + + """A cursor for use in pagination""" + cursor: String! + } + + type Root { + allFilms(after: String, first: Int, before: String, last: Int): FilmsConnection + film(id: ID, filmID: ID): Film + allPeople(after: String, first: Int, before: String, last: Int): PeopleConnection + person(id: ID, personID: ID): Person + allPlanets(after: String, first: Int, before: String, last: Int): PlanetsConnection + planet(id: ID, planetID: ID): Planet + allSpecies(after: String, first: Int, before: String, last: Int): SpeciesConnection + species(id: ID, speciesID: ID): Species + allStarships(after: String, first: Int, before: String, last: Int): StarshipsConnection + starship(id: ID, starshipID: ID): Starship + allVehicles(after: String, first: Int, before: String, last: Int): VehiclesConnection + vehicle(id: ID, vehicleID: ID): Vehicle + + """Fetches an object given its ID""" + node( + """The ID of an object""" + id: ID! + ): Node + } + + """A type of person or character within the Star Wars Universe.""" + type Species implements Node { + """The name of this species.""" + name: String + + """The classification of this species, such as "mammal" or "reptile".""" + classification: String + + """The designation of this species, such as "sentient".""" + designation: String + + """The average height of this species in centimeters.""" + averageHeight: Float + + """The average lifespan of this species in years, null if unknown.""" + averageLifespan: Int + + """ + Common eye colors for this species, null if this species does not typically + have eyes. + """ + eyeColors: [String] + + """ + Common hair colors for this species, null if this species does not typically + have hair. + """ + hairColors: [String] + + """ + Common skin colors for this species, null if this species does not typically + have skin. + """ + skinColors: [String] + + """The language commonly spoken by this species.""" + language: String + + """A planet that this species originates from.""" + homeworld: Planet + personConnection(after: String, first: Int, before: String, last: Int): SpeciesPeopleConnection + filmConnection(after: String, first: Int, before: String, last: Int): SpeciesFilmsConnection + + """The ISO 8601 date format of the time that this resource was created.""" + created: String + + """The ISO 8601 date format of the time that this resource was edited.""" + edited: String + + """The ID of an object""" + id: ID! + } + + """A connection to a list of items.""" + type SpeciesConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [SpeciesEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + species: [Species] + } + + """An edge in a connection.""" + type SpeciesEdge { + """The item at the end of the edge""" + node: Species + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type SpeciesFilmsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [SpeciesFilmsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + films: [Film] + } + + """An edge in a connection.""" + type SpeciesFilmsEdge { + """The item at the end of the edge""" + node: Film + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type SpeciesPeopleConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [SpeciesPeopleEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + people: [Person] + } + + """An edge in a connection.""" + type SpeciesPeopleEdge { + """The item at the end of the edge""" + node: Person + + """A cursor for use in pagination""" + cursor: String! + } + + """A single transport craft that has hyperdrive capability.""" + type Starship implements Node { + """The name of this starship. The common name, such as "Death Star".""" + name: String + + """ + The model or official name of this starship. Such as "T-65 X-wing" or "DS-1 + Orbital Battle Station". + """ + model: String + + """ + The class of this starship, such as "Starfighter" or "Deep Space Mobile + Battlestation" + """ + starshipClass: String + + """The manufacturers of this starship.""" + manufacturers: [String] + + """The cost of this starship new, in galactic credits.""" + costInCredits: Float + + """The length of this starship in meters.""" + length: Float + + """The number of personnel needed to run or pilot this starship.""" + crew: String + + """The number of non-essential people this starship can transport.""" + passengers: String + + """ + The maximum speed of this starship in atmosphere. null if this starship is + incapable of atmosphering flight. + """ + maxAtmospheringSpeed: Int + + """The class of this starships hyperdrive.""" + hyperdriveRating: Float + + """ + The Maximum number of Megalights this starship can travel in a standard hour. + A "Megalight" is a standard unit of distance and has never been defined before + within the Star Wars universe. This figure is only really useful for measuring + the difference in speed of starships. We can assume it is similar to AU, the + distance between our Sun (Sol) and Earth. + """ + MGLT: Int + + """The maximum number of kilograms that this starship can transport.""" + cargoCapacity: Float + + """ + The maximum length of time that this starship can provide consumables for its + entire crew without having to resupply. + """ + consumables: String + pilotConnection(after: String, first: Int, before: String, last: Int): StarshipPilotsConnection + filmConnection(after: String, first: Int, before: String, last: Int): StarshipFilmsConnection + + """The ISO 8601 date format of the time that this resource was created.""" + created: String + + """The ISO 8601 date format of the time that this resource was edited.""" + edited: String + + """The ID of an object""" + id: ID! + } + + """A connection to a list of items.""" + type StarshipFilmsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [StarshipFilmsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + films: [Film] + } + + """An edge in a connection.""" + type StarshipFilmsEdge { + """The item at the end of the edge""" + node: Film + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type StarshipPilotsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [StarshipPilotsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + pilots: [Person] + } + + """An edge in a connection.""" + type StarshipPilotsEdge { + """The item at the end of the edge""" + node: Person + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type StarshipsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [StarshipsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + starships: [Starship] + } + + """An edge in a connection.""" + type StarshipsEdge { + """The item at the end of the edge""" + node: Starship + + """A cursor for use in pagination""" + cursor: String! + } + + """A single transport craft that does not have hyperdrive capability""" + type Vehicle implements Node { + """ + The name of this vehicle. The common name, such as "Sand Crawler" or "Speeder + bike". + """ + name: String + + """ + The model or official name of this vehicle. Such as "All-Terrain Attack + Transport". + """ + model: String + + """The class of this vehicle, such as "Wheeled" or "Repulsorcraft".""" + vehicleClass: String + + """The manufacturers of this vehicle.""" + manufacturers: [String] + + """The cost of this vehicle new, in Galactic Credits.""" + costInCredits: Float + + """The length of this vehicle in meters.""" + length: Float + + """The number of personnel needed to run or pilot this vehicle.""" + crew: String + + """The number of non-essential people this vehicle can transport.""" + passengers: String + + """The maximum speed of this vehicle in atmosphere.""" + maxAtmospheringSpeed: Int + + """The maximum number of kilograms that this vehicle can transport.""" + cargoCapacity: Float + + """ + The maximum length of time that this vehicle can provide consumables for its + entire crew without having to resupply. + """ + consumables: String + pilotConnection(after: String, first: Int, before: String, last: Int): VehiclePilotsConnection + filmConnection(after: String, first: Int, before: String, last: Int): VehicleFilmsConnection + + """The ISO 8601 date format of the time that this resource was created.""" + created: String + + """The ISO 8601 date format of the time that this resource was edited.""" + edited: String + + """The ID of an object""" + id: ID! + } + + """A connection to a list of items.""" + type VehicleFilmsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [VehicleFilmsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + films: [Film] + } + + """An edge in a connection.""" + type VehicleFilmsEdge { + """The item at the end of the edge""" + node: Film + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type VehiclePilotsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [VehiclePilotsEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + pilots: [Person] + } + + """An edge in a connection.""" + type VehiclePilotsEdge { + """The item at the end of the edge""" + node: Person + + """A cursor for use in pagination""" + cursor: String! + } + + """A connection to a list of items.""" + type VehiclesConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [VehiclesEdge] + + """ + A count of the total number of objects in this connection, ignoring pagination. + This allows a client to fetch the first five objects by passing "5" as the + argument to "first", then fetch the total count so it could display "5 of 83", + for example. + """ + totalCount: Int + + """ + A list of all of the objects returned in the connection. This is a convenience + field provided for quickly exploring the API; rather than querying for + "{ edges { node } }" when no edge data is needed, this field can be be used + instead. Note that when clients like Relay need to fetch the "cursor" field on + the edge to enable efficient pagination, this shortcut cannot be used, and the + full "{ edges { node } }" version should be used instead. + """ + vehicles: [Vehicle] + } + + """An edge in a connection.""" + type VehiclesEdge { + """The item at the end of the edge""" + node: Vehicle + + """A cursor for use in pagination""" + cursor: String! + } diff --git a/packages/catalog-model/examples-relative/components/artist-lookup-component.yaml b/packages/catalog-model/examples-relative/components/artist-lookup-component.yaml new file mode 100644 index 0000000000..257344be3d --- /dev/null +++ b/packages/catalog-model/examples-relative/components/artist-lookup-component.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: artist-lookup + description: Artist Lookup + tags: + - java + - data +spec: + type: service + lifecycle: experimental + owner: team-a diff --git a/packages/catalog-model/examples-relative/components/petstore-component.yaml b/packages/catalog-model/examples-relative/components/petstore-component.yaml new file mode 100644 index 0000000000..e6dd56c274 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/petstore-component.yaml @@ -0,0 +1,13 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: petstore + description: Petstore +spec: + type: service + lifecycle: experimental + owner: team-c + implementsApis: + - petstore + - streetlights + - hello-world diff --git a/packages/catalog-model/examples-relative/components/playback-lib-component.yaml b/packages/catalog-model/examples-relative/components/playback-lib-component.yaml new file mode 100644 index 0000000000..f7d7670b5d --- /dev/null +++ b/packages/catalog-model/examples-relative/components/playback-lib-component.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: playback-sdk + description: Audio and video playback SDK +spec: + type: library + lifecycle: experimental + owner: team-c diff --git a/packages/catalog-model/examples-relative/components/playback-order-component.yaml b/packages/catalog-model/examples-relative/components/playback-order-component.yaml new file mode 100644 index 0000000000..c4f41b2b58 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/playback-order-component.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: playback-order + description: Playback Order + tags: + - java + - playback +spec: + type: service + lifecycle: production + owner: user:guest diff --git a/packages/catalog-model/examples-relative/components/podcast-api-component.yaml b/packages/catalog-model/examples-relative/components/podcast-api-component.yaml new file mode 100644 index 0000000000..b89ff48c48 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/podcast-api-component.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: podcast-api + description: Podcast API + tags: + - java +spec: + type: service + lifecycle: experimental + owner: team-b diff --git a/packages/catalog-model/examples-relative/components/queue-proxy-component.yaml b/packages/catalog-model/examples-relative/components/queue-proxy-component.yaml new file mode 100644 index 0000000000..7f7fcbd527 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/queue-proxy-component.yaml @@ -0,0 +1,12 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: queue-proxy + description: Queue Proxy + tags: + - go + - website +spec: + type: website + lifecycle: production + owner: team-b diff --git a/packages/catalog-model/examples-relative/components/searcher-component.yaml b/packages/catalog-model/examples-relative/components/searcher-component.yaml new file mode 100644 index 0000000000..77150c96f0 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/searcher-component.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: searcher + description: Searcher + tags: + - go +spec: + type: service + lifecycle: production + owner: user:guest diff --git a/packages/catalog-model/examples-relative/components/shuffle-api-component.yaml b/packages/catalog-model/examples-relative/components/shuffle-api-component.yaml new file mode 100644 index 0000000000..1c2da03511 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/shuffle-api-component.yaml @@ -0,0 +1,11 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: shuffle-api + description: Shuffle API + tags: + - go +spec: + type: service + lifecycle: production + owner: user:guest diff --git a/packages/catalog-model/examples-relative/components/www-artist-component.yaml b/packages/catalog-model/examples-relative/components/www-artist-component.yaml new file mode 100644 index 0000000000..c333eb8c09 --- /dev/null +++ b/packages/catalog-model/examples-relative/components/www-artist-component.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: www-artist + description: Artist main website +spec: + type: website + lifecycle: production + owner: team-a diff --git a/packages/catalog-model/examples/README.md b/packages/catalog-model/examples/README.md new file mode 100644 index 0000000000..abf4934f3f --- /dev/null +++ b/packages/catalog-model/examples/README.md @@ -0,0 +1,7 @@ +# Example Entities + +NOTE: These are being replaced with the contents of the `examples-relative` +sibling directory, after December 16 2020. If you are using this example data +you will need to be using a released version of the catalog backend which +supports relative targets in Location entities +[see #3513](https://github.com/backstage/backstage/pull/3513) after that time. diff --git a/packages/catalog-model/examples/acme/team-a-group.yaml b/packages/catalog-model/examples/acme/team-a-group.yaml index dacc4c6c3c..8dbf0bb839 100644 --- a/packages/catalog-model/examples/acme/team-a-group.yaml +++ b/packages/catalog-model/examples/acme/team-a-group.yaml @@ -42,3 +42,17 @@ spec: email: nigel-manning@example.com picture: https://example.com/staff/nigel.jpeg memberOf: [team-a] +--- +# This user is added as an example, to make it more easy for the "Guest" +# sign-in option to demonstrate some entities being owned. In a regular org, +# a guest user would probably not be registered like this. +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: guest +spec: + profile: + displayName: Guest User + email: guest@example.com + picture: https://example.com/staff/the-ceos-dog.jpeg + memberOf: [team-a] diff --git a/packages/catalog-model/examples/apis/hello-world-api.yaml b/packages/catalog-model/examples/apis/hello-world-api.yaml index 659c48adfb..0f9786329f 100644 --- a/packages/catalog-model/examples/apis/hello-world-api.yaml +++ b/packages/catalog-model/examples/apis/hello-world-api.yaml @@ -6,7 +6,7 @@ metadata: spec: type: grpc lifecycle: deprecated - owner: grpc@example.com + owner: team-c definition: | // Copyright 2015 gRPC authors. // diff --git a/packages/catalog-model/examples/apis/petstore-api.yaml b/packages/catalog-model/examples/apis/petstore-api.yaml index c8ce576cce..a314b4e255 100644 --- a/packages/catalog-model/examples/apis/petstore-api.yaml +++ b/packages/catalog-model/examples/apis/petstore-api.yaml @@ -9,7 +9,7 @@ metadata: spec: type: openapi lifecycle: experimental - owner: pets@example.com + owner: team-c definition: | openapi: "3.0.0" info: diff --git a/packages/catalog-model/examples/apis/spotify-api.yaml b/packages/catalog-model/examples/apis/spotify-api.yaml index 14deb17abe..0b8e80b86e 100644 --- a/packages/catalog-model/examples/apis/spotify-api.yaml +++ b/packages/catalog-model/examples/apis/spotify-api.yaml @@ -12,6 +12,6 @@ metadata: spec: type: openapi lifecycle: production - owner: spotify@example.com + owner: team-a definition: $text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/spotify.com/v1/swagger.yaml diff --git a/packages/catalog-model/examples/apis/streetlights-api.yaml b/packages/catalog-model/examples/apis/streetlights-api.yaml index d53b05fc2d..725811d0cc 100644 --- a/packages/catalog-model/examples/apis/streetlights-api.yaml +++ b/packages/catalog-model/examples/apis/streetlights-api.yaml @@ -8,7 +8,7 @@ metadata: spec: type: asyncapi lifecycle: production - owner: streetlights@example.com + owner: team-c definition: | asyncapi: 2.0.0 info: diff --git a/packages/catalog-model/examples/apis/swapi-graphql.yaml b/packages/catalog-model/examples/apis/swapi-graphql.yaml index bc5e8e60ac..0c1f7af5a4 100644 --- a/packages/catalog-model/examples/apis/swapi-graphql.yaml +++ b/packages/catalog-model/examples/apis/swapi-graphql.yaml @@ -6,7 +6,7 @@ metadata: spec: type: graphql lifecycle: production - owner: yoda@dagobah.space + owner: team-b definition: | schema { query: Root diff --git a/packages/catalog-model/examples/components/artist-lookup-component.yaml b/packages/catalog-model/examples/components/artist-lookup-component.yaml index 79dd3bccf6..257344be3d 100644 --- a/packages/catalog-model/examples/components/artist-lookup-component.yaml +++ b/packages/catalog-model/examples/components/artist-lookup-component.yaml @@ -9,4 +9,4 @@ metadata: spec: type: service lifecycle: experimental - owner: artists@example.com + owner: team-a diff --git a/packages/catalog-model/examples/components/petstore-component.yaml b/packages/catalog-model/examples/components/petstore-component.yaml index 7e2093ad09..e6dd56c274 100644 --- a/packages/catalog-model/examples/components/petstore-component.yaml +++ b/packages/catalog-model/examples/components/petstore-component.yaml @@ -6,7 +6,7 @@ metadata: spec: type: service lifecycle: experimental - owner: pets@example.com + owner: team-c implementsApis: - petstore - streetlights diff --git a/packages/catalog-model/examples/components/playback-lib-component.yaml b/packages/catalog-model/examples/components/playback-lib-component.yaml index c32332d4d2..f7d7670b5d 100644 --- a/packages/catalog-model/examples/components/playback-lib-component.yaml +++ b/packages/catalog-model/examples/components/playback-lib-component.yaml @@ -6,4 +6,4 @@ metadata: spec: type: library lifecycle: experimental - owner: players@example.com + owner: team-c diff --git a/packages/catalog-model/examples/components/playback-order-component.yaml b/packages/catalog-model/examples/components/playback-order-component.yaml index 3e46953928..c4f41b2b58 100644 --- a/packages/catalog-model/examples/components/playback-order-component.yaml +++ b/packages/catalog-model/examples/components/playback-order-component.yaml @@ -9,4 +9,4 @@ metadata: spec: type: service lifecycle: production - owner: guest + owner: user:guest diff --git a/packages/catalog-model/examples/components/podcast-api-component.yaml b/packages/catalog-model/examples/components/podcast-api-component.yaml index c1b1c9281c..b89ff48c48 100644 --- a/packages/catalog-model/examples/components/podcast-api-component.yaml +++ b/packages/catalog-model/examples/components/podcast-api-component.yaml @@ -8,4 +8,4 @@ metadata: spec: type: service lifecycle: experimental - owner: players@example.com + owner: team-b diff --git a/packages/catalog-model/examples/components/queue-proxy-component.yaml b/packages/catalog-model/examples/components/queue-proxy-component.yaml index c9b130db52..7f7fcbd527 100644 --- a/packages/catalog-model/examples/components/queue-proxy-component.yaml +++ b/packages/catalog-model/examples/components/queue-proxy-component.yaml @@ -9,4 +9,4 @@ metadata: spec: type: website lifecycle: production - owner: tools@example.com + owner: team-b diff --git a/packages/catalog-model/examples/components/searcher-component.yaml b/packages/catalog-model/examples/components/searcher-component.yaml index 042fcb24a9..77150c96f0 100644 --- a/packages/catalog-model/examples/components/searcher-component.yaml +++ b/packages/catalog-model/examples/components/searcher-component.yaml @@ -8,4 +8,4 @@ metadata: spec: type: service lifecycle: production - owner: guest + owner: user:guest diff --git a/packages/catalog-model/examples/components/shuffle-api-component.yaml b/packages/catalog-model/examples/components/shuffle-api-component.yaml index 1f9de46d4e..1c2da03511 100644 --- a/packages/catalog-model/examples/components/shuffle-api-component.yaml +++ b/packages/catalog-model/examples/components/shuffle-api-component.yaml @@ -8,4 +8,4 @@ metadata: spec: type: service lifecycle: production - owner: guest + owner: user:guest diff --git a/packages/catalog-model/examples/components/www-artist-component.yaml b/packages/catalog-model/examples/components/www-artist-component.yaml index 84379c93fc..c333eb8c09 100644 --- a/packages/catalog-model/examples/components/www-artist-component.yaml +++ b/packages/catalog-model/examples/components/www-artist-component.yaml @@ -6,4 +6,4 @@ metadata: spec: type: website lifecycle: production - owner: artists@example.com + owner: team-a diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 53f56c6600..25406c8eef 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.3.1", + "version": "0.4.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts index 5fd4f88537..680674f16e 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts @@ -32,15 +32,6 @@ const schema = yup.object>({ implementsApis: yup.array(yup.string().required()).notRequired(), providesApis: yup.array(yup.string().required()).notRequired(), consumesApis: yup.array(yup.string().required()).notRequired(), - kubernetes: yup - .object({ - selector: yup - .object({ - matchLabels: yup.object().required(), - }) - .required(), - }) - .notRequired(), }) .required(), }); @@ -60,13 +51,6 @@ export interface ComponentEntityV1alpha1 extends Entity { implementsApis?: string[]; providesApis?: string[]; consumesApis?: string[]; - kubernetes?: { - selector: { - matchLabels: { - [key: string]: string; - }; - }; - }; }; } diff --git a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts index d9c1e9185f..2451df8e64 100644 --- a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts @@ -54,9 +54,9 @@ describe('LocationV1alpha1Validator', () => { await expect(validator.check(entity)).resolves.toBe(false); }); - it('rejects missing type', async () => { + it('accepts missing type', async () => { delete (entity as any).spec.type; - await expect(validator.check(entity)).rejects.toThrow(/type/); + await expect(validator.check(entity)).resolves.toBe(true); }); it('rejects wrong type', async () => { diff --git a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts index e536be7922..9cd767de94 100644 --- a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts @@ -26,7 +26,7 @@ const schema = yup.object>({ kind: yup.string().required().equals([KIND]), spec: yup .object({ - type: yup.string().required().min(1), + type: yup.string().notRequired().min(1), target: yup.string().notRequired().min(1), targets: yup.array(yup.string().required()).notRequired(), }) @@ -37,7 +37,7 @@ export interface LocationEntityV1alpha1 extends Entity { apiVersion: typeof API_VERSION[number]; kind: typeof KIND; spec: { - type: string; + type?: string; target?: string; targets?: string[]; }; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index d1770eaf1f..45214cd1e0 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/cli +## 0.4.0 + +### Minor Changes + +- 00670a96e: sort product panels and navigation menu by greatest cost + update tsconfig.json to use ES2020 api + +### Patch Changes + +- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError +- 4a655c89d: Bump versions of `esbuild` and `rollup-plugin-esbuild` +- 8a16e8af8: Support `.npmrc` when building with private NPM registries +- Updated dependencies [4e7091759] +- Updated dependencies [b4488ddb0] + - @backstage/config-loader@0.4.0 + ## 0.3.2 ### Patch Changes diff --git a/packages/cli/asset-types/asset-types.d.ts b/packages/cli/asset-types/asset-types.d.ts index 28c1fde9e3..9db4438fd4 100644 --- a/packages/cli/asset-types/asset-types.d.ts +++ b/packages/cli/asset-types/asset-types.d.ts @@ -97,3 +97,15 @@ declare module '*.module.sass' { const classes: { readonly [key: string]: string }; export default classes; } + +// NOTE(freben): Both the fix, and the placement of the fix, are not great. +// +// The fix is because the PositionError was renamed to +// GeolocationPositionError outside of our control, and react-use is dependent +// on the old name. +// +// The placement is because it's the one location we have at the moment, where +// a central .d.ts file is imported by the frontend and can be amended. +// +// After both TS and react-use are bumped high enough, this should be removed. +type PositionError = GeolocationPositionError; diff --git a/packages/cli/package.json b/packages/cli/package.json index eff546168e..fd02473453 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.3.2", + "version": "0.4.0", "private": false, "publishConfig": { "access": "public" @@ -30,7 +30,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.1", - "@backstage/config-loader": "^0.3.0", + "@backstage/config-loader": "^0.4.0", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^3.18.5", "@lerna/project": "^3.18.0", @@ -111,11 +111,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.3.2", + "@backstage/backend-common": "^0.3.3", "@backstage/config": "^0.1.1", "@backstage/core": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@types/diff": "^4.0.2", "@types/fs-extra": "^9.0.1", diff --git a/packages/cli/src/commands/backend/buildImage.ts b/packages/cli/src/commands/backend/buildImage.ts index b6ae0dd579..654f51cd10 100644 --- a/packages/cli/src/commands/backend/buildImage.ts +++ b/packages/cli/src/commands/backend/buildImage.ts @@ -34,11 +34,15 @@ export default async (cmd: Command) => { const pkgPath = paths.resolveTarget(PKG_PATH); const pkg = await fs.readJson(pkgPath); const appConfigs = await findAppConfigs(); + const npmrc = (await fs.pathExists(paths.resolveTargetRoot('.npmrc'))) + ? ['.npmrc'] + : []; const tempDistWorkspace = await createDistWorkspace([pkg.name], { buildDependencies: Boolean(cmd.build), files: [ 'package.json', 'yarn.lock', + ...npmrc, ...appConfigs, { src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' }, ], diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 2b0e3e25b9..f773f279fc 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -47,6 +47,7 @@ export function registerCommands(program: CommanderStatic) { program .command('backend:__experimental__bundle__', { hidden: true }) .description('Bundle all backend packages into dist-workspace') + .option('--build', 'Build packages before packing them into the image') .action(lazy(() => import('./backend/bundle').then(m => m.default))); program @@ -55,7 +56,11 @@ export function registerCommands(program: CommanderStatic) { .helpOption(', --backstage-cli-help') // Let docker handle --help .option('--build', 'Build packages before packing them into the image') .description( - 'Bundles the package into a docker image. All extra args are forwarded to docker image build', + // TODO: Add example use cases in Backstage documentation. + // For example, if a $NPM_TOKEN needs to be exposed, run `backend:build-image --secret + // id=NPM_TOKEN,src=/NPM_TOKEN.txt`. + 'Bundles the package into a docker image. All extra args are forwarded to ' + + '`docker image build`.', ) .action(lazy(() => import('./backend/buildImage').then(m => m.default))); diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 729dc81206..30891c2dd9 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/config-loader +## 0.4.0 + +### Minor Changes + +- 4e7091759: Fix typo of "visibility" in config schema reference + + If you have defined a config element named `visiblity`, you + will need to fix the spelling to `visibility`. For more info, + see https://backstage.io/docs/conf/defining#visibility. + +### Patch Changes + +- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError + ## 0.3.0 ### Minor Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 1584a61df5..3e2c58c676 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.3.0", + "version": "0.4.0", "private": false, "publishConfig": { "access": "public", @@ -36,7 +36,7 @@ "fs-extra": "^9.0.0", "json-schema": "^0.2.5", "json-schema-merge-allof": "^0.7.0", - "typescript-json-schema": "^0.43.0", + "typescript-json-schema": "^0.45.0", "yaml": "^1.9.2", "yup": "^0.29.3" }, diff --git a/packages/core-api/CHANGELOG.md b/packages/core-api/CHANGELOG.md index 79bed2dfa3..ab333b998e 100644 --- a/packages/core-api/CHANGELOG.md +++ b/packages/core-api/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/core-api +## 0.2.4 + +### Patch Changes + +- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError + - @backstage/test-utils@0.1.4 + ## 0.2.3 ### Patch Changes diff --git a/packages/core-api/package.json b/packages/core-api/package.json index fe56d11b9e..bfff89f27a 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.2.3", + "version": "0.2.4", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.1", - "@backstage/test-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -42,7 +42,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@backstage/test-utils-core": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/core-api/src/apis/implementations/OAuthRequestApi/OAuthPendingRequests.ts b/packages/core-api/src/apis/implementations/OAuthRequestApi/OAuthPendingRequests.ts index 6287f35015..e616ece5ae 100644 --- a/packages/core-api/src/apis/implementations/OAuthRequestApi/OAuthPendingRequests.ts +++ b/packages/core-api/src/apis/implementations/OAuthRequestApi/OAuthPendingRequests.ts @@ -19,7 +19,7 @@ import { Observable } from '../../../types'; type RequestQueueEntry = { scopes: Set; - resolve: (value?: ResultType | PromiseLike | undefined) => void; + resolve: (value: ResultType | PromiseLike) => void; reject: (reason: Error) => void; }; diff --git a/packages/core-api/src/extensions/componentData.test.tsx b/packages/core-api/src/extensions/componentData.test.tsx new file mode 100644 index 0000000000..6c4abda40f --- /dev/null +++ b/packages/core-api/src/extensions/componentData.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { attachComponentData, getComponentData } from './componentData'; + +describe('elementData', () => { + it('should attach a single piece of data', () => { + const data = { foo: 'bar' }; + const Component = () => null; + attachComponentData(Component, 'my-data', data); + + const element = ; + expect(getComponentData(element, 'my-data')).toBe(data); + }); + + it('should attach several distinct pieces of data', () => { + const data1 = { foo: 'bar' }; + const data2 = { test: 'value' }; + const Component = () => null; + attachComponentData(Component, 'my-data', data1); + attachComponentData(Component, 'second', data2); + + const element = ; + expect(getComponentData(element, 'my-data')).toBe(data1); + expect(getComponentData(element, 'second')).toBe(data2); + }); + + it('returns undefined for missing data', () => { + const data = { foo: 'bar' }; + const Component1 = () => null; + const Component2 = () => null; + attachComponentData(Component2, 'my-data', data); + + const element1 = ; + const element2 = ; + expect(getComponentData(element1, 'missing')).toBeUndefined(); + expect(getComponentData(element2, 'missing')).toBeUndefined(); + }); + + it('should throw when attempting to overwrite data', () => { + const data = { foo: 'bar' }; + const MyComponent = () => null; + attachComponentData(MyComponent, 'my-data', data); + expect(() => attachComponentData(MyComponent, 'my-data', data)).toThrow( + 'Attempted to attach duplicate data "my-data" to component "MyComponent"', + ); + }); +}); diff --git a/packages/core-api/src/extensions/componentData.tsx b/packages/core-api/src/extensions/componentData.tsx new file mode 100644 index 0000000000..1f88356a48 --- /dev/null +++ b/packages/core-api/src/extensions/componentData.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { ComponentType, ReactNode } from 'react'; + +const DATA_KEY = Symbol('backstage-component-data'); + +type DataContainer = { + map: Map; +}; + +type ComponentWithData

= ComponentType

& { + [DATA_KEY]?: DataContainer; +}; + +type ReactNodeWithData = ReactNode & { + type?: { [DATA_KEY]?: DataContainer }; +}; + +export function attachComponentData

( + component: ComponentType

, + type: string, + data: unknown, +) { + const dataComponent = component as ComponentWithData

; + + let container = dataComponent[DATA_KEY]; + if (!container) { + container = dataComponent[DATA_KEY] = { map: new Map() }; + } + + if (container.map.has(type)) { + const name = component.displayName || component.name; + throw new Error( + `Attempted to attach duplicate data "${type}" to component "${name}"`, + ); + } + + container.map.set(type, data); +} + +export function getComponentData( + node: ReactNode, + type: string, +): T | undefined { + if (!node) { + return undefined; + } + + const container = (node as ReactNodeWithData).type?.[DATA_KEY]; + if (!container) { + return undefined; + } + + return container.map.get(type) as T | undefined; +} diff --git a/packages/core-api/src/extensions/extensions.test.tsx b/packages/core-api/src/extensions/extensions.test.tsx new file mode 100644 index 0000000000..92fcecebc4 --- /dev/null +++ b/packages/core-api/src/extensions/extensions.test.tsx @@ -0,0 +1,72 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React from 'react'; +import { createPlugin } from '../plugin'; +import { createRouteRef } from '../routing'; +import { getComponentData } from './componentData'; +import { + createComponentExtension, + createReactExtension, + createRoutableExtension, +} from './extensions'; + +const plugin = createPlugin({ + id: 'my-plugin', +}); + +describe('extensions', () => { + it('should create a react extension with component data', () => { + const Component = () => null; + + const extension = createReactExtension({ + component: Component, + data: { + myData: { foo: 'bar' }, + }, + }); + + const ExtensionComponent = plugin.provide(extension); + const element = ; + + expect(getComponentData(element, 'core.plugin')).toBe(plugin); + expect(getComponentData(element, 'myData')).toEqual({ foo: 'bar' }); + }); + + it('should create react extensions of different types', () => { + const Component = () => null; + const routeRef = createRouteRef({ path: '/foo', title: 'Foo' }); + + const extension1 = createComponentExtension({ + component: Component, + }); + + const extension2 = createRoutableExtension({ + component: Component, + mountPoint: routeRef, + }); + + const ExtensionComponent1 = plugin.provide(extension1); + const ExtensionComponent2 = plugin.provide(extension2); + + const element1 = ; + const element2 = ; + + expect(getComponentData(element1, 'core.plugin')).toBe(plugin); + expect(getComponentData(element2, 'core.plugin')).toBe(plugin); + expect(getComponentData(element2, 'core.mountPoint')).toBe(routeRef); + }); +}); diff --git a/packages/core-api/src/extensions/extensions.tsx b/packages/core-api/src/extensions/extensions.tsx new file mode 100644 index 0000000000..4087d5f963 --- /dev/null +++ b/packages/core-api/src/extensions/extensions.tsx @@ -0,0 +1,72 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { + NamedExoticComponent, + ComponentType, + PropsWithChildren, +} from 'react'; +import { RouteRef } from '../routing'; +import { attachComponentData } from './componentData'; +import { Extension, BackstagePlugin } from '../plugin/types'; + +export function createRoutableExtension(options: { + component: ComponentType; + mountPoint: RouteRef; + // TODO(Rugvip): We want to carry forward the exact props type from the inner component, with + // or without children. ComponentType stops us from doing that though, as it always + // adds children to the props internally. We may want to work around this with custom types. +}): Extension< + NamedExoticComponent> +> { + const { component, mountPoint } = options; + return createReactExtension({ + component, + data: { + 'core.mountPoint': mountPoint, + }, + }); +} + +export function createComponentExtension(options: { + component: ComponentType; +}): Extension> { + const { component } = options; + return createReactExtension({ component }); +} + +export function createReactExtension(options: { + component: ComponentType; + data?: Record; +}): Extension> { + const { component: Component, data = {} } = options; + return { + expose(plugin: BackstagePlugin): NamedExoticComponent { + const Result = (props: Props) => ; + + attachComponentData(Result, 'core.plugin', plugin); + for (const [key, value] of Object.entries(data)) { + attachComponentData(Result, key, value); + } + + const name = Component.displayName || Component.name || 'Component'; + if (name) { + Result.displayName = `Extension(${name})`; + } + return Result as NamedExoticComponent; + }, + }; +} diff --git a/packages/core-api/src/extensions/index.ts b/packages/core-api/src/extensions/index.ts new file mode 100644 index 0000000000..26a0c597b1 --- /dev/null +++ b/packages/core-api/src/extensions/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { attachComponentData, getComponentData } from './componentData'; +export { + createReactExtension, + createRoutableExtension, + createComponentExtension, +} from './extensions'; diff --git a/packages/core-api/src/extensions/traversal.test.tsx b/packages/core-api/src/extensions/traversal.test.tsx new file mode 100644 index 0000000000..a69ee9de77 --- /dev/null +++ b/packages/core-api/src/extensions/traversal.test.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { Children, isValidElement } from 'react'; +import { + childDiscoverer, + createCollector, + traverseElementTree, +} from './traversal'; + +describe('discovery', () => { + it('should collect element names', () => { + const root = ( +

+
+

Title

+

Text

+
+
+
+

Title

+ Text +
+
+ ); + + const { names } = traverseElementTree({ + root, + discoverers: [childDiscoverer], + collectors: { + names: createCollector(Array(), (acc, el) => { + if (typeof el.type === 'string') { + acc.push(el.type); + } + }), + }, + }); + + expect(names).toEqual([ + 'main', + 'div', + 'hr', + 'div', + 'h1', + 'p', + 'h2', + 'span', + ]); + }); + + it('should collect element names while skipping one level of children', () => { + const root = ( +
+
+

Title

+

Text

+
+
+
+

Title

+ Text +
+
+ ); + + const { names } = traverseElementTree({ + root, + discoverers: [ + el => + Children.toArray(el.props.children).flatMap(child => + isValidElement(child) ? child?.props?.children : [], + ), + ], + collectors: { + names: createCollector(Array(), (acc, el) => { + if (typeof el.type === 'string') { + acc.push(el.type); + } + }), + }, + }); + + expect(names).toEqual(['main', 'h1', 'p', 'h2', 'span']); + }); +}); diff --git a/packages/core-api/src/extensions/traversal.ts b/packages/core-api/src/extensions/traversal.ts new file mode 100644 index 0000000000..a1fbdab25a --- /dev/null +++ b/packages/core-api/src/extensions/traversal.ts @@ -0,0 +1,138 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { isValidElement, ReactNode, ReactElement, Children } from 'react'; + +export type Discoverer = (element: ReactElement) => ReactNode; + +export type Collector = () => { + accumulator: Result; + visit( + accumulator: Result, + element: ReactElement, + parent: ReactElement, + context: Context, + ): Context; +}; + +/** + * A function that allows you to traverse a tree of React elements using + * varying methods to discover child nodes and collect data along the way. + */ +export function traverseElementTree(options: { + root: ReactElement; + discoverers: Discoverer[]; + collectors: { [name in keyof Results]: Collector }; +}): Results { + const visited = new Set(); + const collectors: { + [name in string]: ReturnType>; + } = {}; + + // Bootstrap all collectors, initializing the accumulators and providing the visitor function + for (const name in options.collectors) { + if (options.collectors.hasOwnProperty(name)) { + collectors[name] = options.collectors[name](); + } + } + + // Internal representation of an element in the tree that we're iterating over + type QueueItem = { + node: ReactNode; + parent: ReactElement; + contexts: { [name in string]: unknown }; + }; + + const queue = [ + { + node: Children.toArray(options.root), + parent: options.root, + contexts: {}, + } as QueueItem, + ]; + + while (queue.length !== 0) { + const { node, parent, contexts } = queue.shift()!; + + // While the parent and the element we pass on to collectors and discoverers + // have been validated and are known to be React elements, the child nodes + // emitted by the discoverers are not. + Children.forEach(node, element => { + if (!isValidElement(element)) { + return; + } + if (visited.has(element)) { + const anyType = element?.type as + | { displayName?: string; name?: string } + | undefined; + const name = anyType?.displayName || anyType?.name || String(anyType); + throw new Error(`Visited element ${name} twice`); + } + visited.add(element); + + const nextContexts: QueueItem['contexts'] = {}; + + // Collectors populate their result data using the current node, and compute + // context for the next iteration + for (const name in collectors) { + if (collectors.hasOwnProperty(name)) { + const collector = collectors[name]; + + nextContexts[name] = collector.visit( + collector.accumulator, + element, + parent, + contexts[name], + ); + } + } + + // Discoverers provide ways to continue the traversal from the current element + for (const discoverer of options.discoverers) { + const children = discoverer(element); + if (children) { + queue.push({ + node: children, + parent: element, + contexts: nextContexts, + }); + } + } + }); + } + + return Object.fromEntries( + Object.entries(collectors).map(([name, c]) => [name, c.accumulator]), + ) as Results; +} + +export function createCollector( + initialResult: Result, + visit: ReturnType>['visit'], +): Collector { + return () => ({ accumulator: initialResult, visit }); +} + +export function childDiscoverer(element: ReactElement): ReactNode { + return element.props?.children; +} + +export function routeElementDiscoverer(element: ReactElement): ReactNode { + if (element.props?.path && element.props?.element) { + return element.props?.element; + } + return undefined; +} diff --git a/packages/core-api/src/plugin/Plugin.tsx b/packages/core-api/src/plugin/Plugin.tsx index d69bb7e721..f477674f4d 100644 --- a/packages/core-api/src/plugin/Plugin.tsx +++ b/packages/core-api/src/plugin/Plugin.tsx @@ -14,10 +14,15 @@ * limitations under the License. */ -import { PluginConfig, PluginOutput, BackstagePlugin } from './types'; +import { + PluginConfig, + PluginOutput, + BackstagePlugin, + Extension, +} from './types'; import { AnyApiFactory } from '../apis'; -export class PluginImpl { +export class PluginImpl implements BackstagePlugin { private storedOutput?: PluginOutput[]; constructor(private readonly config: PluginConfig) {} @@ -65,6 +70,10 @@ export class PluginImpl { return this.storedOutput; } + provide(extension: Extension): T { + return extension.expose(this); + } + toString() { return `plugin{${this.config.id}}`; } diff --git a/packages/core-api/src/plugin/collectors.test.tsx b/packages/core-api/src/plugin/collectors.test.tsx new file mode 100644 index 0000000000..f040cfd433 --- /dev/null +++ b/packages/core-api/src/plugin/collectors.test.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { PropsWithChildren } from 'react'; +import { createRouteRef } from '../routing'; +import { createPlugin } from './Plugin'; +import { + createRoutableExtension, + createComponentExtension, +} from '../extensions'; +import { MemoryRouter, Routes, Route } from 'react-router-dom'; +import { + traverseElementTree, + childDiscoverer, + routeElementDiscoverer, +} from '../extensions/traversal'; +import { pluginCollector } from './collectors'; + +const mockConfig = () => ({ path: '/foo', title: 'Foo' }); +const MockComponent = ({ children }: PropsWithChildren<{}>) => <>{children}; + +const pluginA = createPlugin({ id: 'my-plugin-a' }); +const pluginB = createPlugin({ id: 'my-plugin-b' }); +const pluginC = createPlugin({ id: 'my-plugin-c' }); + +const ref1 = createRouteRef(mockConfig()); +const ref2 = createRouteRef(mockConfig()); + +const Extension1 = pluginA.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref1 }), +); +const Extension2 = pluginB.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref2 }), +); +const Extension3 = pluginA.provide( + createComponentExtension({ component: MockComponent }), +); +const Extension4 = pluginB.provide( + createComponentExtension({ component: MockComponent }), +); +const Extension5 = pluginC.provide( + createComponentExtension({ component: MockComponent }), +); + +describe('collection', () => { + it('should collect the plugins', () => { + const root = ( + + + +
+ +
+
+ {[]} + Some text here shouldn't be a problem +
+ {null} +
+ +
+ + {false} + {true} + {0} +
+ +
+ } /> +
+ + + ); + + const { plugins } = traverseElementTree({ + root, + discoverers: [childDiscoverer, routeElementDiscoverer], + collectors: { + plugins: pluginCollector, + }, + }); + + expect(plugins).toEqual(new Set([pluginA, pluginB, pluginC])); + }); +}); diff --git a/packages/core-api/src/plugin/collectors.ts b/packages/core-api/src/plugin/collectors.ts new file mode 100644 index 0000000000..a222746f9e --- /dev/null +++ b/packages/core-api/src/plugin/collectors.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2020 Spotify AB + * + * 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. + */ +/* + * Copyright 2020 Spotify AB + * + * 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 { BackstagePlugin } from './types'; +import { getComponentData } from '../extensions'; +import { createCollector } from '../extensions/traversal'; + +export const pluginCollector = createCollector( + new Set(), + (acc, node) => { + const plugin = getComponentData(node, 'core.plugin'); + if (plugin) { + acc.add(plugin); + } + }, +); diff --git a/packages/core-api/src/plugin/types.ts b/packages/core-api/src/plugin/types.ts index 855264c5f4..dd19948a0a 100644 --- a/packages/core-api/src/plugin/types.ts +++ b/packages/core-api/src/plugin/types.ts @@ -66,10 +66,15 @@ export type PluginOutput = | RedirectRouteOutput | FeatureFlagOutput; +export type Extension = { + expose(plugin: BackstagePlugin): T; +}; + export type BackstagePlugin = { getId(): string; output(): PluginOutput[]; getApis(): Iterable; + provide(extension: Extension): T; }; export type PluginConfig = { diff --git a/packages/core-api/src/routing/collectors.test.tsx b/packages/core-api/src/routing/collectors.test.tsx new file mode 100644 index 0000000000..c2eefa7e82 --- /dev/null +++ b/packages/core-api/src/routing/collectors.test.tsx @@ -0,0 +1,193 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 React, { PropsWithChildren } from 'react'; +import { routeCollector, routeParentCollector } from './collectors'; + +import { + traverseElementTree, + childDiscoverer, + routeElementDiscoverer, +} from '../extensions/traversal'; +import { createRouteRef } from './RouteRef'; +import { createPlugin } from '../plugin'; +import { createRoutableExtension } from '../extensions'; +import { MemoryRouter, Routes, Route } from 'react-router-dom'; + +const mockConfig = () => ({ path: '/foo', title: 'Foo' }); +const MockComponent = ({ children }: PropsWithChildren<{}>) => <>{children}; + +const plugin = createPlugin({ id: 'my-plugin' }); + +const ref1 = createRouteRef(mockConfig()); +const ref2 = createRouteRef(mockConfig()); +const ref3 = createRouteRef(mockConfig()); +const ref4 = createRouteRef(mockConfig()); +const ref5 = createRouteRef(mockConfig()); + +const Extension1 = plugin.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref1 }), +); +const Extension2 = plugin.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref2 }), +); +const Extension3 = plugin.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref3 }), +); +const Extension4 = plugin.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref4 }), +); +const Extension5 = plugin.provide( + createRoutableExtension({ component: MockComponent, mountPoint: ref5 }), +); + +describe('discovery', () => { + it('should collect routes', () => { + const list = [ +
, +
, +
+ +
, + ]; + + const root = ( + + + +
+ +
+
+ Some text here shouldn't be a problem +
+ {null} +
+ +
+ + {false} + {list} + {true} + {0} +
+ +
+ } /> +
+ + + ); + + const { routes, routeParents } = traverseElementTree({ + root, + discoverers: [childDiscoverer, routeElementDiscoverer], + collectors: { + routes: routeCollector, + routeParents: routeParentCollector, + }, + }); + expect(routes).toEqual( + new Map([ + [ref1, '/foo'], + [ref2, '/bar/:id'], + [ref3, '/baz'], + [ref4, '/divsoup'], + [ref5, '/blop'], + ]), + ); + + expect(routeParents).toEqual( + new Map([ + [ref1, undefined], + [ref2, ref1], + [ref3, ref2], + [ref4, undefined], + [ref5, ref1], + ]), + ); + }); + + it('should handle all react router Route patterns', () => { + const root = ( + + + + + + + + } + /> + }> + } /> + + + + + ); + + const { routes, routeParents } = traverseElementTree({ + root, + discoverers: [childDiscoverer, routeElementDiscoverer], + collectors: { + routes: routeCollector, + routeParents: routeParentCollector, + }, + }); + expect(routes).toEqual( + new Map([ + [ref1, '/foo'], + [ref2, '/bar/:id'], + [ref3, '/baz'], + [ref4, '/divsoup'], + [ref5, '/blop'], + ]), + ); + expect(routeParents).toEqual( + new Map([ + [ref1, undefined], + [ref2, ref1], + [ref3, undefined], + [ref4, ref3], + [ref5, ref3], + ]), + ); + }); + + it('should not visit the same element twice', () => { + const element = ; + + expect(() => + traverseElementTree({ + root: ( + + {element} + {element} + + ), + discoverers: [childDiscoverer, routeElementDiscoverer], + collectors: { + routes: routeCollector, + routeParents: routeParentCollector, + }, + }), + ).toThrow(`Visited element Extension(MockComponent) twice`); + }); +}); diff --git a/packages/core-api/src/routing/collectors.tsx b/packages/core-api/src/routing/collectors.tsx new file mode 100644 index 0000000000..fab0cbe112 --- /dev/null +++ b/packages/core-api/src/routing/collectors.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { isValidElement, ReactNode } from 'react'; +import { RouteRef } from '../routing/types'; +import { getComponentData } from '../extensions'; +import { createCollector } from '../extensions/traversal'; + +export const routeCollector = createCollector( + new Map(), + (acc, node, parent) => { + if (parent.props.element === node) { + return; + } + + const path: string | undefined = node.props?.path; + const element: ReactNode = node.props?.element; + + const routeRef = getComponentData(node, 'core.mountPoint'); + if (routeRef) { + if (!path) { + throw new Error('Mounted routable extension must have a path'); + } + acc.set(routeRef, path); + } else if (isValidElement(element)) { + const elementRouteRef = getComponentData( + element, + 'core.mountPoint', + ); + if (elementRouteRef) { + if (!path) { + throw new Error('Route element must have a path'); + } + acc.set(elementRouteRef, path); + } + } + }, +); + +export const routeParentCollector = createCollector( + new Map(), + (acc, node, parent, parentRouteRef?: RouteRef) => { + if (parent.props.element === node) { + return parentRouteRef; + } + + const element: ReactNode = node.props?.element; + + let nextParent = parentRouteRef; + + const routeRef = getComponentData(node, 'core.mountPoint'); + if (routeRef) { + acc.set(routeRef, parentRouteRef); + nextParent = routeRef; + } else if (isValidElement(element)) { + const elementRouteRef = getComponentData( + element, + 'core.mountPoint', + ); + + if (elementRouteRef) { + acc.set(elementRouteRef, parentRouteRef); + nextParent = elementRouteRef; + } + } + + return nextParent; + }, +); diff --git a/packages/core/package.json b/packages/core/package.json index f667ffe7be..dd48a5d4ce 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.1", - "@backstage/core-api": "^0.2.1", + "@backstage/core-api": "^0.2.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -64,8 +64,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.3.1", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 4526bd4733..e13e31f406 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/create-app +## 0.2.3 + +### Patch Changes + +- 68fdc3a9f: Optimized the `yarn install` step in the backend `Dockerfile`. + + To apply these changes to an existing app, make the following changes to `packages/backend/Dockerfile`: + + Replace the `RUN yarn install ...` line with the following: + + ```bash + RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" + ``` + +- 4a655c89d: Removed `"resolutions"` entry for `esbuild` in the root `package.json` in order to use the version specified by `@backstage/cli`. + + To apply this change to an existing app, remove the following from your root `package.json`: + + ```json + "resolutions": { + "esbuild": "0.6.3" + }, + ``` + +- ea475893d: Add [API docs plugin](https://github.com/backstage/backstage/tree/master/plugins/api-docs) to new apps being created through the CLI. + ## 0.2.2 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index adb9d30465..384a6f11a2 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.2.2", + "version": "0.2.3", "private": false, "publishConfig": { "access": "public" @@ -37,30 +37,30 @@ "recursive-readdir": "^2.2.2" }, "devDependencies": { - "@backstage/backend-common": "^0.3.2", - "@backstage/catalog-model": "^0.3.1", - "@backstage/cli": "^0.3.2", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", + "@backstage/cli": "^0.4.0", "@backstage/config": "^0.1.1", "@backstage/core": "^0.3.2", - "@backstage/plugin-api-docs": "^0.3.0", - "@backstage/plugin-app-backend": "^0.3.1", - "@backstage/plugin-auth-backend": "^0.2.4", - "@backstage/plugin-catalog": "^0.2.4", - "@backstage/plugin-catalog-backend": "^0.2.3", - "@backstage/plugin-circleci": "^0.2.2", + "@backstage/plugin-api-docs": "^0.3.1", + "@backstage/plugin-app-backend": "^0.3.2", + "@backstage/plugin-auth-backend": "^0.2.5", + "@backstage/plugin-catalog": "^0.2.5", + "@backstage/plugin-catalog-backend": "^0.3.0", + "@backstage/plugin-circleci": "^0.2.3", "@backstage/plugin-explore": "^0.2.1", - "@backstage/plugin-github-actions": "^0.2.2", - "@backstage/plugin-lighthouse": "^0.2.3", - "@backstage/plugin-proxy-backend": "^0.2.1", - "@backstage/plugin-register-component": "^0.2.2", + "@backstage/plugin-github-actions": "^0.2.3", + "@backstage/plugin-lighthouse": "^0.2.4", + "@backstage/plugin-proxy-backend": "^0.2.2", + "@backstage/plugin-register-component": "^0.2.3", "@backstage/plugin-rollbar-backend": "^0.1.4", - "@backstage/plugin-scaffolder": "^0.3.1", - "@backstage/plugin-scaffolder-backend": "^0.3.2", + "@backstage/plugin-scaffolder": "^0.3.2", + "@backstage/plugin-scaffolder-backend": "^0.3.3", "@backstage/plugin-tech-radar": "^0.3.0", - "@backstage/plugin-techdocs": "^0.3.0", - "@backstage/plugin-techdocs-backend": "^0.3.0", + "@backstage/plugin-techdocs": "^0.3.1", + "@backstage/plugin-techdocs-backend": "^0.3.1", "@backstage/plugin-user-settings": "^0.2.2", - "@backstage/test-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@types/fs-extra": "^9.0.1", "@types/inquirer": "^7.3.1", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index fd8b11bada..9a1048aa3d 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/dev-utils +## 0.1.5 + +### Patch Changes + +- Updated dependencies [b4488ddb0] +- Updated dependencies [4a655c89d] +- Updated dependencies [8a16e8af8] +- Updated dependencies [00670a96e] + - @backstage/cli@0.4.0 + - @backstage/test-utils@0.1.4 + ## 0.1.4 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index bf2d230ba7..4ceb0cec5e 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.1.4", + "version": "0.1.5", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.3.0", + "@backstage/cli": "^0.4.0", "@backstage/core": "^0.3.1", - "@backstage/test-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", diff --git a/packages/integration/package.json b/packages/integration/package.json index fd4bd0bd3a..342476c38f 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -24,7 +24,7 @@ "git-url-parse": "^11.4.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@types/jest": "^26.0.7" }, "files": [ diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index b4bc2bbe76..dbeb79ed78 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/test-utils +## 0.1.4 + +### Patch Changes + +- Updated dependencies [b4488ddb0] +- Updated dependencies [4a655c89d] +- Updated dependencies [8a16e8af8] +- Updated dependencies [00670a96e] + - @backstage/cli@0.4.0 + - @backstage/core-api@0.2.4 + ## 0.1.3 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index a8f5cd1661..86997a6767 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.3", + "version": "0.1.4", "private": false, "publishConfig": { "access": "public", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/cli": "^0.3.0", - "@backstage/core-api": "^0.2.0", + "@backstage/cli": "^0.4.0", + "@backstage/core-api": "^0.2.4", "@backstage/test-utils-core": "^0.1.1", "@backstage/theme": "^0.2.0", "@material-ui/core": "^4.11.0", diff --git a/packages/theme/package.json b/packages/theme/package.json index 68c70c656a..18b75afff4 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -31,7 +31,7 @@ "@material-ui/core": "^4.11.0" }, "devDependencies": { - "@backstage/cli": "^0.3.0" + "@backstage/cli": "^0.4.0" }, "files": [ "dist" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index efa94fb46f..3b9c1951d4 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-api-docs +## 0.3.1 + +### Patch Changes + +- 7eb8bfe4a: Update swagger-ui-react to 3.37.2 +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.3.0 ### Minor Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index f9719226ee..659ded8061 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.3.0", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.1", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.4", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@kyma-project/asyncapi-react": "^0.14.2", "@material-icons/font": "^1.0.2", @@ -40,9 +40,9 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 1f2b3f008a..e92b886f2d 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-app-backend +## 0.3.2 + +### Patch Changes + +- Updated dependencies [4e7091759] +- Updated dependencies [b4488ddb0] +- Updated dependencies [612368274] + - @backstage/config-loader@0.4.0 + - @backstage/backend-common@0.3.3 + ## 0.3.1 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index ac350a14b4..6d2fdcb67c 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.3.1", + "version": "0.3.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.2", - "@backstage/config-loader": "^0.3.0", + "@backstage/backend-common": "^0.3.3", + "@backstage/config-loader": "^0.4.0", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@types/supertest": "^2.0.8", "msw": "^0.20.5", "supertest": "^4.0.2" diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 257c38a3e5..9bdd04783a 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-auth-backend +## 0.2.5 + +### Patch Changes + +- Updated dependencies [612368274] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/backend-common@0.3.3 + - @backstage/catalog-model@0.4.0 + - @backstage/catalog-client@0.3.2 + ## 0.2.4 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 814c8c5258..ddca6a24b4 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.2", - "@backstage/catalog-client": "^0.3.1", - "@backstage/catalog-model": "^0.3.1", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-client": "^0.3.2", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -36,7 +36,7 @@ "got": "^11.5.2", "helmet": "^4.0.0", "jose": "^1.27.1", - "jwt-decode": "2.2.0", + "jwt-decode": "^3.1.0", "knex": "^0.21.6", "moment": "^2.26.0", "morgan": "^1.10.0", @@ -55,11 +55,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", - "@types/jwt-decode": "2.2.1", + "@types/jwt-decode": "^3.1.0", "@types/nock": "^11.1.0", "@types/openid-client": "^3.7.0", "@types/passport": "^1.0.3", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 64f1a7ba87..935d52f116 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-catalog-backend +## 0.3.0 + +### Minor Changes + +- a9fd599f7: Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file: + + ```ts + export default async function createPlugin(env: PluginEnvironment) { + const builder = new CatalogBuilder(env); + const { + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + } = await builder.build(); + + return await createRouter({ + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + logger: env.logger, + }); + } + ``` + +### Patch Changes + +- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError +- 08835a61d: Add support for relative targets and implicit types in Location entities. +- e42402b47: Gracefully handle missing codeowners. + + The CodeOwnersProcessor now also takes a logger as a parameter. + +- Updated dependencies [612368274] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/backend-common@0.3.3 + - @backstage/catalog-model@0.4.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 9a4933ea6e..69f260d020 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.2.3", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ }, "dependencies": { "@azure/msal-node": "^1.0.0-alpha.8", - "@backstage/backend-common": "^0.3.2", - "@backstage/catalog-model": "^0.3.1", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@octokit/graphql": "^4.5.6", "@types/express": "^4.17.6", @@ -48,8 +48,8 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/test-utils": "^0.1.4", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/lodash": "^4.14.151", diff --git a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.test.ts new file mode 100644 index 0000000000..998dfb62a2 --- /dev/null +++ b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { LocationSpec } from '@backstage/catalog-model'; +import { toAbsoluteUrl } from './LocationEntityProcessor'; +import path from 'path'; + +describe('LocationEntityProcessor', () => { + describe('toAbsoluteUrl', () => { + it('handles files', () => { + const base: LocationSpec = { + type: 'file', + target: `some${path.sep}path${path.sep}catalog-info.yaml`, + }; + expect(toAbsoluteUrl(base, `.${path.sep}c`)).toBe( + `some${path.sep}path${path.sep}c`, + ); + expect(toAbsoluteUrl(base, `${path.sep}c`)).toBe(`${path.sep}c`); + }); + + it('handles urls', () => { + const base: LocationSpec = { + type: 'url', + target: 'http://a.com/b/catalog-info.yaml', + }; + expect(toAbsoluteUrl(base, './c/d')).toBe('http://a.com/b/c/d'); + expect(toAbsoluteUrl(base, 'c/d')).toBe('http://a.com/b/c/d'); + expect(toAbsoluteUrl(base, 'http://b.com/z')).toBe('http://b.com/z'); + }); + }); +}); diff --git a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts index 86e17f07f2..1c7e13f1c4 100644 --- a/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/LocationEntityProcessor.ts @@ -17,27 +17,52 @@ import { Entity, LocationEntity, LocationSpec } from '@backstage/catalog-model'; import * as result from './results'; import { CatalogProcessor, CatalogProcessorEmit } from './types'; +import path from 'path'; + +export function toAbsoluteUrl(base: LocationSpec, target: string): string { + try { + if (base.type === 'file') { + if (target.startsWith('.')) { + return path.join(path.dirname(base.target), target); + } + return target; + } + return new URL(target, base.target).toString(); + } catch (e) { + return target; + } +} export class LocationRefProcessor implements CatalogProcessor { async postProcessEntity( entity: Entity, - _location: LocationSpec, + location: LocationSpec, emit: CatalogProcessorEmit, ): Promise { if (entity.kind === 'Location') { - const location = entity as LocationEntity; - if (location.spec.target) { + const locationEntity = entity as LocationEntity; + + const type = locationEntity.spec.type || location.type; + if (type === 'file' && location.target.endsWith(path.sep)) { emit( - result.location( - { type: location.spec.type, target: location.spec.target }, - false, + result.inputError( + location, + `LocationRefProcessor cannot handle ${type} type location with target ${location.target} that ends with a path separator`, ), ); } - if (location.spec.targets) { - for (const target of location.spec.targets) { - emit(result.location({ type: location.spec.type, target }, false)); - } + + const targets = new Array(); + if (locationEntity.spec.target) { + targets.push(locationEntity.spec.target); + } + if (locationEntity.spec.targets) { + targets.push(...locationEntity.spec.targets); + } + + for (const maybeRelativeTarget of targets) { + const target = toAbsoluteUrl(location, maybeRelativeTarget); + emit(result.location({ type, target }, false)); } } diff --git a/plugins/catalog-backend/src/ingestion/processors/util/org.test.ts b/plugins/catalog-backend/src/ingestion/processors/util/org.test.ts index f7afd63101..7e5a2e9754 100644 --- a/plugins/catalog-backend/src/ingestion/processors/util/org.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/util/org.test.ts @@ -76,7 +76,7 @@ describe('buildMemberOf', () => { const u: UserEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'User', - metadata: { name }, + metadata: { name: 'n' }, spec: { profile: {}, memberOf: ['c'] }, }; diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index 56911b9c45..2bb4d9ac54 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-graphql +## 0.2.3 + +### Patch Changes + +- Updated dependencies [612368274] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/backend-common@0.3.3 + - @backstage/catalog-model@0.4.0 + ## 0.2.2 ### Patch Changes diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index a489d66e5b..9b12250ca0 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graphql", - "version": "0.2.2", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.1", - "@backstage/catalog-model": "^0.3.0", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@graphql-modules/core": "^0.7.17", "apollo-server": "^2.16.1", @@ -32,8 +32,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.3.1", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/test-utils": "^0.1.4", "@graphql-codegen/cli": "^1.17.7", "@graphql-codegen/typescript": "^1.17.7", "@graphql-codegen/typescript-resolvers": "^1.17.7", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md new file mode 100644 index 0000000000..bdbb8681de --- /dev/null +++ b/plugins/catalog-import/CHANGELOG.md @@ -0,0 +1,39 @@ +# @backstage/plugin-catalog-import + +## 0.3.0 + +### Minor Changes + +- a9fd599f7: Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file: + + ```ts + export default async function createPlugin(env: PluginEnvironment) { + const builder = new CatalogBuilder(env); + const { + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + } = await builder.build(); + + return await createRouter({ + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + logger: env.logger, + }); + } + ``` + +### Patch Changes + +- Updated dependencies [b4488ddb0] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [e42402b47] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/plugin-catalog-backend@0.3.0 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 11562b741e..74cb3635be 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.2.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.0", - "@backstage/plugin-catalog-backend": "^0.2.2", + "@backstage/plugin-catalog": "^0.2.5", + "@backstage/plugin-catalog-backend": "^0.3.0", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,9 +40,9 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index b1c3cefa51..7630bdb077 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog +## 0.2.5 + +### Patch Changes + +- ebf37bbae: Use the OWNED_BY relation and compare it to the users MEMBER_OF relation. The user entity is searched by name, based on the userId of the identity. +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [da2ad65cb] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-scaffolder@0.3.2 + - @backstage/plugin-techdocs@0.3.1 + - @backstage/catalog-client@0.3.2 + ## 0.2.4 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index f039749f64..6887f71b3b 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.1", - "@backstage/catalog-model": "^0.3.1", + "@backstage/catalog-client": "^0.3.2", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-scaffolder": "^0.3.1", - "@backstage/plugin-techdocs": "^0.3.0", + "@backstage/plugin-scaffolder": "^0.3.2", + "@backstage/plugin-techdocs": "^0.3.1", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,9 +43,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@microsoft/microsoft-graph-types": "^1.25.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/plugins/catalog/src/components/useOwnUser.ts b/plugins/catalog/src/components/useOwnUser.ts index a74b60481e..9aa4bda99c 100644 --- a/plugins/catalog/src/components/useOwnUser.ts +++ b/plugins/catalog/src/components/useOwnUser.ts @@ -21,20 +21,21 @@ import { identityApiRef, useApi } from '@backstage/core'; import { catalogApiRef } from '../plugin'; /** - * Get the group memberships of the logged-in user. + * Get the catalog User entity (if any) that matches the logged-in user. */ -export function useOwnUser(): AsyncState { +export function useOwnUser(): AsyncState { const catalogApi = useApi(catalogApiRef); const identityApi = useApi(identityApiRef); - // TODO: get the full entity (or at least the full entity name) from the identityApi + // TODO: get the full entity (or at least the full entity name) from the + // identityApi return useAsync( () => catalogApi.getEntityByName({ kind: 'User', namespace: 'default', name: identityApi.getUserId(), - }) as Promise, + }) as Promise, [catalogApi, identityApi], ); } diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index a81531e28d..7986cca2ec 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-circleci +## 0.2.3 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.2 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index fda28733af..c67696244c 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.2.2", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "postpack": "backstage-cli postpack" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,9 +40,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/circleci/src/api/CircleCIApi.ts b/plugins/circleci/src/api/CircleCIApi.ts new file mode 100644 index 0000000000..c71be6b781 --- /dev/null +++ b/plugins/circleci/src/api/CircleCIApi.ts @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { + CircleCIOptions, + getMe, + getBuildSummaries, + getFullBuild, + postBuildActions, + BuildAction, + BuildWithSteps, + BuildStepAction, + BuildSummary, + GitType, +} from 'circleci-api'; +import { createApiRef, DiscoveryApi } from '@backstage/core'; + +export { GitType }; +export type { BuildWithSteps, BuildStepAction, BuildSummary }; + +export const circleCIApiRef = createApiRef({ + id: 'plugin.circleci.service', + description: 'Used by the CircleCI plugin to make requests', +}); + +const DEFAULT_PROXY_PATH = '/circleci/api'; + +type Options = { + discoveryApi: DiscoveryApi; + /** + * Path to use for requests via the proxy, defaults to /circleci/api + */ + proxyPath?: string; +}; + +export class CircleCIApi { + private readonly discoveryApi: DiscoveryApi; + private readonly proxyPath: string; + + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + this.proxyPath = options.proxyPath ?? DEFAULT_PROXY_PATH; + } + + async retry(buildNumber: number, options: Partial) { + return postBuildActions('', buildNumber, BuildAction.RETRY, { + circleHost: await this.getApiUrl(), + ...options.vcs, + }); + } + + async getBuilds( + { limit = 10, offset = 0 }: { limit: number; offset: number }, + options: Partial, + ) { + return getBuildSummaries('', { + options: { + limit, + offset, + }, + vcs: {}, + circleHost: await this.getApiUrl(), + ...options, + }); + } + + async getUser(options: Partial) { + return getMe('', { circleHost: await this.getApiUrl(), ...options }); + } + + async getBuild(buildNumber: number, options: Partial) { + return getFullBuild('', buildNumber, { + circleHost: await this.getApiUrl(), + ...options.vcs, + }); + } + + private async getApiUrl() { + const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); + return proxyUrl + this.proxyPath; + } +} diff --git a/plugins/circleci/src/api/index.ts b/plugins/circleci/src/api/index.ts index c71be6b781..1853008099 100644 --- a/plugins/circleci/src/api/index.ts +++ b/plugins/circleci/src/api/index.ts @@ -14,82 +14,9 @@ * limitations under the License. */ -import { - CircleCIOptions, - getMe, - getBuildSummaries, - getFullBuild, - postBuildActions, - BuildAction, +export { CircleCIApi, circleCIApiRef, GitType } from './CircleCIApi'; +export type { BuildWithSteps, BuildStepAction, BuildSummary, - GitType, -} from 'circleci-api'; -import { createApiRef, DiscoveryApi } from '@backstage/core'; - -export { GitType }; -export type { BuildWithSteps, BuildStepAction, BuildSummary }; - -export const circleCIApiRef = createApiRef({ - id: 'plugin.circleci.service', - description: 'Used by the CircleCI plugin to make requests', -}); - -const DEFAULT_PROXY_PATH = '/circleci/api'; - -type Options = { - discoveryApi: DiscoveryApi; - /** - * Path to use for requests via the proxy, defaults to /circleci/api - */ - proxyPath?: string; -}; - -export class CircleCIApi { - private readonly discoveryApi: DiscoveryApi; - private readonly proxyPath: string; - - constructor(options: Options) { - this.discoveryApi = options.discoveryApi; - this.proxyPath = options.proxyPath ?? DEFAULT_PROXY_PATH; - } - - async retry(buildNumber: number, options: Partial) { - return postBuildActions('', buildNumber, BuildAction.RETRY, { - circleHost: await this.getApiUrl(), - ...options.vcs, - }); - } - - async getBuilds( - { limit = 10, offset = 0 }: { limit: number; offset: number }, - options: Partial, - ) { - return getBuildSummaries('', { - options: { - limit, - offset, - }, - vcs: {}, - circleHost: await this.getApiUrl(), - ...options, - }); - } - - async getUser(options: Partial) { - return getMe('', { circleHost: await this.getApiUrl(), ...options }); - } - - async getBuild(buildNumber: number, options: Partial) { - return getFullBuild('', buildNumber, { - circleHost: await this.getApiUrl(), - ...options.vcs, - }); - } - - private async getApiUrl() { - const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); - return proxyUrl + this.proxyPath; - } -} +} from './CircleCIApi'; diff --git a/plugins/circleci/src/state/useBuildWithSteps.ts b/plugins/circleci/src/state/useBuildWithSteps.ts index c169b8fe63..dff5927044 100644 --- a/plugins/circleci/src/state/useBuildWithSteps.ts +++ b/plugins/circleci/src/state/useBuildWithSteps.ts @@ -16,7 +16,7 @@ import { errorApiRef, useApi } from '@backstage/core'; import { useCallback, useMemo } from 'react'; import { useAsyncRetry } from 'react-use'; -import { circleCIApiRef } from '../api/index'; +import { circleCIApiRef } from '../api'; import { useAsyncPolling } from './useAsyncPolling'; import { useProjectSlugFromEntity, mapVcsType } from './useBuilds'; diff --git a/plugins/circleci/src/state/useBuilds.ts b/plugins/circleci/src/state/useBuilds.ts index 35bd2a2986..c172a88985 100644 --- a/plugins/circleci/src/state/useBuilds.ts +++ b/plugins/circleci/src/state/useBuilds.ts @@ -18,7 +18,7 @@ import { errorApiRef, useApi } from '@backstage/core'; import { BuildSummary, GitType } from 'circleci-api'; import { useCallback, useEffect, useState } from 'react'; import { useAsyncRetry } from 'react-use'; -import { circleCIApiRef } from '../api/index'; +import { circleCIApiRef } from '../api'; import type { CITableBuildInfo } from '../components/BuildsPage/lib/CITable'; import { useEntity } from '@backstage/plugin-catalog'; import { CIRCLECI_ANNOTATION } from '../constants'; diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index 4cb2d01377..1f707ac777 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-cloudbuild +## 0.2.3 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.2 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index c36669ccb9..3e514a5eb8 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cloudbuild", - "version": "0.2.2", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -39,9 +39,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index b7bdfa9d50..b545b2ec37 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-cost-insights +## 0.4.2 + +### Patch Changes + +- fe7257ff0: enable SKU breakdown for unlabeled entities +- a2cfa311a: Add breakdown view to the Cost Overview panel +- 69f38457f: Add support for non-SKU breakdowns for entities in the product panels. +- bec334b33: disable support button +- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError +- 00670a96e: sort product panels and navigation menu by greatest cost + update tsconfig.json to use ES2020 api + - @backstage/test-utils@0.1.4 + ## 0.4.1 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index bc649e50ab..5a3ee38afe 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cost-insights", - "version": "0.4.1", + "version": "0.4.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,7 +23,7 @@ "dependencies": { "@backstage/config": "^0.1.1", "@backstage/core": "^0.3.2", - "@backstage/test-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,9 +46,9 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx index e361a14322..9a38bf2955 100644 --- a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx +++ b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsPage.tsx @@ -32,7 +32,8 @@ import { import { CostInsightsNavigation } from '../CostInsightsNavigation'; import { CostOverviewCard } from '../CostOverviewCard'; import { ProductInsights } from '../ProductInsights'; -import { CostInsightsSupportButton } from '../CostInsightsSupportButton'; +/* https://github.com/backstage/backstage/issues/2574 */ +// import { CostInsightsSupportButton } from '../CostInsightsSupportButton'; import { useConfig, useCurrency, @@ -164,7 +165,7 @@ export const CostInsightsPage = () => { - + {/* */} @@ -239,7 +240,7 @@ export const CostInsightsPage = () => { mb={2} > - + {/* */} diff --git a/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx b/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx index 4cccd638cb..d617c0df35 100644 --- a/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx +++ b/plugins/cost-insights/src/components/ProductInsightsCard/ProductEntityDialog.tsx @@ -29,7 +29,7 @@ function createRenderer(col: keyof RowData, classes: Record) { const row = rowData as RowData; const rowStyles = classnames(classes.row, { [classes.rowTotal]: row.id === 'total', - [classes.colFirst]: col === 'sku', + [classes.colFirst]: col === 'label', [classes.colLast]: col === 'ratio', }); @@ -50,7 +50,7 @@ function createRenderer(col: keyof RowData, classes: Record) { /> ); default: - return {row.sku}; + return {row.label}; } }; } @@ -63,7 +63,7 @@ function createSorter(field?: keyof Omit) { const b = data2 as RowData; if (a.id === 'total') return 1; if (b.id === 'total') return 1; - if (field === 'sku') return a.sku.localeCompare(b.sku); + if (field === 'label') return a.label.localeCompare(b.label); return field ? a[field] - b[field] @@ -80,7 +80,7 @@ const defaultEntity: Entity = { type RowData = { id: string; - sku: string; + label: string; previous: number; current: number; ratio: number; @@ -93,6 +93,7 @@ type ProductEntityDialogOptions = Partial< type ProductEntityDialogProps = { open: boolean; entity?: Entity; + entitiesLabel: string; options?: ProductEntityDialogOptions; onClose: () => void; }; @@ -100,6 +101,7 @@ type ProductEntityDialogProps = { export const ProductEntityDialog = ({ open, entity = defaultEntity, + entitiesLabel, options = {}, onClose, }: ProductEntityDialogProps) => { @@ -118,10 +120,12 @@ export const ProductEntityDialog = ({ const columns: TableColumn[] = [ { - field: 'sku', - title: SKU, - render: createRenderer('sku', classes), - customSort: createSorter('sku'), + field: 'label', + title: ( + {entitiesLabel} + ), + render: createRenderer('label', classes), + customSort: createSorter('label'), width: '33.33%', }, { @@ -154,14 +158,14 @@ export const ProductEntityDialog = ({ const rowData: RowData[] = entity.entities .map(e => ({ id: e.id || 'Unknown', - sku: e.id || 'Unknown', + label: e.id || 'Unknown', previous: e.aggregation[0], current: e.aggregation[1], ratio: e.change.ratio, })) .concat({ id: 'total', - sku: 'Total', + label: 'Total', previous: entity.aggregation[0], current: entity.aggregation[1], ratio: entity.change.ratio, @@ -176,7 +180,7 @@ export const ProductEntityDialog = ({ { const classes = useStyles(); const layoutClasses = useLayoutStyles(); - const [isOpen, setOpen] = useState(false); - const [isClickable, setClickable] = useState(true); - const [selectLabel, setSelected] = useState>(null); - const [activeLabel, setActive] = useState>(null); + const [activeLabel, setActive] = useState>(); + const [selectLabel, setSelected] = useState>(); + const isSelected = useMemo(() => !isUndefined(selectLabel), [selectLabel]); + const isClickable = useMemo(() => { + const breakdownEntities = + entity.entities.find(e => e.id === activeLabel)?.entities ?? []; + return breakdownEntities.length > 0; + }, [entity, activeLabel]); const legendTitle = `Cost ${entity.change.ratio <= 0 ? 'Savings' : 'Growth'}`; const costStart = entity.aggregation[0]; @@ -76,52 +81,25 @@ export const ProductInsightsChart = ({ currentName: formatPeriod(duration, billingDate, true), }; - useEffect(() => { - function toggleModal() { - if (selectLabel) { - setOpen(true); - } else { - setOpen(false); - } - } - - toggleModal(); - }, [selectLabel]); - - useEffect(() => { - // disable click if an entity is unlabeled or if it does not have any skus - function toggleClickableEntity() { - if (activeLabel) { - const hasSkus = entity.entities.find(e => e.id === activeLabel) - ?.entities.length; - if (hasSkus) { - setClickable(true); - } else { - setClickable(false); - } - } else { - setClickable(false); - } - } - - toggleClickableEntity(); - }, [activeLabel, entity]); - const onMouseMove: RechartsFunction = ( data: Record<'activeLabel', string | undefined>, ) => { - if (isActiveLabel(data)) { + if (isLabeled(data)) { setActive(data.activeLabel!); - } else { + } else if (isUnlabeled(data)) { setActive(null); + } else { + setActive(undefined); } }; const onClick: RechartsFunction = (data: Record<'activeLabel', string>) => { - if (isActiveLabel(data)) { + if (isLabeled(data)) { setSelected(data.activeLabel); - } else { + } else if (isUnlabeled(data)) { setSelected(null); + } else { + setSelected(undefined); } }; @@ -144,10 +122,13 @@ export const ProductInsightsChart = ({ const activeEntity = findAlways(entity.entities, e => e.id === id); const ratio = activeEntity.change.ratio; - const skus = activeEntity.entities; - const subtitle = `${skus.length} ${pluralOf(skus.length, 'SKU')}`; + const breakdownEntities = activeEntity.entities; + const subtitle = `${breakdownEntities.length} ${pluralOf( + breakdownEntities.length, + entity.entitiesLabel || 'SKU', + )}`; - if (skus.length) { + if (breakdownEntities.length) { return ( - {selectLabel && entity.entities.length && ( + {isSelected && entity.entities.length && ( setSelected(null)} + open={isSelected} + onClose={() => setSelected(undefined)} entity={entity.entities.find(e => e.id === selectLabel)} options={options} + entitiesLabel={entity.entitiesLabel || 'SKU'} /> )} diff --git a/plugins/cost-insights/src/types/Entity.ts b/plugins/cost-insights/src/types/Entity.ts index 83347f05c3..52f6271bb4 100644 --- a/plugins/cost-insights/src/types/Entity.ts +++ b/plugins/cost-insights/src/types/Entity.ts @@ -22,6 +22,7 @@ export interface Entity { aggregation: [number, number]; entities: Entity[]; change: ChangeStatistic; + entitiesLabel?: string; } /* diff --git a/plugins/cost-insights/src/utils/assert.ts b/plugins/cost-insights/src/utils/assert.ts index 8f7caef0fa..19ddabe574 100644 --- a/plugins/cost-insights/src/utils/assert.ts +++ b/plugins/cost-insights/src/utils/assert.ts @@ -17,7 +17,15 @@ export function notEmpty( value: TValue | null | undefined, ): value is TValue { - return value !== null && value !== undefined; + return !isNull(value) && !isUndefined(value); +} + +export function isUndefined(value: any): boolean { + return value === undefined; +} + +export function isNull(value: any): boolean { + return value === null; } // Utility for exhaustiveness checking in switch statements diff --git a/plugins/cost-insights/src/utils/graphs.ts b/plugins/cost-insights/src/utils/graphs.ts index ac06e20587..bc82b63f89 100644 --- a/plugins/cost-insights/src/utils/graphs.ts +++ b/plugins/cost-insights/src/utils/graphs.ts @@ -72,8 +72,12 @@ export const isInvalid = ({ label, payload }: TooltipProps) => { return label === undefined || !payload || !payload.length; }; -export const isActiveLabel = ( +export const isLabeled = (data?: Record<'activeLabel', string | undefined>) => { + return data?.activeLabel && data?.activeLabel !== ''; +}; + +export const isUnlabeled = ( data?: Record<'activeLabel', string | undefined>, ) => { - return data?.activeLabel && data.activeLabel !== ''; + return data?.activeLabel === ''; }; diff --git a/plugins/cost-insights/src/utils/mockData.ts b/plugins/cost-insights/src/utils/mockData.ts index f188bd7a57..76bdf5f08f 100644 --- a/plugins/cost-insights/src/utils/mockData.ts +++ b/plugins/cost-insights/src/utils/mockData.ts @@ -563,7 +563,26 @@ export const SampleCloudDataflowInsights: Entity = { ratio: 0.2, amount: 2_000, }, - entities: [], + entities: [ + { + id: 'Sample SKU A', + aggregation: [3_000, 4_000], + change: { + ratio: 0.333333, + amount: 1_000, + }, + entities: [], + }, + { + id: 'Sample SKU B', + aggregation: [7_000, 8_000], + change: { + ratio: 0.14285714, + amount: 1_000, + }, + entities: [], + }, + ], }, { id: 'entity-a', @@ -793,7 +812,7 @@ export const SampleComputeEngineInsights: Entity = { id: 'Sample SKU A', aggregation: [1_000, 2_000], change: { - ratio: 0.5, + ratio: 1, amount: 1_000, }, entities: [], @@ -830,6 +849,92 @@ export const SampleComputeEngineInsights: Entity = { ], }; +export const SampleEventsInsights: Entity = { + id: 'events', + aggregation: [20_000, 10_000], + change: { + ratio: -0.5, + amount: -10_000, + }, + entitiesLabel: 'Product', + entities: [ + { + id: 'entity-a', + aggregation: [15_000, 7_000], + change: { + ratio: -0.53333333333, + amount: -8_000, + }, + entities: [ + { + id: 'Sample Product A', + aggregation: [5_000, 2_000], + change: { + ratio: -0.6, + amount: -3_000, + }, + entities: [], + }, + { + id: 'Sample Product B', + aggregation: [7_000, 2_500], + change: { + ratio: -0.64285714285, + amount: -4_500, + }, + entities: [], + }, + { + id: 'Sample Product C', + aggregation: [3_000, 2_500], + change: { + ratio: -0.16666666666, + amount: -500, + }, + entities: [], + }, + ], + }, + { + id: 'entity-b', + aggregation: [5_000, 3_000], + change: { + ratio: -0.4, + amount: -2_000, + }, + entities: [ + { + id: 'Sample Product A', + aggregation: [2_000, 1_000], + change: { + ratio: -0.5, + amount: -1_000, + }, + entities: [], + }, + { + id: 'Sample Product B', + aggregation: [1_000, 1_500], + change: { + ratio: 0.5, + amount: 500, + }, + entities: [], + }, + { + id: 'Sample Product C', + aggregation: [2_000, 500], + change: { + ratio: -0.75, + amount: -1_500, + }, + entities: [], + }, + ], + }, + ], +}; + export function entityOf(product: string): Entity { switch (product) { case 'computeEngine': @@ -840,6 +945,8 @@ export function entityOf(product: string): Entity { return SampleCloudStorageInsights; case 'bigQuery': return SampleBigQueryInsights; + case 'events': + return SampleEventsInsights; default: throw new Error( `Cannot get insights for ${product}. Make sure product matches product property in app-info.yaml`, diff --git a/plugins/cost-insights/src/utils/styles.ts b/plugins/cost-insights/src/utils/styles.ts index 5a42604311..c04ceba8e3 100644 --- a/plugins/cost-insights/src/utils/styles.ts +++ b/plugins/cost-insights/src/utils/styles.ts @@ -222,16 +222,13 @@ export const useBarChartLabelStyles = makeStyles(theme => }), ); -export const useCostInsightsStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - h6Subtle: { - ...theme.typography.h6, - fontWeight: 'normal', - color: theme.palette.textSubtle, - }, - }), -); +export const useCostInsightsStyles = makeStyles(theme => ({ + h6Subtle: { + ...theme.typography.h6, + fontWeight: 'normal', + color: theme.palette.textSubtle, + }, +})); export const useCostInsightsTabsStyles = makeStyles( (theme: BackstageTheme) => ({ @@ -310,40 +307,37 @@ export const useCostGrowthStyles = makeStyles( }), ); -export const useCostGrowthLegendStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - h5: { - ...theme.typography.h5, - fontWeight: 500, - padding: 0, - }, - marker: { - display: 'inherit', - marginRight: theme.spacing(1), - }, - helpIcon: { - display: 'inherit', - }, - title: { - ...theme.typography.overline, - fontWeight: 500, - lineHeight: 0, - marginRight: theme.spacing(1), - color: theme.palette.textSubtle, - }, - tooltip: { - display: 'block', - padding: theme.spacing(1), - backgroundColor: theme.palette.navigation.background, - }, - tooltipText: { - color: theme.palette.background.default, - fontSize: theme.typography.fontSize, - lineHeight: 1.5, - }, - }), -); +export const useCostGrowthLegendStyles = makeStyles(theme => ({ + h5: { + ...theme.typography.h5, + fontWeight: 500, + padding: 0, + }, + marker: { + display: 'inherit', + marginRight: theme.spacing(1), + }, + helpIcon: { + display: 'inherit', + }, + title: { + ...theme.typography.overline, + fontWeight: 500, + lineHeight: 0, + marginRight: theme.spacing(1), + color: theme.palette.textSubtle, + }, + tooltip: { + display: 'block', + padding: theme.spacing(1), + backgroundColor: theme.palette.navigation.background, + }, + tooltipText: { + color: theme.palette.background.default, + fontSize: theme.typography.fontSize, + lineHeight: 1.5, + }, +})); export const useBarChartStepperStyles = makeStyles( (theme: BackstageTheme) => diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 3c3619f2ce..45cbb310f8 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -33,9 +33,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index f07cd1b0e6..faa79ed647 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -31,9 +31,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 96f3876802..e43704f83a 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-github-actions +## 0.2.3 + +### Patch Changes + +- Updated dependencies [b4488ddb0] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/core-api@0.2.4 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.2 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 83ba62be95..5822a0e608 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.2.2", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/core-api": "^0.2.1", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/core-api": "^0.2.4", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,9 +40,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 78af131bac..3adf7291db 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index d3dc673c01..38c1fb1894 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -43,13 +43,13 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", - "@types/codemirror": "^0.0.97", + "@types/codemirror": "^0.0.100", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "cross-fetch": "^3.0.6", diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index 53af8d9154..f9121ebd9a 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -19,9 +19,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.0", + "@backstage/backend-common": "^0.3.3", "@backstage/config": "^0.1.1", - "@backstage/plugin-catalog-graphql": "^0.2.1", + "@backstage/plugin-catalog-graphql": "^0.2.3", "@graphql-modules/core": "^0.7.17", "@types/express": "^4.17.6", "apollo-server": "^2.16.1", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.3.0", + "@backstage/cli": "^0.4.0", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index aeeb069f19..ca4e0284b5 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-jenkins +## 0.3.2 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.3.1 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index e47ec9edda..64bb7dfb20 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.3.1", + "version": "0.3.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/jenkins/src/api/JenkinsApi.ts b/plugins/jenkins/src/api/JenkinsApi.ts new file mode 100644 index 0000000000..0fa48a1d17 --- /dev/null +++ b/plugins/jenkins/src/api/JenkinsApi.ts @@ -0,0 +1,233 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { createApiRef, DiscoveryApi } from '@backstage/core'; +import { CITableBuildInfo } from '../components/BuildsPage/lib/CITable'; + +const jenkins = require('jenkins'); + +export const jenkinsApiRef = createApiRef({ + id: 'plugin.jenkins.service', + description: 'Used by the Jenkins plugin to make requests', +}); + +const DEFAULT_PROXY_PATH = '/jenkins/api'; + +type Options = { + discoveryApi: DiscoveryApi; + /** + * Path to use for requests via the proxy, defaults to /jenkins/api + */ + proxyPath?: string; +}; + +export class JenkinsApi { + private readonly discoveryApi: DiscoveryApi; + private readonly proxyPath: string; + + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + this.proxyPath = options.proxyPath ?? DEFAULT_PROXY_PATH; + } + + private async getClient() { + const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); + return jenkins({ baseUrl: proxyUrl + this.proxyPath, promisify: true }); + } + + async retry(buildName: string) { + const client = await this.getClient(); + // looks like the current SDK only supports triggering a new build + // can't see any support for replay (re-running the specific build with the same SCM info) + return await client.job.build(buildName); + } + + async getLastBuild(jobName: string) { + const client = await this.getClient(); + const job = await client.job.get(jobName); + + const lastBuild = await client.build.get(jobName, job.lastBuild.number); + return lastBuild; + } + + extractScmDetailsFromJob(jobDetails: any): any { + const scmInfo = jobDetails.actions + .filter( + (action: any) => + action._class === 'jenkins.scm.api.metadata.ObjectMetadataAction', + ) + .map((action: any) => { + return { + url: action?.objectUrl, + // https://javadoc.jenkins.io/plugin/scm-api/jenkins/scm/api/metadata/ObjectMetadataAction.html + // branch name for regular builds, pull request title on pull requests + displayName: action?.objectDisplayName, + }; + }) + .pop(); + + const author = jobDetails.actions + .filter( + (action: any) => + action._class === + 'jenkins.scm.api.metadata.ContributorMetadataAction', + ) + .map((action: any) => { + return action.contributorDisplayName; + }) + .pop(); + + if (author) { + scmInfo.author = author; + } + + return scmInfo; + } + + async getJob(jobName: string) { + const client = await this.getClient(); + return client.job.get({ + name: jobName, + depth: 1, + }); + } + + async getFolder(folderName: string) { + const client = await this.getClient(); + const folder = await client.job.get(folderName); + const results = []; + for (const jobSummary of folder.jobs) { + const jobDetails = await client.job.get({ + name: `${folderName}/${jobSummary.name}`, + depth: 1, + }); + + const jobScmInfo = this.extractScmDetailsFromJob(jobDetails); + if (jobDetails.jobs) { + // skipping folders inside folders for now + } else { + for (const buildDetails of jobDetails.builds) { + const build = await client.build.get({ + name: `${folderName}/${jobSummary.name}`, + number: buildDetails.number, + depth: 1, + }); + + const ciTable = this.mapJenkinsBuildToCITable(build, jobScmInfo); + results.push(ciTable); + } + } + } + return results; + } + + private getTestReport( + jenkinsResult: any, + ): { + total: number; + passed: number; + skipped: number; + failed: number; + testUrl: string; + } { + return jenkinsResult.actions + .filter( + (action: any) => + action._class === 'hudson.tasks.junit.TestResultAction', + ) + .map((action: any) => { + return { + total: action.totalCount, + passed: action.totalCount - action.failCount - action.skipCount, + skipped: action.skipCount, + failed: action.failCount, + testUrl: `${jenkinsResult.url}${action.urlName}/`, + }; + }) + .pop(); + } + + mapJenkinsBuildToCITable( + jenkinsResult: any, + jobScmInfo?: any, + ): CITableBuildInfo { + const source = + jenkinsResult.actions + .filter( + (action: any) => + action._class === 'hudson.plugins.git.util.BuildData', + ) + .map((action: any) => { + const [first]: any = Object.values(action.buildsByBranchName); + const branch = first.revision.branch[0]; + return { + branchName: branch.name, + commit: { + hash: branch.SHA1.substring(0, 8), + }, + }; + }) + .pop() || {}; + + if (jobScmInfo) { + source.url = jobScmInfo?.url; + source.displayName = jobScmInfo?.displayName; + source.author = jobScmInfo?.author; + } + + const path = new URL(jenkinsResult.url).pathname; + + return { + id: path, + buildNumber: jenkinsResult.number, + buildUrl: jenkinsResult.url, + buildName: jenkinsResult.fullDisplayName, + status: jenkinsResult.building ? 'running' : jenkinsResult.result, + onRestartClick: () => { + // TODO: this won't handle non root context path, need a better way to get the job name + const { jobName } = this.extractJobDetailsFromBuildName(path); + return this.retry(jobName); + }, + source: source, + tests: this.getTestReport(jenkinsResult), + }; + } + + async getBuild(buildName: string) { + const client = await this.getClient(); + const { jobName, buildNumber } = this.extractJobDetailsFromBuildName( + buildName, + ); + const buildResult = await client.build.get(jobName, buildNumber); + return buildResult; + } + + extractJobDetailsFromBuildName(buildName: string) { + const trimmedBuild = buildName.replace(/\/job/g, '').replace(/\/$/, ''); + + const split = trimmedBuild.split('/'); + const buildNumber = parseInt(split[split.length - 1], 10); + const jobName = trimmedBuild.slice( + 0, + trimmedBuild.length - buildNumber.toString(10).length - 1, + ); + + return { + jobName, + buildNumber, + }; + } +} diff --git a/plugins/jenkins/src/api/index.ts b/plugins/jenkins/src/api/index.ts index 0fa48a1d17..41e0985be2 100644 --- a/plugins/jenkins/src/api/index.ts +++ b/plugins/jenkins/src/api/index.ts @@ -14,220 +14,4 @@ * limitations under the License. */ -import { createApiRef, DiscoveryApi } from '@backstage/core'; -import { CITableBuildInfo } from '../components/BuildsPage/lib/CITable'; - -const jenkins = require('jenkins'); - -export const jenkinsApiRef = createApiRef({ - id: 'plugin.jenkins.service', - description: 'Used by the Jenkins plugin to make requests', -}); - -const DEFAULT_PROXY_PATH = '/jenkins/api'; - -type Options = { - discoveryApi: DiscoveryApi; - /** - * Path to use for requests via the proxy, defaults to /jenkins/api - */ - proxyPath?: string; -}; - -export class JenkinsApi { - private readonly discoveryApi: DiscoveryApi; - private readonly proxyPath: string; - - constructor(options: Options) { - this.discoveryApi = options.discoveryApi; - this.proxyPath = options.proxyPath ?? DEFAULT_PROXY_PATH; - } - - private async getClient() { - const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); - return jenkins({ baseUrl: proxyUrl + this.proxyPath, promisify: true }); - } - - async retry(buildName: string) { - const client = await this.getClient(); - // looks like the current SDK only supports triggering a new build - // can't see any support for replay (re-running the specific build with the same SCM info) - return await client.job.build(buildName); - } - - async getLastBuild(jobName: string) { - const client = await this.getClient(); - const job = await client.job.get(jobName); - - const lastBuild = await client.build.get(jobName, job.lastBuild.number); - return lastBuild; - } - - extractScmDetailsFromJob(jobDetails: any): any { - const scmInfo = jobDetails.actions - .filter( - (action: any) => - action._class === 'jenkins.scm.api.metadata.ObjectMetadataAction', - ) - .map((action: any) => { - return { - url: action?.objectUrl, - // https://javadoc.jenkins.io/plugin/scm-api/jenkins/scm/api/metadata/ObjectMetadataAction.html - // branch name for regular builds, pull request title on pull requests - displayName: action?.objectDisplayName, - }; - }) - .pop(); - - const author = jobDetails.actions - .filter( - (action: any) => - action._class === - 'jenkins.scm.api.metadata.ContributorMetadataAction', - ) - .map((action: any) => { - return action.contributorDisplayName; - }) - .pop(); - - if (author) { - scmInfo.author = author; - } - - return scmInfo; - } - - async getJob(jobName: string) { - const client = await this.getClient(); - return client.job.get({ - name: jobName, - depth: 1, - }); - } - - async getFolder(folderName: string) { - const client = await this.getClient(); - const folder = await client.job.get(folderName); - const results = []; - for (const jobSummary of folder.jobs) { - const jobDetails = await client.job.get({ - name: `${folderName}/${jobSummary.name}`, - depth: 1, - }); - - const jobScmInfo = this.extractScmDetailsFromJob(jobDetails); - if (jobDetails.jobs) { - // skipping folders inside folders for now - } else { - for (const buildDetails of jobDetails.builds) { - const build = await client.build.get({ - name: `${folderName}/${jobSummary.name}`, - number: buildDetails.number, - depth: 1, - }); - - const ciTable = this.mapJenkinsBuildToCITable(build, jobScmInfo); - results.push(ciTable); - } - } - } - return results; - } - - private getTestReport( - jenkinsResult: any, - ): { - total: number; - passed: number; - skipped: number; - failed: number; - testUrl: string; - } { - return jenkinsResult.actions - .filter( - (action: any) => - action._class === 'hudson.tasks.junit.TestResultAction', - ) - .map((action: any) => { - return { - total: action.totalCount, - passed: action.totalCount - action.failCount - action.skipCount, - skipped: action.skipCount, - failed: action.failCount, - testUrl: `${jenkinsResult.url}${action.urlName}/`, - }; - }) - .pop(); - } - - mapJenkinsBuildToCITable( - jenkinsResult: any, - jobScmInfo?: any, - ): CITableBuildInfo { - const source = - jenkinsResult.actions - .filter( - (action: any) => - action._class === 'hudson.plugins.git.util.BuildData', - ) - .map((action: any) => { - const [first]: any = Object.values(action.buildsByBranchName); - const branch = first.revision.branch[0]; - return { - branchName: branch.name, - commit: { - hash: branch.SHA1.substring(0, 8), - }, - }; - }) - .pop() || {}; - - if (jobScmInfo) { - source.url = jobScmInfo?.url; - source.displayName = jobScmInfo?.displayName; - source.author = jobScmInfo?.author; - } - - const path = new URL(jenkinsResult.url).pathname; - - return { - id: path, - buildNumber: jenkinsResult.number, - buildUrl: jenkinsResult.url, - buildName: jenkinsResult.fullDisplayName, - status: jenkinsResult.building ? 'running' : jenkinsResult.result, - onRestartClick: () => { - // TODO: this won't handle non root context path, need a better way to get the job name - const { jobName } = this.extractJobDetailsFromBuildName(path); - return this.retry(jobName); - }, - source: source, - tests: this.getTestReport(jenkinsResult), - }; - } - - async getBuild(buildName: string) { - const client = await this.getClient(); - const { jobName, buildNumber } = this.extractJobDetailsFromBuildName( - buildName, - ); - const buildResult = await client.build.get(jobName, buildNumber); - return buildResult; - } - - extractJobDetailsFromBuildName(buildName: string) { - const trimmedBuild = buildName.replace(/\/job/g, '').replace(/\/$/, ''); - - const split = trimmedBuild.split('/'); - const buildNumber = parseInt(split[split.length - 1], 10); - const jobName = trimmedBuild.slice( - 0, - trimmedBuild.length - buildNumber.toString(10).length - 1, - ); - - return { - jobName, - buildNumber, - }; - } -} +export { JenkinsApi, jenkinsApiRef } from './JenkinsApi'; diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 2877264e54..dc875669ef 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-backend +## 0.2.1 + +### Patch Changes + +- bcc211a08: k8s-plugin: refactor approach to use annotation based label-selector +- Updated dependencies [612368274] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/backend-common@0.3.3 + - @backstage/catalog-model@0.4.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/kubernetes-backend/examples/dice-roller/README.md b/plugins/kubernetes-backend/examples/dice-roller/README.md index bf3e7d261a..5f541bb61d 100644 --- a/plugins/kubernetes-backend/examples/dice-roller/README.md +++ b/plugins/kubernetes-backend/examples/dice-roller/README.md @@ -42,7 +42,7 @@ kubernetes: Mac copy to clipboard: ``` -kubectl get secret $(kubectl get sa dice-roller -o=json | jq -r .secrets[0].name) -o=json | jq -r '.data["token"]' | base64 --decode | pbcopy +kubectl get secret $(kubectl get sa dice-roller -o=json | jq -r '.secrets[0].name') -o=json | jq -r '.data["token"]' | base64 --decode | pbcopy ``` Paste into `app-config.local.yaml` `kubernetes.clusters[0].serviceAccountToken` diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 8fe2f4c58f..9d1ff2eed4 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.1", - "@backstage/catalog-model": "^0.3.0", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@kubernetes/client-node": "^0.12.1", "@types/express": "^4.17.6", @@ -39,7 +39,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.3.1", + "@backstage/cli": "^0.4.0", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index 32a09cb496..5677fa2d0d 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Config, ConfigReader } from '../../../../packages/config/src'; +import { Config, ConfigReader } from '@backstage/config'; import { getCombinedClusterDetails } from './index'; describe('getCombinedClusterDetails', () => { diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.ts b/plugins/kubernetes-backend/src/cluster-locator/index.ts index ea418768b4..9e6558510c 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -15,7 +15,7 @@ */ import { ClusterDetails, ClusterLocatorMethod } from '..'; -import { Config } from '../../../../packages/config/src'; +import { Config } from '@backstage/config'; import { ConfigClusterLocator } from './ConfigClusterLocator'; export { ConfigClusterLocator } from './ConfigClusterLocator'; diff --git a/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts b/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts index adebebe1c5..5c6513e57c 100644 --- a/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts +++ b/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts @@ -16,7 +16,6 @@ import { handleGetKubernetesObjectsForService } from './getKubernetesObjectsForServiceHandler'; import { getVoidLogger } from '@backstage/backend-common'; -import { ComponentEntityV1alpha1 } from '@backstage/catalog-model'; import { ObjectFetchParams } from '..'; const TEST_SERVICE_ID = 'my-service'; @@ -25,26 +24,6 @@ const fetchObjectsForService = jest.fn(); const getClustersByServiceId = jest.fn(); -const goodEntity: ComponentEntityV1alpha1 = { - apiVersion: 'backstage.io/v1beta1', - kind: 'Component', - metadata: { - name: 'test-component', - }, - spec: { - type: 'service', - lifecycle: 'production', - owner: 'joe', - kubernetes: { - selector: { - matchLabels: { - 'backstage.io/test-label': 'test-component', - }, - }, - }, - }, -}; - const mockFetch = (mock: jest.Mock) => { mock.mockImplementation((params: ObjectFetchParams) => Promise.resolve({ @@ -111,7 +90,24 @@ describe('handleGetKubernetesObjectsForService', () => { getClustersByServiceId, }, getVoidLogger(), - { entity: goodEntity }, + { + entity: { + apiVersion: 'backstage.io/v1beta1', + kind: 'Component', + metadata: { + name: 'test-component', + annotations: { + 'backstage.io/kubernetes-labels-selector': + 'backstage.io/test-label=test-component', + }, + }, + spec: { + type: 'service', + lifecycle: 'production', + owner: 'joe', + }, + }, + }, ); expect(getClustersByServiceId.mock.calls.length).toBe(1); @@ -186,10 +182,25 @@ describe('handleGetKubernetesObjectsForService', () => { }, getVoidLogger(), { - entity: goodEntity, auth: { google: 'google_token_123', }, + entity: { + apiVersion: 'backstage.io/v1beta1', + kind: 'Component', + metadata: { + name: 'test-component', + annotations: { + 'backstage.io/kubernetes-labels-selector': + 'backstage.io/test-label=test-component', + }, + }, + spec: { + type: 'service', + lifecycle: 'production', + owner: 'joe', + }, + }, }, ); diff --git a/plugins/kubernetes-backend/src/service/getKubernetesObjectsForServiceHandler.ts b/plugins/kubernetes-backend/src/service/getKubernetesObjectsForServiceHandler.ts index 82220f5d6c..2dd1e32b96 100644 --- a/plugins/kubernetes-backend/src/service/getKubernetesObjectsForServiceHandler.ts +++ b/plugins/kubernetes-backend/src/service/getKubernetesObjectsForServiceHandler.ts @@ -15,7 +15,6 @@ */ import { Logger } from 'winston'; -import { ComponentEntityV1alpha1 } from '@backstage/catalog-model'; import { KubernetesRequestBody, ClusterDetails, @@ -47,18 +46,6 @@ const DEFAULT_OBJECTS = new Set([ 'ingresses', ]); -function parseLabelSelector(entity: ComponentEntityV1alpha1): string { - const matchLabels = entity?.spec?.kubernetes?.selector?.matchLabels; - if (matchLabels) { - // TODO: figure out how to convert the selector to the full query param from the yaml - // (as shown here https://github.com/kubernetes/apimachinery/blob/master/pkg/labels/selector.go) - return Object.keys(matchLabels) - .map(key => `${key}=${matchLabels[key.toString()]}`) - .join(','); - } - return ''; -} - // Fans out the request to all clusters that the service lives in, aggregates their responses together export const handleGetKubernetesObjectsForService: GetKubernetesObjectsForServiceHandler = async ( serviceId, @@ -92,7 +79,10 @@ export const handleGetKubernetesObjectsForService: GetKubernetesObjectsForServic .join(', ')}]`, ); - const labelSelector = parseLabelSelector(requestBody.entity); + const labelSelector: string = + requestBody.entity?.metadata?.annotations?.[ + 'backstage.io/kubernetes-label-selector' + ] || `backstage.io/kubernetes-id=${requestBody.entity.metadata.name}`; return Promise.all( clusterDetailsDecoratedForAuth.map(clusterDetails => { diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index b2a5ef432c..9f0b90c032 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -23,7 +23,7 @@ import { V1ReplicaSet, V1Service, } from '@kubernetes/client-node'; -import { ComponentEntityV1alpha1 } from '@backstage/catalog-model'; +import { Entity } from '@backstage/catalog-model'; export interface ClusterDetails { name: string; @@ -36,7 +36,7 @@ export interface KubernetesRequestBody { auth?: { google?: string; }; - entity: ComponentEntityV1alpha1; + entity: Entity; } export interface ClusterObjects { diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 652fc394a2..9bc3004913 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes +## 0.3.1 + +### Patch Changes + +- bcc211a08: k8s-plugin: refactor approach to use annotation based label-selector +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-kubernetes-backend@0.2.1 + ## 0.3.0 ### Minor Changes diff --git a/plugins/kubernetes/README.md b/plugins/kubernetes/README.md index 8f3542a46e..a343764b70 100644 --- a/plugins/kubernetes/README.md +++ b/plugins/kubernetes/README.md @@ -17,23 +17,7 @@ It is only meant for local development, and the setup for it can be found inside There are two ways to surface your kubernetes components as part of an entity. The label selector takes precedence over the annotation/service id. -### Full label selector - -#### Adding the entity label selector to the spec - -In order for Backstage to detect that an entity has kubernetes components the `kubernetes.selector` must have a valid selector. (Currently only matchLabels is supported) - -```yaml -spec: - kubernetes: - selector: - matchLabels: - someKey: someValue - other-key: other-value - app.kubernetes.io/name: dice-roller -``` - -### Common `backstage.io/kubernetes-id` label on objects +### Common `backstage.io/kubernetes-id` label #### Adding the entity annotation @@ -53,3 +37,14 @@ as a part of an entity, Kubernetes components must be labeled with the following ```yaml 'backstage.io/kubernetes-id': ``` + +### label selector query annotation + +#### Adding a label selector query annotation + +You can write your own custom label selector query that backstage will use to lookup the objects (similar to `kubectl --selector="your query here"`) +review the documentation [here](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) for more info + +```yaml +'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end' +``` diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 75b9ee098c..6fa2f7e3ff 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.3.0", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@backstage/core": "^0.3.2", - "@backstage/plugin-kubernetes-backend": "^0.2.0", + "@backstage/plugin-kubernetes-backend": "^0.2.1", "@backstage/theme": "^0.2.1", "@kubernetes/client-node": "^0.12.1", "@material-ui/core": "^4.11.0", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx index 14cda3b5f7..65c1102d47 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx @@ -26,7 +26,7 @@ import { TabbedCard, useApi, } from '@backstage/core'; -import { ComponentEntityV1alpha1, Entity } from '@backstage/catalog-model'; +import { Entity } from '@backstage/catalog-model'; import { kubernetesApiRef } from '../../api/types'; import { KubernetesRequestBody, @@ -121,7 +121,7 @@ export const KubernetesContent = ({ entity }: KubernetesContentProps) => { (async () => { // For each auth type, invoke decorateRequestBodyForAuth on corresponding KubernetesAuthProvider let requestBody: KubernetesRequestBody = { - entity: entity as ComponentEntityV1alpha1, + entity, }; for (const authProviderStr of authProviders) { // Multiple asyncs done sequentially instead of all at once to prevent same requestBody from being modified simultaneously diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index e35f3dece0..2106111b5e 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-lighthouse +## 0.2.4 + +### Patch Changes + +- Updated dependencies [b4488ddb0] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/core-api@0.2.4 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.3 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 29b5a81cb9..da824044a9 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.2.3", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,11 +21,11 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@backstage/core": "^0.3.2", - "@backstage/core-api": "^0.2.1", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/core-api": "^0.2.4", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -38,9 +38,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 691b9ab862..9cfb810f4a 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -31,9 +31,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index b034d26c95..4f9d373fdc 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-proxy-backend +## 0.2.2 + +### Patch Changes + +- 6a6c7c14e: Filter the headers that are sent from the proxied-targed back to the frontend to not forwarded unwanted authentication or + monitoring contexts from other origins (like `Set-Cookie` with e.g. a google analytics context). The implementation reuses + the `allowedHeaders` configuration that now controls both directions `frontend->target` and `target->frontend`. +- 3619ea4c4: Add configuration schema for the commonly used properties +- Updated dependencies [612368274] + - @backstage/backend-common@0.3.3 + ## 0.2.1 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 1283f9d261..68c89169c5 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,7 +19,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.0", + "@backstage/backend-common": "^0.3.3", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -33,7 +33,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.3.0", + "@backstage/cli": "^0.4.0", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/CHANGELOG.md b/plugins/register-component/CHANGELOG.md index c569046855..56ece52c40 100644 --- a/plugins/register-component/CHANGELOG.md +++ b/plugins/register-component/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-register-component +## 0.2.3 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.2 ### Patch Changes diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index ad4432be83..18a97e36f7 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.2.2", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -36,9 +36,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index c91bf0cb9e..edaaa3a9b5 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.2", + "@backstage/backend-common": "^0.3.3", "@backstage/config": "^0.1.1", "@types/express": "^4.17.6", "axios": "^0.20.0", @@ -37,7 +37,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "@types/supertest": "^2.0.8", "supertest": "^4.0.2" }, diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 095f04104a..89c7a273af 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-rollbar +## 0.2.5 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.4 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 35b057c054..9cdd7895bf 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.1", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.4", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -37,9 +37,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index ea7e5c85ee..1b22ee1f14 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend +## 0.3.3 + +### Patch Changes + +- Updated dependencies [612368274] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/backend-common@0.3.3 + - @backstage/catalog-model@0.4.0 + ## 0.3.2 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index a095167a08..f71d8bc36c 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.1", - "@backstage/catalog-model": "^0.3.0", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@gitbeaker/core": "^25.2.0", "@gitbeaker/node": "^25.2.0", @@ -48,7 +48,7 @@ "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.3.1", + "@backstage/cli": "^0.4.0", "@octokit/types": "^5.4.1", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index c78e05c3e0..197d54a9a4 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-scaffolder +## 0.3.2 + +### Patch Changes + +- a9fd599f7: Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file: + + ```ts + export default async function createPlugin(env: PluginEnvironment) { + const builder = new CatalogBuilder(env); + const { + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + } = await builder.build(); + + return await createRouter({ + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + locationAnalyzer, //<-- + logger: env.logger, + }); + } + ``` + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.3.1 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 5a6a970851..381f2bfdbf 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.3.1", + "version": "0.3.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.3", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,9 +41,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 3545914e96..106499bad4 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search +## 0.2.2 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + ## 0.2.1 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index ddc7a2d687..490d5003ef 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ }, "dependencies": { "@backstage/core": "^0.3.2", - "@backstage/plugin-catalog": "^0.2.3", - "@backstage/catalog-model": "^0.3.0", + "@backstage/plugin-catalog": "^0.2.5", + "@backstage/catalog-model": "^0.4.0", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -34,9 +34,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry-backend/package.json b/plugins/sentry-backend/package.json index 2b6b5fc60d..5f5eafd5c6 100644 --- a/plugins/sentry-backend/package.json +++ b/plugins/sentry-backend/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.0", + "@backstage/backend-common": "^0.3.3", "@types/express": "^4.17.6", "axios": "^0.20.0", "compression": "^1.7.4", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.3.0" + "@backstage/cli": "^0.4.0" }, "files": [ "dist" diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index b388548d8e..026c0e80c7 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-sentry +## 0.2.4 + +### Patch Changes + +- 303c5ea17: Refactor route registration to remove deprecating code +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/catalog-model@0.4.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 4d8473c509..b952a81d51 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.2.3", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,7 +21,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", @@ -36,9 +36,9 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 9ebd73b812..69d94fa655 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-sonarqube +## 0.1.5 + +### Patch Changes + +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/catalog-model@0.4.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index d5635c2770..2dd52ab021 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,7 +21,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.0", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 532fc26e9c..269e0e0b4f 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@backstage/core": "^0.3.2", - "@backstage/test-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 0a396efd41..6557dfe924 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-backend +## 0.3.1 + +### Patch Changes + +- ae95c7ff3: Update URL auth format for Gitlab clone +- Updated dependencies [612368274] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] + - @backstage/backend-common@0.3.3 + - @backstage/catalog-model@0.4.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index bfa60c4b9c..3ba4f7c2b6 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.3.0", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.3.2", - "@backstage/catalog-model": "^0.3.1", + "@backstage/backend-common": "^0.3.3", + "@backstage/catalog-model": "^0.4.0", "@backstage/config": "^0.1.1", "@backstage/techdocs-common": "^0.1.1", "@types/dockerode": "^2.5.34", @@ -34,7 +34,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.3.2", + "@backstage/cli": "^0.4.0", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 5076362774..b3f1507f9f 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs +## 0.3.1 + +### Patch Changes + +- da2ad65cb: Use type EntityName from catalog-model for entities +- Updated dependencies [b4488ddb0] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/core-api@0.2.4 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + - @backstage/test-utils@0.1.4 + ## 0.3.0 ### Minor Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 6deb40fd0d..3ec6ed6457 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.3.0", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,12 +21,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.3.1", + "@backstage/catalog-model": "^0.4.0", "@backstage/core": "^0.3.2", - "@backstage/core-api": "^0.2.3", - "@backstage/plugin-catalog": "^0.2.4", + "@backstage/core-api": "^0.2.4", + "@backstage/plugin-catalog": "^0.2.5", "@backstage/techdocs-common": "^0.1.1", - "@backstage/test-utils": "^0.1.3", + "@backstage/test-utils": "^0.1.4", "@backstage/theme": "^0.2.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,9 +40,9 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index da4c2cd1cd..e1b94e85d9 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/CHANGELOG.md b/plugins/welcome/CHANGELOG.md index e6165ef4f9..b1111eb107 100644 --- a/plugins/welcome/CHANGELOG.md +++ b/plugins/welcome/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-welcome +## 0.2.2 + +### Patch Changes + +- 303c5ea17: Refactor route registration to remove deprecating code + ## 0.2.1 ### Patch Changes diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 167bb07565..fe1d2e1604 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-welcome", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -32,9 +32,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.3.2", - "@backstage/dev-utils": "^0.1.4", - "@backstage/test-utils": "^0.1.3", + "@backstage/cli": "^0.4.0", + "@backstage/dev-utils": "^0.1.5", + "@backstage/test-utils": "^0.1.4", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", "@testing-library/user-event": "^12.0.7", diff --git a/scripts/verify-links.js b/scripts/verify-links.js index 0422352d4e..75e7a8c1d0 100755 --- a/scripts/verify-links.js +++ b/scripts/verify-links.js @@ -74,6 +74,14 @@ async function verifyUrl(basePath, absUrl, docPages) { path = resolvePath(dirname(resolvePath(projectRoot, basePath)), url); } + if ( + absUrl === url && + basePath.match(/^(?:docs)\//) && + !path.startsWith(resolvePath(projectRoot, 'docs')) + ) { + return { url, basePath, problem: 'out-of-docs' }; + } + const exists = await fs.pathExists(path); if (!exists) { return { url, basePath, problem: 'missing' }; @@ -148,6 +156,18 @@ async function main() { console.error( `Unable to reach ${url} from root or microsite/static/, linked from ${basePath}`, ); + } else if (problem === 'out-of-docs') { + console.error( + 'Links in docs must use absolute URLs for targets outside of docs', + ); + console.error(` From: ${basePath}`); + console.error(` To: ${url}`); + console.error( + ` Likely replace with: https://github.com/backstage/backstage/blob/master/${url.replace( + /^[./]+/, + '', + )}`, + ); } else if (problem === 'doc-missing') { const suggestion = docPages.get(url) || diff --git a/yarn.lock b/yarn.lock index 9e43ab4deb..ef90303927 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5058,10 +5058,10 @@ dependencies: "@types/node" "*" -"@types/codemirror@^0.0.97": - version "0.0.97" - resolved "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.97.tgz#6f2d8266b7f1b34aacfe8c77221fafe324c3d081" - integrity sha512-n5d7o9nWhC49DjfhsxANP7naWSeTzrjXASkUDQh7626sM4zK9XP2EVcHp1IcCf/IPV6c7ORzDUDF3Bkt231VKg== +"@types/codemirror@^0.0.100": + version "0.0.100" + resolved "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.100.tgz#3ebdf1514208d73f38c25925d9cf9399a42a7471" + integrity sha512-4jGmu1T8vpQrJCe8cbe3KveiJmK2UAt3rZO2qE2sPoMhGLuwW0cMzFYJLyXebbRJg5G3RbuUXLip1IHPUESkFA== dependencies: "@types/tern" "*" @@ -5534,10 +5534,12 @@ dependencies: "@types/node" "*" -"@types/jwt-decode@2.2.1": - version "2.2.1" - resolved "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-2.2.1.tgz#afdf5c527fcfccbd4009b5fd02d1e18241f2d2f2" - integrity sha512-aWw2YTtAdT7CskFyxEX2K21/zSDStuf/ikI3yBqmwpwJF0pS+/IX5DWv+1UFffZIbruP6cnT9/LAJV1gFwAT1A== +"@types/jwt-decode@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-3.1.0.tgz#eb1b24f436962b8857beaf8addfd542de56670a2" + integrity sha512-tthwik7TKkou3mVnBnvVuHnHElbjtdbM63pdBCbZTirCt3WAdM73Y79mOri7+ljsS99ZVwUFZHLMxJuJnv/z1w== + dependencies: + jwt-decode "*" "@types/keygrip@*": version "1.0.2" @@ -8952,10 +8954,10 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -cliui@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.1.tgz#a4cb67aad45cd83d8d05128fc9f4d8fbb887e6b3" - integrity sha512-rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" @@ -11327,10 +11329,10 @@ escalade@^3.0.1: resolved "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ== -escalade@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e" - integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig== +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-goat@^2.0.0: version "2.1.1" @@ -12926,7 +12928,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6: +glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -15809,18 +15811,10 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" -jws@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== - dependencies: - jwa "^2.0.0" - safe-buffer "^5.0.1" - -jwt-decode@2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79" - integrity sha1-fYa9VmefWM5qhHBKZX3TkruoGnk= +jwt-decode@*, jwt-decode@^3.1.0: + version "3.1.2" + resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== keyv@^3.0.0: version "3.1.0" @@ -23907,21 +23901,21 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript-json-schema@^0.43.0: - version "0.43.0" - resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.43.0.tgz#8bd9c832f1f15f006ff933907ce192222fdfd92f" - integrity sha512-4c9IMlIlHYJiQtzL1gh2nIPJEjBgJjDUs50gsnnc+GFyDSK1oFM3uQIBSVosiuA/4t6LSAXDS9vTdqbQC6EcgA== +typescript-json-schema@^0.45.0: + version "0.45.0" + resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.45.0.tgz#2f244e99518e589a442ee5f9c1d2c85a1ec7dc84" + integrity sha512-4MeX0HIODRd+K1/sIbkPryGXG43PVQ9cvIC8gDYml6yBgcsWLjvPMpTNr9VV+bQwpDLncB8Ie4qSenuKUwNZpg== dependencies: - "@types/json-schema" "^7.0.5" - glob "~7.1.6" + "@types/json-schema" "^7.0.6" + glob "^7.1.6" json-stable-stringify "^1.0.1" - typescript "~4.0.2" - yargs "^15.4.1" + typescript "^4.1.2" + yargs "^16.1.1" -typescript@^4.0.3, typescript@~4.0.2: - version "4.0.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" - integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== +typescript@^4.0.3, typescript@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9" + integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ== ua-parser-js@^0.7.18: version "0.7.21" @@ -25137,10 +25131,10 @@ y18n@^4.0.0: resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -y18n@^5.0.1: - version "5.0.2" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.2.tgz#48218df5da2731b4403115c39a1af709c873f829" - integrity sha512-CkwaeZw6dQgqgPGeTWKMXCRmMcBgETFlTml1+ZOO+q7kGst8NREJ+eWwFNPVUQ4QGdAaklbqCZHH6Zuep1RjiA== +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== yaeti@^0.0.6: version "0.0.6" @@ -25208,10 +25202,10 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.0.0: - version "20.2.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.1.tgz#28f3773c546cdd8a69ddae68116b48a5da328e77" - integrity sha512-yYsjuSkjbLMBp16eaOt7/siKTjNVjMm3SoJnIg3sEh/JsvqVVDyjRKmaJV4cl+lNIgq6QEco2i3gDebJl7/vLA== +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@^3.2.0: version "3.2.0" @@ -25271,18 +25265,18 @@ yargs@^15.1.0, yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.3: - version "16.0.3" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.0.3.tgz#7a919b9e43c90f80d4a142a89795e85399a7e54c" - integrity sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA== +yargs@^16.0.3, yargs@^16.1.1: + version "16.1.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.1.1.tgz#5a4a095bd1ca806b0a50d0c03611d38034d219a1" + integrity sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w== dependencies: - cliui "^7.0.0" - escalade "^3.0.2" + cliui "^7.0.2" + escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.0" - y18n "^5.0.1" - yargs-parser "^20.0.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" yargs@^5.0.0: version "5.0.0"