diff --git a/.changeset/brave-spoons-grow.md b/.changeset/brave-spoons-grow.md deleted file mode 100644 index d1bd166510..0000000000 --- a/.changeset/brave-spoons-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Switched to relying on the built-in support for async loading in `react-syntax-highlighter`. This should provide further improvements to async rendering and lazy loading, and avoid test flakiness that was happening because of the significant number or resources being loaded in lazily all at once. diff --git a/.changeset/brave-waves-explain.md b/.changeset/brave-waves-explain.md deleted file mode 100644 index 5e76f192e0..0000000000 --- a/.changeset/brave-waves-explain.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Removed the included `jest` configuration from the root `package.json` as the `transformModules` option no longer exists. - -To apply this change to an existing app, make the follow change to the root `package.json`: - -```diff -- "jest": { -- "transformModules": [ -- "@asyncapi/react-component" -- ] -- } -``` diff --git a/.changeset/brown-paws-marry.md b/.changeset/brown-paws-marry.md deleted file mode 100644 index c771d5e1f4..0000000000 --- a/.changeset/brown-paws-marry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Fix a bug preventing an access token to be refreshed a second time with the GitHub provider. diff --git a/.changeset/chilled-tools-worry.md b/.changeset/chilled-tools-worry.md deleted file mode 100644 index 58f89ef503..0000000000 --- a/.changeset/chilled-tools-worry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/config-loader': patch ---- - -Configuration schema errors are now filtered using the provided visibility option. This means that schema errors due to missing backend configuration will no longer break frontend builds. diff --git a/.changeset/giant-phones-exercise.md b/.changeset/giant-phones-exercise.md deleted file mode 100644 index b190c2e7f0..0000000000 --- a/.changeset/giant-phones-exercise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-fossa': patch ---- - -Add `entitiesFilter` option to `FossaPage` to filter entities displayed in the table. diff --git a/.changeset/quiet-parrots-occur.md b/.changeset/quiet-parrots-occur.md deleted file mode 100644 index c9a6ce9a1d..0000000000 --- a/.changeset/quiet-parrots-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-graph': patch ---- - -Capture analytics events for clicks in the graph. diff --git a/.changeset/rare-cooks-play.md b/.changeset/rare-cooks-play.md deleted file mode 100644 index b76d34ee62..0000000000 --- a/.changeset/rare-cooks-play.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/create-app': minor ---- - -Removed `@backstage/plugin-welcome`, no new updates to the packages will be -published in the future. - -The welcome plugin was used by early alpha versions of Backstage, but today only -contained a simple page with welcome instructions. It was superseded by -`@backstage/plugin-home` which can be used to build a homepage customized to the -needs of your organization. - -If it's still used in your app, remove the dependency from your `package.json` -as well as left over code. diff --git a/.changeset/rude-comics-float.md b/.changeset/rude-comics-float.md deleted file mode 100644 index cb3a883fa1..0000000000 --- a/.changeset/rude-comics-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins': patch ---- - -Sort Jenkins build by timestamp in `CITable` diff --git a/.changeset/selfish-papayas-watch.md b/.changeset/selfish-papayas-watch.md deleted file mode 100644 index 1d9b8a1ab6..0000000000 --- a/.changeset/selfish-papayas-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-git-release-manager': patch ---- - -Minor internal type improvement diff --git a/.changeset/techdocs-dull-glasses-decide.md b/.changeset/techdocs-dull-glasses-decide.md deleted file mode 100644 index f043c2f607..0000000000 --- a/.changeset/techdocs-dull-glasses-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Only replace the shadow dom if the content is changed to avoid a flickering UI. diff --git a/.changeset/wet-socks-grow.md b/.changeset/wet-socks-grow.md new file mode 100644 index 0000000000..8ccbb7a2a2 --- /dev/null +++ b/.changeset/wet-socks-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/errors': patch +--- + +Add `stringifyError` that is useful for logging e.g. `Something went wrong, ${stringifyError(e)}` diff --git a/.changeset/wise-camels-run.md b/.changeset/wise-camels-run.md deleted file mode 100644 index 4438032b77..0000000000 --- a/.changeset/wise-camels-run.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/cli': minor ---- - -The Jest configuration that's included with the Backstage CLI has received several changes. - -As a part of migrating to more widespread usage of ESM modules, the default configuration now transforms all source files everywhere, including those within `node_modules`. Due to this change the existing `transformModules` option has been removed and will be ignored. There is also a list of known packages that do not require transforms in the CLI, which will evolve over time. If needed there will also be an option to add packages to this list in the future, but it is not included yet to avoid clutter. - -To counteract the slowdown of the additional transforms that have been introduced, the default configuration has also been reworked to enable caching across different packages. Previously each package in a Backstage monorepo would have its own isolated Jest cache, but it is now shared between packages that have a similar enough Jest configuration. - -Another change that will speed up test execution is that the transformer for `.esm.js` files has been switched. It used to be an ESM transformer based on Babel, but it is also done by sucrase now since it is significantly faster. - -The changes above are not strictly breaking as all tests should still work. It may however cause excessive slowdowns in projects that have configured custom transforms in the `jest` field within `package.json` files. In this case it is either best to consider removing the custom transforms, or overriding the `transformIgnorePatterns` to instead use Jest's default `'/node_modules/'` pattern. - -This change also removes the `@backstage/cli/config/jestEsmTransform.js` transform, which can be replaced by using the `@backstage/cli/config/sucraseEsmTransform.js` transform instead. diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index a38baab3dc..7e478953bc 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -8,6 +8,7 @@ apis args asciidoc async +Atlassian automations autoscaling Autoscaling @@ -310,3 +311,4 @@ Zalando Zhou zoomable zsh +Firestore diff --git a/ADOPTERS.md b/ADOPTERS.md index f96cc08e18..db751bfb9b 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -58,3 +58,4 @@ | [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | | [Box](https://www.box.com) | [@kielosz](https://github.com/kielosz), [@jluk-box](https://github.com/jluk-box), [@ptychu](https://github.com/ptychu), [@alexrybch](https://github.com/alexrybch), [@szubster](https://github.com/szubster) | Developer portal for service catalog, integration with internal systems, new service onboarding. | | [Bazaarvoice](https://www.bazaarvoice.com) | [@niallmccullagh](https://github.com/niallmccullagh) | Developer portal for service catalog and scaffolds, publishing Github docs and API documentation, visualising our internal tech radar and our product engineering org structure. | +| [Krateo PlatformOps](https://www.krateo.io) | [@projectkerberus](https://github.com/projectkerberus) | A multi-cloud control plane to create, manage and deploy any kind of resource easily and centrally via a Developer Portal that centralizes via a self-service catalog the templating and ownership of services, the available documentation, the overview of the components that compose an entire domain and all the data of the service lifecycle. | diff --git a/app-config.yaml b/app-config.yaml index c24ac1701b..8aa3bc569c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -292,6 +292,15 @@ scaffolder: visibility: public # or or 'private' auth: + ### Add auth.keyStore.provider to more granularly control how to store JWK data when running + # the auth-backend. + # + # keyStore: + # provider: firestore + # firestore: + # projectId: my-project + # path: my-sessions + environment: development ### Providing an auth.session.secret will enable session support in the auth-backend # session: @@ -364,6 +373,11 @@ auth: development: clientId: ${AUTH_BITBUCKET_CLIENT_ID} clientSecret: ${AUTH_BITBUCKET_CLIENT_SECRET} + atlassian: + development: + clientId: ${AUTH_ATLASSIAN_CLIENT_ID} + clientSecret: ${AUTH_ATLASSIAN_CLIENT_SECRET} + scopes: ${AUTH_ATLASSIAN_SCOPES} costInsights: engineerCost: 200000 products: diff --git a/docs/auth/atlassian/provider.md b/docs/auth/atlassian/provider.md new file mode 100644 index 0000000000..38774ca3cc --- /dev/null +++ b/docs/auth/atlassian/provider.md @@ -0,0 +1,64 @@ +--- +id: provider +title: Atlassian Authentication Provider +sidebar_label: Atlassian +description: Adding Atlassian as an authentication provider in Backstage +--- + +The Backstage `core-plugin-api` package comes with an Atlassian authentication +provider that can authenticate users using Atlassian products. This auth +**only** provides scopes for the following APIs: + +- Confluence API +- User REST API +- Jira platform REST API +- Jira Service Desk API +- Personal data reporting API +- User identity API + +## Create an OAuth 2.0 (3LO) app in the Atlassian developer console + +To add Atlassian authentication, you must create an OAuth 2.0 (3LO) app. + +Go to `https://developer.atlassian.com/console/myapps/`. + +Click on the drop down `Create`, and choose `OAuth 2.0 integration`. + +Name your integration and click on the `Create` button. + +Settings for local development: + +- Callback URL: `http://localhost:7000/api/auth/atlassian` +- Use rotating refresh tokens +- For permissions, you **must** enable `View user profile` for the currently + logged-in user, under `User identity API` + +## Configuration + +The provider configuration can then be added to your `app-config.yaml` under the +root `auth` configuration: + +```yaml +auth: + environment: development + providers: + atlassian: + development: + clientId: ${AUTH_ATLASSIAN_CLIENT_ID} + clientSecret: ${AUTH_ATLASSIAN_CLIENT_SECRET} + scopes: ${AUTH_ATLASSIAN_SCOPES} +``` + +The Atlassian provider is a structure with three configuration keys: + +- `clientId`: The Key you generated in the developer console. +- `clientSecret`: The Secret tied to the generated Key. +- `scopes`: List of scopes the app has permissions for, separated by spaces. + +**NOTE:** the scopes `offline_access` and `read:me` are provided by default. + +## Adding the provider to the Backstage frontend + +To add the provider to the frontend, add the `atlassianAuthApi` reference and +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index f48faa202d..6b3991144b 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -29,6 +29,7 @@ kubernetes: serviceAccountToken: ${K8S_MINIKUBE_TOKEN} dashboardUrl: http://127.0.0.1:64713 # url copied from running the command: minikube service kubernetes-dashboard -n kubernetes-dashboard dashboardApp: standard + caData: ${K8S_CONFIG_CA_DATA} - url: http://127.0.0.2:9999 name: aws-cluster-1 authProvider: 'aws' @@ -135,6 +136,24 @@ See also https://github.com/backstage/backstage/tree/master/plugins/kubernetes/src/utils/clusterLinks/formatters for real examples. +##### `clusters.\*.caData` (optional) + +PEM-encoded certificate authority certificates. + +This values could be obtained via inspecting the Kubernetes config file (usually +at `~/.kube/config`) under `clusters.cluster.certificate-authority-data`. For +GKE, execute the following command to obtain the value + +``` +gcloud container clusters describe \ + --zone= \ + --format="value(masterAuth.clusterCaCertificate)" +``` + +See also +https://cloud.google.com/kubernetes-engine/docs/how-to/api-server-authentication#environments-without-gcloud +for complete docs about GKE without `gcloud`. + #### `gke` This cluster locator is designed to work with Kubernetes clusters running in diff --git a/docs/features/techdocs/creating-and-publishing.md b/docs/features/techdocs/creating-and-publishing.md index c0989bf02e..ec27a81653 100644 --- a/docs/features/techdocs/creating-and-publishing.md +++ b/docs/features/techdocs/creating-and-publishing.md @@ -38,7 +38,7 @@ up and running and to start writing your documentation. If you have created software templates that do not include documentation by default, we highly recommend you to set that up. Follow our how-to guide -[How to add documentation setup to your software templates](./how-to-guides.md#how-to-add-documentation-setup-to-your-software-templates) +[How to add documentation setup to your software templates](./how-to-guides.md#how-to-add-the-documentation-setup-to-your-software-templates) to get started. ### Use the documentation template diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index 0b1809dc22..d79f6bb891 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -215,10 +215,10 @@ techdocs: publisher: type: 'local' generator: - techdocs: local + runIn: local ``` -Setting `generators.techdocs` to `local` means you will have to make sure your +Setting `generator.runIn` to `local` means you will have to make sure your environment is compatible with techdocs. You will have to install the `mkdocs` and `mkdocs-techdocs-core` package from diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index edca4e84ad..8ff486af37 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -110,7 +110,8 @@ TechDocs uses a composability pattern similar to the Search and Catalog plugins in Backstage. While a default table experience, similar to the one provided by the Catalog plugin, is made available for ease-of-use, it's possible for you to provide a completely custom experience, tailored to the needs of your -organization. +organization. For example, TechDocs comes with an alternative grid based layout +(``). This is done in your `app` package. By default, you might see something like this in your `App.tsx`: diff --git a/docs/integrations/azure/org.md b/docs/integrations/azure/org.md index c359960f4d..8e17bb2cc1 100644 --- a/docs/integrations/azure/org.md +++ b/docs/integrations/azure/org.md @@ -7,7 +7,7 @@ description: Importing users and groups from a Microsoft Azure Active Directory --- The Backstage catalog can be set up to ingest organizational data - users and -teams - directly from an tenant in Microsoft Azure Active Directory via the +teams - directly from a tenant in Microsoft Azure Active Directory via the Microsoft Graph API. More details on this are available in the diff --git a/microsite/sidebars.json b/microsite/sidebars.json index cdbfdccec3..bee9cd7180 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -213,6 +213,7 @@ "label": "Included providers", "ids": [ "auth/auth0/provider", + "auth/atlassian/provider", "auth/bitbucket/provider", "auth/microsoft/provider", "auth/github/provider", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index d81623e7c0..1dee41af56 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,24 @@ # example-app +## 0.2.51 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.1 + - @backstage/plugin-techdocs@0.12.3 + - @backstage/plugin-sentry@0.3.26 + - @backstage/plugin-user-settings@0.3.10 + - @backstage/plugin-catalog-graph@0.2.1 + - @backstage/plugin-jenkins@0.5.11 + - @backstage/core-app-api@0.1.18 + - @backstage/core-plugin-api@0.1.11 + - @backstage/cli@0.8.0 + - @backstage/plugin-catalog@0.7.2 + - @backstage/plugin-catalog-import@0.7.3 + - @backstage/plugin-catalog-react@0.6.1 + - @backstage/catalog-model@0.9.5 + ## 0.2.50 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 3f4a8bef70..833e146d45 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,22 +1,22 @@ { "name": "example-app", - "version": "0.2.50", + "version": "0.2.51", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.9.4", - "@backstage/cli": "^0.7.16", - "@backstage/core-app-api": "^0.1.17", - "@backstage/core-components": "^0.7.0", - "@backstage/core-plugin-api": "^0.1.10", + "@backstage/catalog-model": "^0.9.5", + "@backstage/cli": "^0.8.0", + "@backstage/core-app-api": "^0.1.18", + "@backstage/core-components": "^0.7.1", + "@backstage/core-plugin-api": "^0.1.11", "@backstage/integration-react": "^0.1.12", "@backstage/plugin-api-docs": "^0.6.12", "@backstage/plugin-azure-devops": "^0.1.1", "@backstage/plugin-badges": "^0.2.13", - "@backstage/plugin-catalog": "^0.7.1", - "@backstage/plugin-catalog-graph": "^0.2.0", - "@backstage/plugin-catalog-import": "^0.7.2", - "@backstage/plugin-catalog-react": "^0.6.0", + "@backstage/plugin-catalog": "^0.7.2", + "@backstage/plugin-catalog-graph": "^0.2.1", + "@backstage/plugin-catalog-import": "^0.7.3", + "@backstage/plugin-catalog-react": "^0.6.1", "@backstage/plugin-circleci": "^0.2.27", "@backstage/plugin-cloudbuild": "^0.2.27", "@backstage/plugin-code-coverage": "^0.1.15", @@ -26,7 +26,7 @@ "@backstage/plugin-github-actions": "^0.4.22", "@backstage/plugin-graphiql": "^0.2.20", "@backstage/plugin-home": "^0.4.4", - "@backstage/plugin-jenkins": "^0.5.10", + "@backstage/plugin-jenkins": "^0.5.11", "@backstage/plugin-kafka": "^0.2.19", "@backstage/plugin-kubernetes": "^0.4.17", "@backstage/plugin-lighthouse": "^0.2.29", @@ -36,12 +36,12 @@ "@backstage/plugin-rollbar": "^0.3.18", "@backstage/plugin-scaffolder": "^0.11.8", "@backstage/plugin-search": "^0.4.15", - "@backstage/plugin-sentry": "^0.3.25", + "@backstage/plugin-sentry": "^0.3.26", "@backstage/plugin-shortcuts": "^0.1.12", "@backstage/plugin-tech-radar": "^0.4.11", - "@backstage/plugin-techdocs": "^0.12.2", + "@backstage/plugin-techdocs": "^0.12.3", "@backstage/plugin-todo": "^0.1.14", - "@backstage/plugin-user-settings": "^0.3.9", + "@backstage/plugin-user-settings": "^0.3.10", "@backstage/search-common": "^0.2.0", "@backstage/theme": "^0.2.11", "@material-ui/core": "^4.12.2", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index e7b897692e..131b160a36 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-common +## 0.9.7 + +### Patch Changes + +- be59619212: Add "rate limit exceeded" to error from GithubUrlReader if that is the cause of a read failure +- 36e67d2f24: Internal updates to apply more strict checks to throw errors. +- Updated dependencies + - @backstage/config-loader@0.7.0 + - @backstage/errors@0.1.3 + ## 0.9.6 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 16167f7803..8da4184f71 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.9.6", + "version": "0.9.7", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -31,8 +31,8 @@ "dependencies": { "@backstage/cli-common": "^0.1.4", "@backstage/config": "^0.1.10", - "@backstage/config-loader": "^0.6.10", - "@backstage/errors": "^0.1.2", + "@backstage/config-loader": "^0.7.0", + "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.7", "@google-cloud/storage": "^5.8.0", "@octokit/rest": "^18.5.3", @@ -77,7 +77,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.7.15", + "@backstage/cli": "^0.8.0", "@backstage/test-utils": "^0.1.18", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index f6e42d1945..62de2e001e 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -17,6 +17,7 @@ import knexFactory, { Knex } from 'knex'; import { Config } from '@backstage/config'; +import { ForwardedError } from '@backstage/errors'; import { mergeDatabaseConfig } from '../config'; import { DatabaseConnector } from '../types'; import defaultNameOverride from './defaultNameOverride'; @@ -94,8 +95,7 @@ function requirePgConnectionString() { try { return require('pg-connection-string').parse; } catch (e) { - const message = `Postgres: Install 'pg-connection-string'`; - throw new Error(`${message}\n${e.message}`); + throw new ForwardedError("Postgres: Install 'pg-connection-string'", e); } } diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts index 0bc0e4f97f..6959edcc8b 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts @@ -167,7 +167,7 @@ describe('AwsS3UrlReader', () => { ), ).rejects.toThrow( Error( - `Could not retrieve file from S3: not a valid AWS S3 URL: https://test-bucket.s3.us-east-2.NOTamazonaws.com/file.yaml`, + `Could not retrieve file from S3; caused by Error: not a valid AWS S3 URL: https://test-bucket.s3.us-east-2.NOTamazonaws.com/file.yaml`, ), ); }); @@ -216,7 +216,7 @@ describe('AwsS3UrlReader', () => { ), ).rejects.toThrow( Error( - `Could not retrieve file from S3: not a valid AWS S3 URL: https://test-bucket.s3.us-east-2.NOTamazonaws.com/file.yaml`, + `Could not retrieve file from S3; caused by Error: not a valid AWS S3 URL: https://test-bucket.s3.us-east-2.NOTamazonaws.com/file.yaml`, ), ); }); diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.ts b/packages/backend-common/src/reading/AwsS3UrlReader.ts index cb03ec4f71..7fd0f6ba19 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.ts @@ -27,6 +27,7 @@ import { } from './types'; import getRawBody from 'raw-body'; import { AwsS3Integration, ScmIntegrations } from '@backstage/integration'; +import { ForwardedError } from '@backstage/errors'; import { ListObjectsV2Output, ObjectList } from 'aws-sdk/clients/s3'; const parseURL = ( @@ -162,7 +163,7 @@ export class AwsS3UrlReader implements UrlReader { etag: etag, }; } catch (e) { - throw new Error(`Could not retrieve file from S3: ${e.message}`); + throw new ForwardedError('Could not retrieve file from S3', e); } } @@ -203,7 +204,7 @@ export class AwsS3UrlReader implements UrlReader { return await this.deps.treeResponseFactory.fromReadableArray(responses); } catch (e) { - throw new Error(`Could not retrieve file tree from S3: ${e.message}`); + throw new ForwardedError('Could not retrieve file tree from S3', e); } } diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index e70444cf2c..c76364976d 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -221,6 +221,32 @@ describe('GithubUrlReader', () => { ).rejects.toThrow(NotModifiedError); }); + it('should throw Error with ratelimit exceeded if GitHub responds with 403 and rate limit is exceeded', async () => { + expect.assertions(1); + + worker.use( + rest.get( + 'https://ghe.github.com/api/v3/repos/backstage/mock/tree/contents/', + (_req, res, ctx) => { + return res( + ctx.status(403), + ctx.set('X-RateLimit-Remaining', '0'), + ctx.body( + '{"message": "API rate limit exceeded for xxx.xxx.xxx.xxx..."}', + ), + ); + }, + ), + ); + + await expect( + gheProcessor.readUrl( + 'https://github.com/backstage/mock/tree/blob/main', + { etag: 'foo' }, + ), + ).rejects.toThrow(/rate limit exceeded/); + }); + it('should return etag from the response', async () => { (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ headers: { diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 1eed5764ef..ca16e23fec 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -126,10 +126,21 @@ export class GithubUrlReader implements UrlReader { }; } - const message = `${url} could not be read as ${ghUrl}, ${response.status} ${response.statusText}`; + let message = `${url} could not be read as ${ghUrl}, ${response.status} ${response.statusText}`; if (response.status === 404) { throw new NotFoundError(message); } + + // GitHub returns a 403 response with a couple of headers indicating rate + // limit status. See more in the GitHub docs: + // https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting + if ( + response.status === 403 && + response.headers.get('X-RateLimit-Remaining') === '0' + ) { + message += ' (rate limit exceeded)'; + } + throw new Error(message); } diff --git a/packages/backend-common/src/reading/integration.test.ts b/packages/backend-common/src/reading/integration.test.ts index ae557be583..951df29605 100644 --- a/packages/backend-common/src/reading/integration.test.ts +++ b/packages/backend-common/src/reading/integration.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { isError } from '@backstage/errors'; import { getVoidLogger } from '../logging'; import { UrlReaders } from './UrlReaders'; @@ -71,7 +72,10 @@ function withRetries(count: number, fn: () => Promise) { error = err; } } - if (!error.message.match(/rate limit|Too Many Requests/)) { + if ( + isError(error) && + !error.message.match(/rate limit|Too Many Requests/) + ) { throw error; } else { console.warn('Request was rate limited', error); diff --git a/packages/backend-common/src/util/DockerContainerRunner.ts b/packages/backend-common/src/util/DockerContainerRunner.ts index da3ce66ec2..b81bd995ca 100644 --- a/packages/backend-common/src/util/DockerContainerRunner.ts +++ b/packages/backend-common/src/util/DockerContainerRunner.ts @@ -16,6 +16,7 @@ import Docker from 'dockerode'; import fs from 'fs-extra'; +import { ForwardedError } from '@backstage/errors'; import { PassThrough } from 'stream'; import { ContainerRunner, RunContainerOptions } from './ContainerRunner'; @@ -45,8 +46,9 @@ export class DockerContainerRunner implements ContainerRunner { try { await this.dockerClient.ping(); } catch (e) { - throw new Error( - `This operation requires Docker. Docker does not appear to be available. Docker.ping() failed with: ${e.message}`, + throw new ForwardedError( + 'This operation requires Docker. Docker does not appear to be available. Docker.ping() failed with', + e, ); } diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 6e10d188df..287e40a296 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-test-utils +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.9.7 + - @backstage/cli@0.8.0 + ## 0.1.7 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 1713f05d25..2f29c2412b 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.7", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.2", - "@backstage/cli": "^0.7.11", + "@backstage/backend-common": "^0.9.7", + "@backstage/cli": "^0.8.0", "@backstage/config": "^0.1.9", "knex": "^0.95.1", "mysql2": "^2.2.5", @@ -41,7 +41,7 @@ "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.11", + "@backstage/cli": "^0.8.0", "jest": "^26.0.1" }, "files": [ diff --git a/packages/backend/package.json b/packages/backend/package.json index d02282d569..b9a28c2853 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -24,36 +24,36 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.9.6", + "@backstage/backend-common": "^0.9.7", "@backstage/catalog-client": "^0.5.0", - "@backstage/catalog-model": "^0.9.4", + "@backstage/catalog-model": "^0.9.5", "@backstage/config": "^0.1.10", "@backstage/integration": "^0.6.8", - "@backstage/plugin-app-backend": "^0.3.16", - "@backstage/plugin-auth-backend": "^0.4.4", - "@backstage/plugin-azure-devops-backend": "^0.1.2", + "@backstage/plugin-app-backend": "^0.3.17", + "@backstage/plugin-auth-backend": "^0.4.5", + "@backstage/plugin-azure-devops-backend": "^0.1.3", "@backstage/plugin-badges-backend": "^0.1.11", - "@backstage/plugin-catalog-backend": "^0.17.0", - "@backstage/plugin-code-coverage-backend": "^0.1.13", + "@backstage/plugin-catalog-backend": "^0.17.1", + "@backstage/plugin-code-coverage-backend": "^0.1.14", "@backstage/plugin-graphql-backend": "^0.1.9", "@backstage/plugin-jenkins-backend": "^0.1.6", - "@backstage/plugin-kubernetes-backend": "^0.3.17", + "@backstage/plugin-kubernetes-backend": "^0.3.18", "@backstage/plugin-kafka-backend": "^0.2.10", "@backstage/plugin-proxy-backend": "^0.2.13", "@backstage/plugin-rollbar-backend": "^0.1.15", - "@backstage/plugin-scaffolder-backend": "^0.15.8", + "@backstage/plugin-scaffolder-backend": "^0.15.10", "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.5", "@backstage/plugin-search-backend": "^0.2.6", "@backstage/plugin-search-backend-node": "^0.4.2", "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.4", "@backstage/plugin-search-backend-module-pg": "^0.2.1", - "@backstage/plugin-techdocs-backend": "^0.10.4", + "@backstage/plugin-techdocs-backend": "^0.10.5", "@backstage/plugin-todo-backend": "^0.1.13", "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^11.0.1", "dockerode": "^3.2.1", - "example-app": "^0.2.50", + "example-app": "^0.2.51", "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-prom-bundle": "^6.3.6", @@ -65,7 +65,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.7.16", + "@backstage/cli": "^0.8.0", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 8da62f6115..f8dfd59e41 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -30,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.4", - "@backstage/errors": "^0.1.2", + "@backstage/catalog-model": "^0.9.5", + "@backstage/errors": "^0.1.3", "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.7.15", + "@backstage/cli": "^0.8.0", "@types/jest": "^26.0.7", "msw": "^0.29.0" }, diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index e7c36953db..066b01cd50 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/catalog-model +## 0.9.5 + +### Patch Changes + +- ab2df3be33: Improved documentation for exported symbols. +- Updated dependencies + - @backstage/errors@0.1.3 + ## 0.9.4 ### Patch Changes diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 1e7eb8fd52..c9429f9bdb 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -9,12 +9,12 @@ import { JsonValue } from '@backstage/config'; import { SerializedError } from '@backstage/errors'; import * as yup from 'yup'; -// @public @deprecated (undocumented) +// @public @deprecated export const analyzeLocationSchema: yup.SchemaOf<{ location: LocationSpec; }>; -// @public (undocumented) +// @public interface ApiEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -32,7 +32,7 @@ interface ApiEntityV1alpha1 extends Entity { export { ApiEntityV1alpha1 as ApiEntity }; export { ApiEntityV1alpha1 }; -// @public (undocumented) +// @public export const apiEntityV1alpha1Validator: KindValidator; // @public @@ -58,7 +58,7 @@ export function compareEntityToRef( context?: EntityRefContext, ): boolean; -// @public (undocumented) +// @public interface ComponentEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -79,7 +79,7 @@ interface ComponentEntityV1alpha1 extends Entity { export { ComponentEntityV1alpha1 as ComponentEntity }; export { ComponentEntityV1alpha1 }; -// @public (undocumented) +// @public export const componentEntityV1alpha1Validator: KindValidator; // @public @@ -89,7 +89,7 @@ export class DefaultNamespaceEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } -// @public (undocumented) +// @public interface DomainEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -103,10 +103,10 @@ interface DomainEntityV1alpha1 extends Entity { export { DomainEntityV1alpha1 as DomainEntity }; export { DomainEntityV1alpha1 }; -// @public (undocumented) +// @public export const domainEntityV1alpha1Validator: KindValidator; -// @public (undocumented) +// @public export const EDIT_URL_ANNOTATION = 'backstage.io/edit-url'; // @public @@ -181,7 +181,7 @@ export type EntityName = { name: string; }; -// @public (undocumented) +// @public export const EntityPolicies: { allOf(policies: EntityPolicy[]): EntityPolicy; oneOf(policies: EntityPolicy[]): EntityPolicy; @@ -250,7 +250,7 @@ export function getEntitySourceLocation(entity: Entity): { target: string; }; -// @public (undocumented) +// @public interface GroupEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -272,10 +272,10 @@ interface GroupEntityV1alpha1 extends Entity { export { GroupEntityV1alpha1 as GroupEntity }; export { GroupEntityV1alpha1 }; -// @public (undocumented) +// @public export const groupEntityV1alpha1Validator: KindValidator; -// @public (undocumented) +// @public export type JSONSchema = JSONSchema7 & { [key in string]?: JsonValue; }; @@ -305,16 +305,16 @@ export class KubernetesValidatorFunctions { static isValidObjectName(value: unknown): boolean; } -// @public (undocumented) +// @public type Location_2 = { id: string; } & LocationSpec; export { Location_2 as Location }; -// @public (undocumented) +// @public export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location'; -// @public (undocumented) +// @public interface LocationEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -330,23 +330,23 @@ interface LocationEntityV1alpha1 extends Entity { export { LocationEntityV1alpha1 as LocationEntity }; export { LocationEntityV1alpha1 }; -// @public (undocumented) +// @public export const locationEntityV1alpha1Validator: KindValidator; -// @public @deprecated (undocumented) +// @public @deprecated export const locationSchema: yup.SchemaOf; -// @public (undocumented) +// @public export type LocationSpec = { type: string; target: string; presence?: 'optional' | 'required'; }; -// @public @deprecated (undocumented) +// @public @deprecated export const locationSpecSchema: yup.SchemaOf; -// @public (undocumented) +// @public export function makeValidator(overrides?: Partial): Validators; // @public @@ -356,7 +356,7 @@ export class NoForeignRootFieldsEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } -// @public (undocumented) +// @public export const ORIGIN_LOCATION_ANNOTATION = 'backstage.io/managed-by-origin-location'; @@ -373,13 +373,9 @@ export function parseEntityRef( defaultKind: string; defaultNamespace: string; }, -): { - kind: string; - namespace: string; - name: string; -}; +): EntityName; -// @public (undocumented) +// @public export function parseEntityRef( ref: EntityRef, context?: { @@ -391,7 +387,7 @@ export function parseEntityRef( name: string; }; -// @public (undocumented) +// @public export function parseEntityRef( ref: EntityRef, context?: { @@ -409,28 +405,28 @@ export function parseLocationReference(ref: string): { target: string; }; -// @public (undocumented) +// @public export const RELATION_API_CONSUMED_BY = 'apiConsumedBy'; -// @public (undocumented) +// @public export const RELATION_API_PROVIDED_BY = 'apiProvidedBy'; -// @public (undocumented) +// @public export const RELATION_CHILD_OF = 'childOf'; // @public export const RELATION_CONSUMES_API = 'consumesApi'; -// @public (undocumented) +// @public export const RELATION_DEPENDENCY_OF = 'dependencyOf'; // @public export const RELATION_DEPENDS_ON = 'dependsOn'; -// @public (undocumented) +// @public export const RELATION_HAS_MEMBER = 'hasMember'; -// @public (undocumented) +// @public export const RELATION_HAS_PART = 'hasPart'; // @public @@ -439,7 +435,7 @@ export const RELATION_MEMBER_OF = 'memberOf'; // @public export const RELATION_OWNED_BY = 'ownedBy'; -// @public (undocumented) +// @public export const RELATION_OWNER_OF = 'ownerOf'; // @public @@ -448,10 +444,10 @@ export const RELATION_PARENT_OF = 'parentOf'; // @public export const RELATION_PART_OF = 'partOf'; -// @public (undocumented) +// @public export const RELATION_PROVIDES_API = 'providesApi'; -// @public (undocumented) +// @public interface ResourceEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -468,7 +464,7 @@ interface ResourceEntityV1alpha1 extends Entity { export { ResourceEntityV1alpha1 as ResourceEntity }; export { ResourceEntityV1alpha1 }; -// @public (undocumented) +// @public export const resourceEntityV1alpha1Validator: KindValidator; // @public @@ -488,7 +484,7 @@ export function serializeEntityRef( }, ): EntityRef; -// @public (undocumented) +// @public export const SOURCE_LOCATION_ANNOTATION = 'backstage.io/source-location'; // @public @@ -508,7 +504,7 @@ export function stringifyLocationReference(ref: { target: string; }): string; -// @public (undocumented) +// @public interface SystemEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -523,10 +519,10 @@ interface SystemEntityV1alpha1 extends Entity { export { SystemEntityV1alpha1 as SystemEntity }; export { SystemEntityV1alpha1 }; -// @public (undocumented) +// @public export const systemEntityV1alpha1Validator: KindValidator; -// @public (undocumented) +// @public export interface TemplateEntityV1beta2 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1beta2'; @@ -550,7 +546,7 @@ export interface TemplateEntityV1beta2 extends Entity { }; } -// @public (undocumented) +// @public export const templateEntityV1beta2Validator: KindValidator; // @alpha @@ -569,7 +565,7 @@ export type UNSTABLE_EntityStatusItem = { // @alpha export type UNSTABLE_EntityStatusLevel = 'info' | 'warning' | 'error'; -// @public (undocumented) +// @public interface UserEntityV1alpha1 extends Entity { // (undocumented) apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; @@ -588,10 +584,10 @@ interface UserEntityV1alpha1 extends Entity { export { UserEntityV1alpha1 as UserEntity }; export { UserEntityV1alpha1 }; -// @public (undocumented) +// @public export const userEntityV1alpha1Validator: KindValidator; -// @public (undocumented) +// @public export type Validators = { isValidApiVersion(value: unknown): boolean; isValidKind(value: unknown): boolean; @@ -609,5 +605,5 @@ export const VIEW_URL_ANNOTATION = 'backstage.io/view-url'; // Warnings were encountered during analysis: // -// src/entity/Entity.d.ts:38:5 - (ae-incompatible-release-tags) The symbol "status" is marked as @public, but its signature references "UNSTABLE_EntityStatus" which is marked as @alpha +// src/entity/Entity.d.ts:41:5 - (ae-incompatible-release-tags) The symbol "status" is marked as @public, but its signature references "UNSTABLE_EntityStatus" which is marked as @alpha ``` diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 45b947c890..69d1c34ba9 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-model", "description": "Types and validators that help describe the model of a Backstage Catalog", - "version": "0.9.4", + "version": "0.9.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/config": "^0.1.10", - "@backstage/errors": "^0.1.2", + "@backstage/errors": "^0.1.3", "@types/json-schema": "^7.0.5", "@types/yup": "^0.29.13", "ajv": "^7.0.3", @@ -41,7 +41,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.7.15", + "@backstage/cli": "^0.8.0", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", "yaml": "^1.9.2" diff --git a/packages/catalog-model/src/EntityPolicies.ts b/packages/catalog-model/src/EntityPolicies.ts index 0fff68d569..e5aaeb0c20 100644 --- a/packages/catalog-model/src/EntityPolicies.ts +++ b/packages/catalog-model/src/EntityPolicies.ts @@ -52,7 +52,11 @@ class AnyEntityPolicy implements EntityPolicy { } } -/** @public */ +/** + * Provides helpers for enforcing a set of {@link EntityPolicy} in an `and`/`or` expression. + * + * @public + */ export const EntityPolicies = { allOf(policies: EntityPolicy[]): EntityPolicy { return new AllEntityPolicies(policies); diff --git a/packages/catalog-model/src/entity/Entity.ts b/packages/catalog-model/src/entity/Entity.ts index ca38fef183..7a4729c309 100644 --- a/packages/catalog-model/src/entity/Entity.ts +++ b/packages/catalog-model/src/entity/Entity.ts @@ -21,8 +21,11 @@ import { UNSTABLE_EntityStatus } from './EntityStatus'; /** * The parts of the format that's common to all versions/kinds of entity. * + * @remarks + * + * See also: + * {@link https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/} * @public - * @see https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/ */ export type Entity = { /** @@ -63,9 +66,13 @@ export type Entity = { /** * Metadata fields common to all versions/kinds of entity. * + * @remarks + * + * See also: + * {@link https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta} + * {@link https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/} + * * @public - * @see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta - * @see https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/ */ export type EntityMeta = JsonObject & { /** diff --git a/packages/catalog-model/src/entity/constants.ts b/packages/catalog-model/src/entity/constants.ts index ff031fc1ae..6daf6b78d9 100644 --- a/packages/catalog-model/src/entity/constants.ts +++ b/packages/catalog-model/src/entity/constants.ts @@ -33,10 +33,15 @@ export const ENTITY_META_GENERATED_FIELDS = [ ] as const; /** - * Annotations for linking to entity from catalog pages. + * Annotation for linking to entity page from catalog pages. * * @public */ export const VIEW_URL_ANNOTATION = 'backstage.io/view-url'; -/** @public */ + +/** + * Annotation for linking to entity edit page from catalog pages. + * + * @public + */ export const EDIT_URL_ANNOTATION = 'backstage.io/edit-url'; diff --git a/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts b/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts index b99901b89b..51059328a6 100644 --- a/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts +++ b/packages/catalog-model/src/entity/policies/FieldFormatEntityPolicy.ts @@ -27,6 +27,8 @@ import { Entity } from '../Entity'; * Ensures that the format of individual fields of the entity envelope * is valid. * + * @remarks + * * This does not take into account machine generated fields such as uid, etag * and generation. * diff --git a/packages/catalog-model/src/entity/policies/SchemaValidEntityPolicy.ts b/packages/catalog-model/src/entity/policies/SchemaValidEntityPolicy.ts index 721dc7d2dc..7f33e71f1c 100644 --- a/packages/catalog-model/src/entity/policies/SchemaValidEntityPolicy.ts +++ b/packages/catalog-model/src/entity/policies/SchemaValidEntityPolicy.ts @@ -24,6 +24,8 @@ import { EntityPolicy } from './types'; /** * Ensures that the entity spec is valid according to a schema. * + * @remarks + * * This should be the first policy in the list, to ensure that other downstream * policies can work with a structure that is at least valid in therms of the * typescript type. diff --git a/packages/catalog-model/src/entity/ref.ts b/packages/catalog-model/src/entity/ref.ts index 6a38e80dfe..958f750e32 100644 --- a/packages/catalog-model/src/entity/ref.ts +++ b/packages/catalog-model/src/entity/ref.ts @@ -69,6 +69,8 @@ export type EntityRefContext = { * Parses an entity reference, either on string or compound form, and always * returns a complete entity name including kind, namespace and name. * + * @remarks + * * This function automatically assumes the default namespace "default" unless * otherwise specified as part of the options, and will throw an error if no * kind was specified in the input reference and no default kind was given. @@ -100,7 +102,9 @@ export function parseEntityName( * Parses an entity reference, either on string or compound form, and returns * a structure with a name, and optional kind and namespace. * - * The options object can contain default values for the kind and namespace, + * @remarks + * + * The context object can contain default values for the kind and namespace, * that will be used if the input reference did not specify any. * * @public @@ -111,12 +115,12 @@ export function parseEntityName( export function parseEntityRef( ref: EntityRef, context?: { defaultKind: string; defaultNamespace: string }, -): { - kind: string; - namespace: string; - name: string; -}; -/** @public */ +): EntityName; +/** + * parseEntityRef with optional Kind. + * + * @public + */ export function parseEntityRef( ref: EntityRef, context?: { defaultKind: string }, @@ -125,7 +129,11 @@ export function parseEntityRef( namespace?: string; name: string; }; -/** @public */ +/** + * parseEntityRef with optional Namespace. + * + * @public + */ export function parseEntityRef( ref: EntityRef, context?: { defaultNamespace: string }, @@ -134,6 +142,11 @@ export function parseEntityRef( namespace: string; name: string; }; +/** + * parseEntityRef with optional Kind and Namespace. + * + * @public + */ export function parseEntityRef( ref: EntityRef, context: EntityRefContext = {}, @@ -223,6 +236,8 @@ export function serializeEntityRef( * Takes an entity or entity name/reference, and returns the string form of an * entity ref. * + * @remarks + * * This function creates a canonical and unique reference to the entity, converting * all parts of the name to lowercase and inserts the default namespace if needed. * It is typically not the best way to represent the entity reference to the user. @@ -256,6 +271,8 @@ export function stringifyEntityRef( /** * Compares an entity to either a string reference or a compound reference. * + * @remarks + * * The comparison is case insensitive, and all of kind, namespace, and name * must match (after applying the optional context to the ref). * diff --git a/packages/catalog-model/src/entity/util.ts b/packages/catalog-model/src/entity/util.ts index 314f3b7737..ce6415afde 100644 --- a/packages/catalog-model/src/entity/util.ts +++ b/packages/catalog-model/src/entity/util.ts @@ -43,6 +43,8 @@ export function generateEntityEtag(): string { * Checks whether there are any significant changes going from the previous to * the next version of this entity. * + * @remarks + * * Significance, in this case, means that we do not compare generated fields * such as uid, etag and generation. * @@ -98,6 +100,8 @@ export function entityHasChanges(previous: Entity, next: Entity): boolean { * Takes an old revision of an entity and a new desired state, and merges * them into a complete new state. * + * @remarks + * * The previous revision is expected to be a complete model loaded from the * catalog, including the uid, etag and generation fields. * diff --git a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts index 310b6695eb..d733b03c8c 100644 --- a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts @@ -18,7 +18,15 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/API.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage API kind Entity. APIs describe the interfaces for Components to communicate. + * + * @remarks + * + * See {@link https://backstage.io/docs/features/software-catalog/system-model} + * + * @public + */ export interface ApiEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'API'; @@ -31,6 +39,10 @@ export interface ApiEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link ApiEntityV1alpha1}. + * + * @public + */ export const apiEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts index 6777778409..f931eab553 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts @@ -18,7 +18,15 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Component.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog Component kind Entity. Represents a single, individual piece of software. + * + * @remarks + * + * See {@link https://backstage.io/docs/features/software-catalog/system-model} + * + * @public + */ export interface ComponentEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'Component'; @@ -34,6 +42,10 @@ export interface ComponentEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link ComponentEntityV1alpha1}. + * + * @public + */ export const componentEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts index fe777af24e..e0302fcc52 100644 --- a/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts @@ -18,7 +18,15 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Domain.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage Domain kind Entity. Domains group Systems together. + * + * @remarks + * + * See {@link https://backstage.io/docs/features/software-catalog/system-model} + * + * @public + */ export interface DomainEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'Domain'; @@ -27,6 +35,10 @@ export interface DomainEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link DomainEntityV1alpha1}. + * + * @public + */ export const domainEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/GroupEntityV1alpha1.ts b/packages/catalog-model/src/kinds/GroupEntityV1alpha1.ts index f6aab856f0..8d88817dbe 100644 --- a/packages/catalog-model/src/kinds/GroupEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/GroupEntityV1alpha1.ts @@ -18,7 +18,11 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Group.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog Group kind Entity. + * + * @public + */ export interface GroupEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'Group'; @@ -35,6 +39,9 @@ export interface GroupEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link GroupEntityV1alpha1}. + * @public + */ export const groupEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts index f2ae4bcc91..1872e43526 100644 --- a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts @@ -18,7 +18,11 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Location.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog Location kind Entity. + * + * @public + */ export interface LocationEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'Location'; @@ -29,6 +33,10 @@ export interface LocationEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link LocationEntityV1alpha1}. + * + * @public + */ export const locationEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts index acc6ab5245..0a360cb60b 100644 --- a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts @@ -18,7 +18,15 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Resource.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog Resource kind Entity. Represents infrastructure required to operate Components. + * + * @remarks + * + * See {@link https://backstage.io/docs/features/software-catalog/system-model} + * + * @public + */ export interface ResourceEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'Resource'; @@ -30,6 +38,10 @@ export interface ResourceEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link ResourceEntityV1alpha1}. + * + * @public + */ export const resourceEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts index 5f90351480..7d1ab703a2 100644 --- a/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts @@ -18,7 +18,15 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/System.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog System kind Entity. Systems group Comopnents, Resources and APIs together. + * + * @remarks + * + * See {@link https://backstage.io/docs/features/software-catalog/system-model} + * + * @public + */ export interface SystemEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'System'; @@ -28,6 +36,10 @@ export interface SystemEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link SystemEntityV1alpha1}. + * + * @public + */ export const systemEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts index e708d4b387..1b1966d05d 100644 --- a/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts +++ b/packages/catalog-model/src/kinds/TemplateEntityV1beta2.ts @@ -19,7 +19,11 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/Template.v1beta2.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog Template kind Entity. Templates are used by the Scaffolder plugin to create new Components. + * + * @public + */ export interface TemplateEntityV1beta2 extends Entity { apiVersion: 'backstage.io/v1beta2'; kind: 'Template'; @@ -38,6 +42,10 @@ export interface TemplateEntityV1beta2 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link TemplateEntityV1beta2}. + * + * @public + */ export const templateEntityV1beta2Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/UserEntityV1alpha1.ts b/packages/catalog-model/src/kinds/UserEntityV1alpha1.ts index d719ab35ef..82aaebd9d6 100644 --- a/packages/catalog-model/src/kinds/UserEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/UserEntityV1alpha1.ts @@ -18,7 +18,11 @@ import type { Entity } from '../entity/Entity'; import schema from '../schema/kinds/User.v1alpha1.schema.json'; import { ajvCompiledJsonSchemaValidator } from './util'; -/** @public */ +/** + * Backstage catalog User kind Entity. + * + * @public + */ export interface UserEntityV1alpha1 extends Entity { apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; kind: 'User'; @@ -32,6 +36,10 @@ export interface UserEntityV1alpha1 extends Entity { }; } -/** @public */ +/** + * {@link KindValidator} for {@link UserEntityV1alpha1}. + * + * @public + */ export const userEntityV1alpha1Validator = ajvCompiledJsonSchemaValidator(schema); diff --git a/packages/catalog-model/src/kinds/relations.ts b/packages/catalog-model/src/kinds/relations.ts index 7546d1508b..4d545c9d35 100644 --- a/packages/catalog-model/src/kinds/relations.ts +++ b/packages/catalog-model/src/kinds/relations.ts @@ -24,61 +24,115 @@ Naming rules for relations in priority order: /** * An ownership relation where the owner is usually an organizational - * entity (user or group), and the other entity can be anything. + * entity (user or group), and the other entity can be anything. Reversed + * direction of {@link RELATION_OWNER_OF}. * * @public */ export const RELATION_OWNED_BY = 'ownedBy'; -/** @public */ + +/** + * A relationship from an owner to the owned entity. Reversed direction of + * {@link RELATION_OWNED_BY}. + * + * @public + */ export const RELATION_OWNER_OF = 'ownerOf'; /** - * A relation with an API entity, typically from a component + * A relation with an API entity, typically from a component. Reversed direction of + * {@link RELATION_API_CONSUMED_BY}. * * @public */ export const RELATION_CONSUMES_API = 'consumesApi'; -/** @public */ + +/** + * A relation of an API being consumed, typically by a component. Reversed direction of + * {@link RELATION_CONSUMES_API}. + * + * @public + */ export const RELATION_API_CONSUMED_BY = 'apiConsumedBy'; -/** @public */ + +/** + * A relation from an API provider entity (typically a component) to the API. Reversed direction of + * {@link RELATION_API_PROVIDED_BY}. + * + * @public + */ export const RELATION_PROVIDES_API = 'providesApi'; -/** @public */ + +/** + * A relation from an API to its provider entity (typically a component). Reversed direction of + * {@link RELATION_PROVIDES_API}. + * + * @public + */ export const RELATION_API_PROVIDED_BY = 'apiProvidedBy'; /** - * A relation denoting a dependency on another entity. + * A relation denoting a dependency on another entity. Reversed direction of + * {@link RELATION_DEPENDENCY_OF}. * * @public */ export const RELATION_DEPENDS_ON = 'dependsOn'; -/** @public */ + +/** + * A relation denoting a reverse dependency by another entity. Reversed direction of + * {@link RELATION_DEPENDS_ON}. + * + * @public + */ export const RELATION_DEPENDENCY_OF = 'dependencyOf'; /** * A parent/child relation to build up a tree, used for example to describe - * the organizational structure between groups. + * the organizational structure between groups. Reversed direction of + * {@link RELATION_CHILD_OF}. * * @public */ export const RELATION_PARENT_OF = 'parentOf'; -/** @public */ + +/** + * A relation from a child to a parent entity, used for example to describe + * the organizational structure between groups. Reversed direction of + * {@link RELATION_PARENT_OF}. + * + * @public + */ export const RELATION_CHILD_OF = 'childOf'; /** - * A membership relation, typically for users in a group. + * A membership relation, typically for users in a group. Reversed direction of + * {@link RELATION_HAS_MEMBER}. * * @public */ export const RELATION_MEMBER_OF = 'memberOf'; -/** @public */ + +/** + * A relation from a group to its member, typcally a user in a group. Reversed direction of + * {@link RELATION_MEMBER_OF}. + * + * @public + */ export const RELATION_HAS_MEMBER = 'hasMember'; /** * A part/whole relation, typically for components in a system and systems - * in a domain. + * in a domain. Reversed direction of {@link RELATION_HAS_PART}. * * @public */ export const RELATION_PART_OF = 'partOf'; -/** @public */ + +/** + * A relation from a containing entity to a contained entity. Reversed direction of + * {@link RELATION_PART_OF}. + * + * @public + */ export const RELATION_HAS_PART = 'hasPart'; diff --git a/packages/catalog-model/src/location/annotation.ts b/packages/catalog-model/src/location/annotation.ts index 4bcabdb705..8eb393f555 100644 --- a/packages/catalog-model/src/location/annotation.ts +++ b/packages/catalog-model/src/location/annotation.ts @@ -14,11 +14,20 @@ * limitations under the License. */ -/** @public */ +/** + * Constant storing location annotation. + * + * @public */ export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location'; -/** @public */ +/** + * Constant storing origin location annotation + * + * @public */ export const ORIGIN_LOCATION_ANNOTATION = 'backstage.io/managed-by-origin-location'; -/** @public */ +/** + * Contant storing source location annotation + * + * @public */ export const SOURCE_LOCATION_ANNOTATION = 'backstage.io/source-location'; diff --git a/packages/catalog-model/src/location/helpers.ts b/packages/catalog-model/src/location/helpers.ts index cf12031dc1..1edd4e5ec5 100644 --- a/packages/catalog-model/src/location/helpers.ts +++ b/packages/catalog-model/src/location/helpers.ts @@ -65,6 +65,8 @@ export function parseLocationReference(ref: string): { /** * Turns a location reference into its string form. * + * @remarks + * * Note that the input type is not `LocationSpec`, because we do not want to * conflate the string form with the additional properties of that type. * @@ -90,6 +92,8 @@ export function stringifyLocationReference(ref: { /** * Returns the source code location of the Entity, to the extent that one exists. * + * @remarks + * * If the returned location type is of type 'url', the target should be readable at least * using the UrlReader from `@backstage/backend-common`. If it is not of type 'url', the caller * needs to have explicit handling of each location type or signal that it is not supported. diff --git a/packages/catalog-model/src/location/types.ts b/packages/catalog-model/src/location/types.ts index 1d16a9047d..725f873f2d 100644 --- a/packages/catalog-model/src/location/types.ts +++ b/packages/catalog-model/src/location/types.ts @@ -14,17 +14,28 @@ * limitations under the License. */ -/** @public */ +/** + * Holds the entity location information. + * + * @remarks + * + * `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch. + * This flag is then set to indicate that the file can be not present. + * default value: 'required'. + * + * @public + */ export type LocationSpec = { type: string; target: string; - // When using repo importer plugin, location is being created before the component yaml file is merged to the main branch. - // This flag is then set to indicate that the file can be not present. - // default value: 'required'. presence?: 'optional' | 'required'; }; -/** @public */ +/** + * Entity location for a specific entity. + * + * @public + */ export type Location = { id: string; } & LocationSpec; diff --git a/packages/catalog-model/src/location/validation.ts b/packages/catalog-model/src/location/validation.ts index 452c69af53..2fd692e649 100644 --- a/packages/catalog-model/src/location/validation.ts +++ b/packages/catalog-model/src/location/validation.ts @@ -18,8 +18,10 @@ import * as yup from 'yup'; import { LocationSpec, Location } from './types'; /** + * Deprecated. + * * @public - * @deprecated Use JSONSchema or validators instead. + * @deprecated Use {@link JSONSchema} or validators instead. */ export const locationSpecSchema: yup.SchemaOf = yup .object({ @@ -31,8 +33,10 @@ export const locationSpecSchema: yup.SchemaOf = yup .required(); /** + * Deprecated. + * * @public - * @deprecated Use JSONSchema or validators instead. + * @deprecated Use {@link JSONSchema} or validators instead. */ export const locationSchema: yup.SchemaOf = yup .object({ @@ -45,8 +49,10 @@ export const locationSchema: yup.SchemaOf = yup .required(); /** + * Deprecated. + * * @public - * @deprecated Use JSONSchema or validators instead. + * @deprecated Use {@link JSONSchema} or validators instead. */ export const analyzeLocationSchema: yup.SchemaOf<{ location: LocationSpec }> = yup diff --git a/packages/catalog-model/src/types.ts b/packages/catalog-model/src/types.ts index 1fbb3be95c..8ec1303a40 100644 --- a/packages/catalog-model/src/types.ts +++ b/packages/catalog-model/src/types.ts @@ -17,7 +17,11 @@ import { JsonValue } from '@backstage/config'; import { JSONSchema7 } from 'json-schema'; -/** @public */ +/** + * JSONSchema extendable by arbitrary JSON attributes + * + * @public + */ export type JSONSchema = JSONSchema7 & { [key in string]?: JsonValue }; /** @@ -35,7 +39,9 @@ export type EntityName = { * A reference by name to an entity, either as a compact string representation, * or as a compound reference structure. * - * The string representation is on the form [:][/]. + * @remarks + * + * The string representation is on the form `[:][/]`. * * Left-out parts of the reference need to be handled by the application, * either by rejecting the reference or by falling back to default values. diff --git a/packages/catalog-model/src/validation/entityEnvelopeSchemaValidator.ts b/packages/catalog-model/src/validation/entityEnvelopeSchemaValidator.ts index daa44ffaf1..a2a8ba64b7 100644 --- a/packages/catalog-model/src/validation/entityEnvelopeSchemaValidator.ts +++ b/packages/catalog-model/src/validation/entityEnvelopeSchemaValidator.ts @@ -25,6 +25,8 @@ import { compileAjvSchema, throwAjvError } from './ajv'; * if it matches that schema, or throws a {@link globals#TypeError} describing the * errors. * + * @remarks + * * Note that this validator is only meant for applying the base schema checks; * it does not take custom policies or additional processor based validation * into account. @@ -33,13 +35,15 @@ import { compileAjvSchema, throwAjvError } from './ajv'; * own, it may contain `$ref` references to the following, which are resolved * automatically for you: * - * - EntityEnvelope - * - Entity - * - EntityMeta - * - common# + * - {@link EntityEnvelope} + * - {@link Entity} + * - {@link EntityMeta} + * - `common#` + * + * See also {@link https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema} * * @public - * @see https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema + * */ export function entityEnvelopeSchemaValidator< T extends EntityEnvelope = EntityEnvelope, diff --git a/packages/catalog-model/src/validation/entityKindSchemaValidator.ts b/packages/catalog-model/src/validation/entityKindSchemaValidator.ts index 535dba9842..66259cc63d 100644 --- a/packages/catalog-model/src/validation/entityKindSchemaValidator.ts +++ b/packages/catalog-model/src/validation/entityKindSchemaValidator.ts @@ -24,6 +24,8 @@ import { compileAjvSchema, throwAjvError } from './ajv'; * schema apiVersion/kind didn't apply to that data, or throws a * {@link globals#TypeError} describing actual errors. * + * @remarks + * * This validator is highly specialized, in that it has special treatment of * the `kind` and `apiVersion` root keys. This only works if your schema has * their rule set to `"enum"`: @@ -47,13 +49,16 @@ import { compileAjvSchema, throwAjvError } from './ajv'; * The given schema may contain `$ref` references to the following, which are * resolved automatically for you: * - * - EntityEnvelope - * - Entity - * - EntityMeta - * - common# + * - {@link Entity} + * + * - {@link EntityEnvelope} + * + * - {@link EntityMeta} + * + * - `common#` + * @see {@link https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema} * * @public - * @see https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema */ export function entityKindSchemaValidator( schema: unknown, diff --git a/packages/catalog-model/src/validation/entitySchemaValidator.ts b/packages/catalog-model/src/validation/entitySchemaValidator.ts index dd110efd75..fc1918e566 100644 --- a/packages/catalog-model/src/validation/entitySchemaValidator.ts +++ b/packages/catalog-model/src/validation/entitySchemaValidator.ts @@ -24,21 +24,23 @@ import { compileAjvSchema, throwAjvError } from './ajv'; * returns that data cast to an {@link Entity} (or the given subtype) if it * matches that schema, or throws a {@link globals#TypeError} describing the errors. * + * @remarks + * * Note that this validator is only meant for applying the base schema checks; * it does not take custom policies or additional processor based validation * into account. * - * By default, the plain `Entity` schema is used. If you pass in your own, it + * By default, the plain {@link Entity} schema is used. If you pass in your own, it * may contain `$ref` references to the following, which are resolved * automatically for you: * - * - EntityEnvelope - * - Entity - * - EntityMeta - * - common# + * - {@link Entity} + * - {@link EntityEnvelope} + * - {@link EntityMeta} + * - `common#` * * @public - * @see https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema + * @see {@link https://github.com/backstage/backstage/tree/master/packages/catalog-model/src/schema} */ export function entitySchemaValidator( schema?: unknown, diff --git a/packages/catalog-model/src/validation/makeValidator.ts b/packages/catalog-model/src/validation/makeValidator.ts index bd6dccc419..ad3f52e563 100644 --- a/packages/catalog-model/src/validation/makeValidator.ts +++ b/packages/catalog-model/src/validation/makeValidator.ts @@ -30,7 +30,11 @@ const defaultValidators: Validators = { isValidTag: CommonValidatorFunctions.isValidTag, }; -/** @public */ +/** + * Creates a {@link Validators} object from `overrides`, with default values taken from {@link KubernetesValidatorFunctions} + * + * @public + */ export function makeValidator(overrides: Partial = {}): Validators { return { ...defaultValidators, diff --git a/packages/catalog-model/src/validation/types.ts b/packages/catalog-model/src/validation/types.ts index 23d639c166..e3615e6a83 100644 --- a/packages/catalog-model/src/validation/types.ts +++ b/packages/catalog-model/src/validation/types.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -/** @public */ +/** + * Type alias for implementing validators of various entity objects. + * + * @public + */ export type Validators = { isValidApiVersion(value: unknown): boolean; isValidKind(value: unknown): boolean; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index fce803b3cc..7cb3c5be91 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/cli +## 0.8.0 + +### Minor Changes + +- b486adb8c6: The Jest configuration that's included with the Backstage CLI has received several changes. + + As a part of migrating to more widespread usage of ESM modules, the default configuration now transforms all source files everywhere, including those within `node_modules`. Due to this change the existing `transformModules` option has been removed and will be ignored. There is also a list of known packages that do not require transforms in the CLI, which will evolve over time. If needed there will also be an option to add packages to this list in the future, but it is not included yet to avoid clutter. + + To counteract the slowdown of the additional transforms that have been introduced, the default configuration has also been reworked to enable caching across different packages. Previously each package in a Backstage monorepo would have its own isolated Jest cache, but it is now shared between packages that have a similar enough Jest configuration. + + Another change that will speed up test execution is that the transformer for `.esm.js` files has been switched. It used to be an ESM transformer based on Babel, but it is also done by sucrase now since it is significantly faster. + + The changes above are not strictly breaking as all tests should still work. It may however cause excessive slowdowns in projects that have configured custom transforms in the `jest` field within `package.json` files. In this case it is either best to consider removing the custom transforms, or overriding the `transformIgnorePatterns` to instead use Jest's default `'/node_modules/'` pattern. + + This change also removes the `@backstage/cli/config/jestEsmTransform.js` transform, which can be replaced by using the `@backstage/cli/config/sucraseEsmTransform.js` transform instead. + +### Patch Changes + +- 36e67d2f24: Internal updates to apply more strict checks to throw errors. +- Updated dependencies + - @backstage/config-loader@0.7.0 + - @backstage/errors@0.1.3 + ## 0.7.16 ### Patch Changes diff --git a/packages/cli/config/jestEsmTransform.js b/packages/cli/config/jestEsmTransform.js new file mode 100644 index 0000000000..c1eec7d9f8 --- /dev/null +++ b/packages/cli/config/jestEsmTransform.js @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +throw new Error( + 'The ./jestEsmTransform.js transform has been removed, switch to using ./jestSucraseTransform.js instead', +); diff --git a/packages/cli/package.json b/packages/cli/package.json index ecc3d3b4e1..da9e161fef 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.7.16", + "version": "0.8.0", "private": false, "publishConfig": { "access": "public" @@ -30,7 +30,8 @@ "dependencies": { "@backstage/cli-common": "^0.1.4", "@backstage/config": "^0.1.10", - "@backstage/config-loader": "^0.6.10", + "@backstage/config-loader": "^0.7.0", + "@backstage/errors": "^0.1.3", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -115,11 +116,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.6", + "@backstage/backend-common": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.7.0", - "@backstage/core-plugin-api": "^0.1.10", - "@backstage/core-app-api": "^0.1.17", + "@backstage/core-components": "^0.7.1", + "@backstage/core-plugin-api": "^0.1.11", + "@backstage/core-app-api": "^0.1.18", "@backstage/dev-utils": "^0.2.12", "@backstage/test-utils": "^0.1.19", "@backstage/theme": "^0.2.11", diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 6ddd1f7160..a5ea565cae 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -24,6 +24,7 @@ import camelCase from 'lodash/camelCase'; import upperFirst from 'lodash/upperFirst'; import os from 'os'; import { Command } from 'commander'; +import { assertError } from '@backstage/errors'; import { parseOwnerIds, addCodeownersEntry, @@ -173,6 +174,7 @@ async function buildPlugin(pluginFolder: string) { ); }); } catch (error) { + assertError(error); Task.error(error.message); break; } @@ -329,6 +331,7 @@ export default async (cmd: Command) => { Task.log(); Task.exit(); } catch (error) { + assertError(error); Task.error(error.message); Task.log('It seems that something went wrong when creating the plugin 🤔'); diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index d6686f2655..302f76cc5d 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { assertError } from '@backstage/errors'; import { CommanderStatic } from 'commander'; import { exitWithError } from '../lib/errors'; @@ -252,6 +253,7 @@ function lazy( process.exit(0); } catch (error) { + assertError(error); exitWithError(error); } }; diff --git a/packages/cli/src/commands/remove-plugin/removePlugin.ts b/packages/cli/src/commands/remove-plugin/removePlugin.ts index 9e1e18bb7b..11fdc8bb11 100644 --- a/packages/cli/src/commands/remove-plugin/removePlugin.ts +++ b/packages/cli/src/commands/remove-plugin/removePlugin.ts @@ -20,6 +20,7 @@ import inquirer, { Answers, Question } from 'inquirer'; import { getCodeownersFilePath } from '../../lib/codeowners'; import { paths } from '../../lib/paths'; import { Task } from '../../lib/tasks'; +import { assertError } from '@backstage/errors'; const BACKSTAGE = '@backstage'; @@ -35,6 +36,7 @@ export const checkExists = async (rootDir: string, pluginName: string) => { ); } } catch (e) { + assertError(e); throw new Error( chalk.red( ` There was an error removing plugin ${chalk.cyan(pluginName)}: ${ @@ -51,6 +53,7 @@ export const removePluginDirectory = async (destination: string) => { try { await fse.remove(destination); } catch (e) { + assertError(e); throw Error( chalk.red( ` There was a problem removing the plugin directory: ${e.message}`, @@ -67,6 +70,7 @@ export const removeSymLink = async (destination: string) => { try { await fse.remove(destination); } catch (e) { + assertError(e); throw Error( chalk.red( ` Could not remove symbolic link\t${chalk.cyan(destination)}: ${ @@ -106,6 +110,7 @@ export const removeReferencesFromPluginsFile = async ( try { await removeAllStatementsContainingID(pluginsFile, pluginNameCapitalized); } catch (e) { + assertError(e); throw new Error( chalk.red( ` There was an error removing export statement for plugin ${chalk.cyan( @@ -125,6 +130,7 @@ export const removePluginFromCodeOwners = async ( try { await removeAllStatementsContainingID(codeOwnersFile, pluginName); } catch (e) { + assertError(e); throw new Error( chalk.red( ` There was an error removing code owners statement for plugin ${chalk.cyan( @@ -165,6 +171,7 @@ export const removeReferencesFromAppPackage = async ( 'utf-8', ); } catch (e) { + assertError(e); throw new Error( chalk.red( ` Failed to remove plugin as dependency in app: ${chalk.cyan( @@ -245,6 +252,7 @@ export default async () => { ); Task.log(); } catch (error) { + assertError(error); Task.error(error.message); Task.log('It seems that something went wrong when removing the plugin 🤔'); } diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 257a83e2be..7b967839b2 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -17,6 +17,7 @@ import fs from 'fs-extra'; import chalk from 'chalk'; import semver from 'semver'; +import { isError } from '@backstage/errors'; import { resolve as resolvePath } from 'path'; import { run } from '../../lib/run'; import { paths } from '../../lib/paths'; @@ -59,7 +60,7 @@ export default async () => { try { target = await findTargetVersion(name); } catch (error) { - if (error.name === 'NotFoundError') { + if (isError(error) && error.name === 'NotFoundError') { console.log(`Package info not found, ignoring package ${name}`); return; } @@ -97,7 +98,7 @@ export default async () => { try { target = await findTargetVersion(name); } catch (error) { - if (error.name === 'NotFoundError') { + if (isError(error) && error.name === 'NotFoundError') { console.log(`Package info not found, ignoring package ${name}`); return; } diff --git a/packages/cli/src/lib/run.ts b/packages/cli/src/lib/run.ts index 7cf22a6df9..4efb791c30 100644 --- a/packages/cli/src/lib/run.ts +++ b/packages/cli/src/lib/run.ts @@ -23,6 +23,7 @@ import { import { ExitCodeError } from './errors'; import { promisify } from 'util'; import { LogFunc } from './logging'; +import { assertError, ForwardedError } from '@backstage/errors'; const execFile = promisify(execFileCb); @@ -75,10 +76,14 @@ export async function runPlain(cmd: string, ...args: string[]) { const { stdout } = await execFile(cmd, args, { shell: true }); return stdout.trim(); } catch (error) { - if (error.stderr) { - process.stderr.write(error.stderr); + assertError(error); + if ('stderr' in error) { + process.stderr.write(error.stderr as Buffer); } - throw new ExitCodeError(error.code, [cmd, ...args].join(' ')); + if (typeof error.code === 'number') { + throw new ExitCodeError(error.code, [cmd, ...args].join(' ')); + } + throw new ForwardedError('Unknown execution error', error); } } diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index b22d5c311c..7aabaef583 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/codemods +## 0.1.19 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.7.1 + - @backstage/core-app-api@0.1.18 + - @backstage/core-plugin-api@0.1.11 + ## 0.1.18 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 47b97edbe8..77bdd181be 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.18", + "version": "0.1.19", "private": false, "publishConfig": { "access": "public", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 34fc34b441..0b4ef9a5f3 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/config-loader +## 0.7.0 + +### Minor Changes + +- 7e97d0b8c1: Removed the `EnvFunc` public export. Its only usage was to be passed in to `LoadConfigOptions.experimentalEnvFunc`. If you were using this type, add a definition in your own project instead with the signature `(name: string) => Promise`. + +### Patch Changes + +- 223e8de6b4: Configuration schema errors are now filtered using the provided visibility option. This means that schema errors due to missing backend configuration will no longer break frontend builds. +- 7e97d0b8c1: Add public tags and documentation +- 36e67d2f24: Internal updates to apply more strict checks to throw errors. +- Updated dependencies + - @backstage/errors@0.1.3 + ## 0.6.10 ### Patch Changes diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index 355839c5af..b47119d470 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -26,18 +26,15 @@ export type ConfigSchemaProcessingOptions = { // @public export type ConfigVisibility = 'frontend' | 'backend' | 'secret'; -// @public (undocumented) -export type EnvFunc = (name: string) => Promise; - // @public export function loadConfig(options: LoadConfigOptions): Promise; -// @public (undocumented) +// @public export type LoadConfigOptions = { configRoot: string; configPaths: string[]; env?: string; - experimentalEnvFunc?: EnvFunc; + experimentalEnvFunc?: (name: string) => Promise; watch?: { onChange: (configs: AppConfig[]) => void; stopSignal?: Promise; @@ -49,7 +46,7 @@ export function loadConfigSchema( options: LoadConfigSchemaOptions, ): Promise; -// @public (undocumented) +// @public export type LoadConfigSchemaOptions = | { dependencies: string[]; diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index a719c6389b..03ea61abe2 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.6.10", + "version": "0.7.0", "private": false, "publishConfig": { "access": "public", @@ -32,6 +32,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.4", "@backstage/config": "^0.1.9", + "@backstage/errors": "^0.1.3", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "chokidar": "^3.5.2", diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index 0e66c59ec4..888c68ef70 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -25,7 +25,6 @@ export type { ConfigSchema, ConfigSchemaProcessingOptions, ConfigVisibility, - EnvFunc, LoadConfigSchemaOptions, TransformFunc, } from './lib'; diff --git a/packages/config-loader/src/lib/env.ts b/packages/config-loader/src/lib/env.ts index b244c06c64..57cfc077a7 100644 --- a/packages/config-loader/src/lib/env.ts +++ b/packages/config-loader/src/lib/env.ts @@ -15,6 +15,7 @@ */ import { AppConfig, JsonObject } from '@backstage/config'; +import { assertError } from '@backstage/errors'; const ENV_PREFIX = 'APP_CONFIG_'; @@ -96,6 +97,7 @@ function safeJsonParse(str: string): [Error | null, any] { try { return [null, JSON.parse(str)]; } catch (err) { + assertError(err); return [err, str]; } } diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index f8b134119b..de6aff376f 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -24,6 +24,7 @@ import { import { ConfigSchemaPackageEntry } from './types'; import { getProgramFromFiles, generateSchema } from 'typescript-json-schema'; import { JsonObject } from '@backstage/config'; +import { assertError } from '@backstage/errors'; type Item = { name?: string; @@ -189,6 +190,7 @@ function compileTsSchemas(paths: string[]) { [path.split(sep).join('/')], // Unix paths are expected for all OSes here ) as JsonObject | null; } catch (error) { + assertError(error); if (error.message !== 'type Config not found') { throw error; } diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 0e9ddabcb3..f3ea0b900e 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -25,7 +25,11 @@ import { CONFIG_VISIBILITIES, } from './types'; -/** @public */ +/** + * Options that control the loading of configuration schema files in the backend. + * + * @public + */ export type LoadConfigSchemaOptions = | { dependencies: string[]; diff --git a/packages/config-loader/src/lib/transform/apply.ts b/packages/config-loader/src/lib/transform/apply.ts index 690d280266..091f9c23c5 100644 --- a/packages/config-loader/src/lib/transform/apply.ts +++ b/packages/config-loader/src/lib/transform/apply.ts @@ -15,6 +15,7 @@ */ import { JsonObject, JsonValue } from '@backstage/config'; +import { assertError } from '@backstage/errors'; import { TransformFunc } from './types'; import { isObject } from './utils'; @@ -46,6 +47,7 @@ export async function applyConfigTransforms( break; } } catch (error) { + assertError(error); throw new Error(`error at ${path}, ${error.message}`); } } diff --git a/packages/config-loader/src/lib/transform/index.ts b/packages/config-loader/src/lib/transform/index.ts index eaa85cca3c..5053cd7443 100644 --- a/packages/config-loader/src/lib/transform/index.ts +++ b/packages/config-loader/src/lib/transform/index.ts @@ -17,4 +17,3 @@ export { applyConfigTransforms } from './apply'; export { createIncludeTransform } from './include'; export { createSubstitutionTransform } from './substitution'; -export type { EnvFunc } from './types'; diff --git a/packages/config-loader/src/lib/transform/types.ts b/packages/config-loader/src/lib/transform/types.ts index afd3550984..20e5f88718 100644 --- a/packages/config-loader/src/lib/transform/types.ts +++ b/packages/config-loader/src/lib/transform/types.ts @@ -16,7 +16,6 @@ import { JsonValue } from '@backstage/config'; -/** @public */ export type EnvFunc = (name: string) => Promise; export type ReadFileFunc = (path: string) => Promise; diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 315b44c4ee..6803d61786 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -19,15 +19,19 @@ import yaml from 'yaml'; import chokidar from 'chokidar'; import { resolve as resolvePath, dirname, isAbsolute, basename } from 'path'; import { AppConfig } from '@backstage/config'; +import { ForwardedError } from '@backstage/errors'; import { applyConfigTransforms, readEnvConfig, createIncludeTransform, createSubstitutionTransform, } from './lib'; -import { EnvFunc } from './lib/transform/types'; -/** @public */ +/** + * Options that control the loading of configuration files in the backend. + * + * @public + */ export type LoadConfigOptions = { // The root directory of the config loading context. Used to find default configs. configRoot: string; @@ -43,7 +47,7 @@ export type LoadConfigOptions = { * * @experimental This API is not stable and may change at any point */ - experimentalEnvFunc?: EnvFunc; + experimentalEnvFunc?: (name: string) => Promise; /** * An optional configuration that enables watching of config files. @@ -114,9 +118,7 @@ export async function loadConfig( try { fileConfigs = await loadConfigFiles(); } catch (error) { - throw new Error( - `Failed to read static configuration file, ${error.message}`, - ); + throw new ForwardedError('Failed to read static configuration file', error); } const envConfigs = await readEnvConfig(process.env); diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 65fd736283..e01e89f04c 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/core-app-api +## 0.1.18 + +### Patch Changes + +- 202f322927: Atlassian auth provider + + - AtlassianAuth added to core-app-api + - Atlassian provider added to plugin-auth-backend + - Updated user-settings with Atlassian connection + +- 36e67d2f24: Internal updates to apply more strict checks to throw errors. +- Updated dependencies + - @backstage/core-components@0.7.1 + - @backstage/core-plugin-api@0.1.11 + ## 0.1.17 ### Patch Changes diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 22d5092aa6..869f9a6eb0 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -15,6 +15,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { AppConfig } from '@backstage/config'; import { AppTheme } from '@backstage/core-plugin-api'; import { AppThemeApi } from '@backstage/core-plugin-api'; +import { atlassianAuthApiRef } from '@backstage/core-plugin-api'; import { auth0AuthApiRef } from '@backstage/core-plugin-api'; import { AuthProvider } from '@backstage/core-plugin-api'; import { AuthRequester } from '@backstage/core-plugin-api'; @@ -236,12 +237,25 @@ export class AppThemeSelector implements AppThemeApi { setActiveThemeId(themeId?: string): void; } +// Warning: (ae-missing-release-tag) "AtlassianAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class AtlassianAuth { + // Warning: (ae-forgotten-export) The symbol "OAuthApiCreateOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + }: OAuthApiCreateOptions): typeof atlassianAuthApiRef.T; +} + // Warning: (ae-missing-release-tag) "Auth0Auth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export class Auth0Auth { - // Warning: (ae-forgotten-export) The symbol "OAuthApiCreateOptions" needs to be exported by the entry point index.d.ts - // // (undocumented) static create({ discoveryApi, diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 3000b0e01e..d11b7122ce 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "0.1.17", + "version": "0.1.18", "private": false, "publishConfig": { "access": "public", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.7.0", + "@backstage/core-components": "^0.7.1", "@backstage/config": "^0.1.10", - "@backstage/core-plugin-api": "^0.1.10", + "@backstage/core-plugin-api": "^0.1.11", "@backstage/theme": "^0.2.11", "@backstage/version-bridge": "^0.1.0", "@material-ui/core": "^4.12.2", @@ -45,7 +45,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.16", + "@backstage/cli": "^0.8.0", "@backstage/test-utils": "^0.1.19", "@backstage/test-utils-core": "^0.1.3", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts b/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts index 9cd666e8a3..7fcbde97df 100644 --- a/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts +++ b/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.test.ts @@ -45,25 +45,25 @@ describe('UrlPatternDiscovery', () => { it('should validate that the pattern is a valid URL', () => { expect(() => { UrlPatternDiscovery.compile('example.com'); - }).toThrow('Invalid discovery URL pattern, Invalid URL: example.com'); + }).toThrow("Invalid discovery URL pattern, URL 'example.com' is invalid"); expect(() => { UrlPatternDiscovery.compile('http://'); - }).toThrow('Invalid discovery URL pattern, Invalid URL: http://'); + }).toThrow("Invalid discovery URL pattern, URL 'http://' is invalid"); expect(() => { UrlPatternDiscovery.compile('abc123'); - }).toThrow('Invalid discovery URL pattern, Invalid URL: abc123'); + }).toThrow("Invalid discovery URL pattern, URL 'abc123' is invalid"); expect(() => { UrlPatternDiscovery.compile('http://example.com:{{pluginId}}'); }).toThrow( - 'Invalid discovery URL pattern, Invalid URL: http://example.com:pluginId', + "Invalid discovery URL pattern, URL 'http://example.com:pluginId' is invalid", ); expect(() => { UrlPatternDiscovery.compile('/{{pluginId}}'); - }).toThrow('Invalid discovery URL pattern, Invalid URL: /pluginId'); + }).toThrow("Invalid discovery URL pattern, URL '/pluginId' is invalid"); expect(() => { UrlPatternDiscovery.compile('http://localhost/{{pluginId}}?forbidden'); diff --git a/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts b/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts index 81cc5a26b1..58e14c9f04 100644 --- a/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts +++ b/packages/core-app-api/src/apis/implementations/DiscoveryApi/UrlPatternDiscovery.ts @@ -16,6 +16,8 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; +const ERROR_PREFIX = 'Invalid discovery URL pattern,'; + /** * UrlPatternDiscovery is a lightweight DiscoveryApi implementation. * It uses a single template string to construct URLs for each plugin. @@ -30,21 +32,22 @@ export class UrlPatternDiscovery implements DiscoveryApi { */ static compile(pattern: string): UrlPatternDiscovery { const parts = pattern.split(/\{\{\s*pluginId\s*\}\}/); + const urlStr = parts.join('pluginId'); + let url; try { - const urlStr = parts.join('pluginId'); - const url = new URL(urlStr); - if (url.hash) { - throw new Error('URL must not have a hash'); - } - if (url.search) { - throw new Error('URL must not have a query'); - } - if (urlStr.endsWith('/')) { - throw new Error('URL must not end with a slash'); - } - } catch (error) { - throw new Error(`Invalid discovery URL pattern, ${error.message}`); + url = new URL(urlStr); + } catch { + throw new Error(`${ERROR_PREFIX} URL '${urlStr}' is invalid`); + } + if (url.hash) { + throw new Error(`${ERROR_PREFIX} URL must not have a hash`); + } + if (url.search) { + throw new Error(`${ERROR_PREFIX} URL must not have a query`); + } + if (urlStr.endsWith('/')) { + throw new Error(`${ERROR_PREFIX} URL must not end with a slash`); } return new UrlPatternDiscovery(parts); diff --git a/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts b/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts new file mode 100644 index 0000000000..423c45bc6c --- /dev/null +++ b/packages/core-app-api/src/apis/implementations/auth/atlassian/AtlassianAuth.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AtlassianIcon from '@material-ui/icons/AcUnit'; +import { atlassianAuthApiRef } from '@backstage/core-plugin-api'; +import { OAuth2 } from '../oauth2'; +import { OAuthApiCreateOptions } from '../types'; + +const DEFAULT_PROVIDER = { + id: 'atlassian', + title: 'Atlassian', + icon: AtlassianIcon, +}; + +class AtlassianAuth { + static create({ + discoveryApi, + environment = 'development', + provider = DEFAULT_PROVIDER, + oauthRequestApi, + }: OAuthApiCreateOptions): typeof atlassianAuthApiRef.T { + return OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider, + environment, + }); + } +} + +export default AtlassianAuth; diff --git a/packages/techdocs-common/src/globals.d.ts b/packages/core-app-api/src/apis/implementations/auth/atlassian/index.ts similarity index 84% rename from packages/techdocs-common/src/globals.d.ts rename to packages/core-app-api/src/apis/implementations/auth/atlassian/index.ts index ad293d2ebe..fb787be1ce 100644 --- a/packages/techdocs-common/src/globals.d.ts +++ b/packages/core-app-api/src/apis/implementations/auth/atlassian/index.ts @@ -14,9 +14,4 @@ * limitations under the License. */ -declare module NodeJS { - interface Global { - rootDir: string; - storageFilesMock: IStorageFilesMock; - } -} +export { default as AtlassianAuth } from './AtlassianAuth'; diff --git a/packages/core-app-api/src/apis/implementations/auth/index.ts b/packages/core-app-api/src/apis/implementations/auth/index.ts index b622b90b8c..bbc9d23ccc 100644 --- a/packages/core-app-api/src/apis/implementations/auth/index.ts +++ b/packages/core-app-api/src/apis/implementations/auth/index.ts @@ -24,3 +24,4 @@ export * from './auth0'; export * from './microsoft'; export * from './onelogin'; export * from './bitbucket'; +export * from './atlassian'; diff --git a/packages/core-app-api/src/app/defaultApis.ts b/packages/core-app-api/src/app/defaultApis.ts index 5e850f4cce..fb02274cf0 100644 --- a/packages/core-app-api/src/app/defaultApis.ts +++ b/packages/core-app-api/src/app/defaultApis.ts @@ -33,6 +33,7 @@ import { SamlAuth, OneLoginAuth, UnhandledErrorForwarder, + AtlassianAuth, } from '../apis'; import { @@ -55,6 +56,7 @@ import { oneloginAuthApiRef, oidcAuthApiRef, bitbucketAuthApiRef, + atlassianAuthApiRef, } from '@backstage/core-plugin-api'; import OAuth2Icon from '@material-ui/icons/AcUnit'; @@ -244,4 +246,19 @@ export const defaultApis = [ environment: configApi.getOptionalString('auth.environment'), }), }), + createApiFactory({ + api: atlassianAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return AtlassianAuth.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), ]; diff --git a/packages/core-app-api/src/routing/RoutingProvider.test.tsx b/packages/core-app-api/src/routing/RoutingProvider.test.tsx index 12d9e9ee33..fbdd34ae68 100644 --- a/packages/core-app-api/src/routing/RoutingProvider.test.tsx +++ b/packages/core-app-api/src/routing/RoutingProvider.test.tsx @@ -83,7 +83,7 @@ const MockRouteSource = (props: { } catch (ex) { return (
- Error at {props.name}: {ex.message} + Error at {props.name}, {String(ex)}
); } @@ -293,12 +293,12 @@ describe('discovery', () => { expect( rendered.getByText( - `Error at outsideWithParams: Cannot route to ${ref3} with parent ${ref5} as it has parameters`, + `Error at outsideWithParams, Error: Cannot route to ${ref3} with parent ${ref5} as it has parameters`, ), ).toBeInTheDocument(); expect( rendered.getByText( - `Error at outsideNoParams: Cannot route to ${ref3} with parent ${ref5} as it has parameters`, + `Error at outsideNoParams, Error: Cannot route to ${ref3} with parent ${ref5} as it has parameters`, ), ).toBeInTheDocument(); }); diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 4300acb628..375f23dfda 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-components +## 0.7.1 + +### Patch Changes + +- e535ea766a: Switched to relying on the built-in support for async loading in `react-syntax-highlighter`. This should provide further improvements to async rendering and lazy loading, and avoid test flakiness that was happening because of the significant number or resources being loaded in lazily all at once. +- 2023a9683f: Fixed invalid wrap in DismissableBanner component +- 36e67d2f24: Internal updates to apply more strict checks to throw errors. +- Updated dependencies + - @backstage/errors@0.1.3 + - @backstage/core-plugin-api@0.1.11 + ## 0.7.0 ### Minor Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index e637bc1b11..865f5b6df9 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.7.0", + "version": "0.7.1", "private": false, "publishConfig": { "access": "public", @@ -30,8 +30,8 @@ }, "dependencies": { "@backstage/config": "^0.1.10", - "@backstage/core-plugin-api": "^0.1.10", - "@backstage/errors": "^0.1.2", + "@backstage/core-plugin-api": "^0.1.11", + "@backstage/errors": "^0.1.3", "@backstage/theme": "^0.2.11", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", @@ -67,8 +67,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/core-app-api": "^0.1.17", - "@backstage/cli": "^0.7.16", + "@backstage/core-app-api": "^0.1.18", + "@backstage/cli": "^0.8.0", "@backstage/test-utils": "^0.1.19", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx index be8e613b61..b2d0c68d17 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx @@ -56,6 +56,7 @@ const useStyles = makeStyles( content: { width: '100%', maxWidth: 'inherit', + flexWrap: 'nowrap', }, message: { display: 'flex', diff --git a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx index b911a9a1d9..ef0d2373e4 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx @@ -21,6 +21,7 @@ import ListItemText from '@material-ui/core/ListItemText'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; import React, { useState } from 'react'; +import { isError } from '@backstage/errors'; import { PendingAuthRequest } from '@backstage/core-plugin-api'; export type LoginRequestListItemClassKey = 'root'; @@ -42,14 +43,14 @@ type RowProps = { const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => { const classes = useItemStyles(); - const [error, setError] = useState(); + const [error, setError] = useState(); const handleContinue = async () => { setBusy(true); try { await request.trigger(); } catch (e) { - setError(e); + setError(isError(e) ? e.message : 'An unspecified error occurred'); } finally { setBusy(false); } @@ -64,13 +65,7 @@ const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => { - {error.message || 'An unspecified error occurred'} - - ) - } + secondary={error && {error}} />