diff --git a/.changeset/angry-ghosts-report.md b/.changeset/angry-ghosts-report.md deleted file mode 100644 index dfa83663a9..0000000000 --- a/.changeset/angry-ghosts-report.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Support a `ensureExists` config option to skip ensuring a configured database exists. This allows deployment scenarios where -limited permissions are given for provisioned databases without privileges to create new databases. If set to `false`, the -database connection will not be validated prior to use which means the backend will not attempt to create the database if it -doesn't exist. You can configure this in your app-config.yaml: - -```yaml -backend: - database: - ensureExists: false -``` - -This defaults to `true` if unspecified. You can also configure this per plugin connection and will override the base option. diff --git a/.changeset/angry-rules-fail.md b/.changeset/angry-rules-fail.md deleted file mode 100644 index cc52d4478e..0000000000 --- a/.changeset/angry-rules-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Add unstable prop for disabling unregister entity menu diff --git a/.changeset/big-pens-train.md b/.changeset/big-pens-train.md deleted file mode 100644 index 5c4f8dc7b0..0000000000 --- a/.changeset/big-pens-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Unsubscribe from broker after response is flushed diff --git a/.changeset/curly-badgers-sit.md b/.changeset/curly-badgers-sit.md deleted file mode 100644 index ca740e907a..0000000000 --- a/.changeset/curly-badgers-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Store filter values set in `EntityListProvider` in query parameters. This allows selected filters to be restored when returning to pages that list catalog entities. diff --git a/.changeset/eighty-snakes-melt.md b/.changeset/eighty-snakes-melt.md new file mode 100644 index 0000000000..f980d1c02e --- /dev/null +++ b/.changeset/eighty-snakes-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +chore: set the lifecycle as beta logo diff --git a/.changeset/green-vans-peel.md b/.changeset/green-vans-peel.md deleted file mode 100644 index cd4d30139a..0000000000 --- a/.changeset/green-vans-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Fix `EntityTypeFilter` so it produces unique case-insensitive set of available types diff --git a/.changeset/grumpy-dolls-call.md b/.changeset/grumpy-dolls-call.md deleted file mode 100644 index 13d96ecb1f..0000000000 --- a/.changeset/grumpy-dolls-call.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-catalog-react': minor -'@backstage/plugin-scaffolder': patch ---- - -Updated the software templates list page (`ScaffolderPage`) to use the `useEntityListProvider` hook from #5643. This reduces the code footprint, making it easier to customize the display of this page, and consolidates duplicate approaches to querying the catalog with filters. - -- The `useEntityTypeFilter` hook has been updated along with the underlying `EntityTypeFilter` to work with multiple values, to allow more flexibility for different user interfaces. It's unlikely that this change affects you; however, if you're using either of these directly, you'll need to update your usage. -- `SearchToolbar` was renamed to `EntitySearchBar` and moved to `catalog-react` to be usable by other entity list pages -- `UserListPicker` now has an `availableTypes` prop to restrict which user-related options to present diff --git a/.changeset/short-eggs-confess.md b/.changeset/kind-needles-appear.md similarity index 53% rename from .changeset/short-eggs-confess.md rename to .changeset/kind-needles-appear.md index 3fa240a347..9a8f56bf48 100644 --- a/.changeset/short-eggs-confess.md +++ b/.changeset/kind-needles-appear.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend-module-ldap': patch --- -Expose missing types used by the custom transformers +chore(deps): bump `@types/ldapjs` from 1.0.10 to 2.2.0 diff --git a/.changeset/lemon-crabs-confess.md b/.changeset/lemon-crabs-confess.md deleted file mode 100644 index 0d87843df5..0000000000 --- a/.changeset/lemon-crabs-confess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Updating fs-extra to 10.0.0 to handle broken symbolic links correctly diff --git a/.changeset/lemon-dancers-taste.md b/.changeset/lemon-dancers-taste.md deleted file mode 100644 index 0542fe4a54..0000000000 --- a/.changeset/lemon-dancers-taste.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -The codeowners processor extracts the username of the primary owner and uses this as the owner field. -Given the kind isn't specified this is assumed to be a group and so the link to the owner in the about card -doesn't work. This change specifies the kind where the entity is a user. e.g: - -`@iain-b` -> `user:iain-b` diff --git a/.changeset/nice-bugs-beg.md b/.changeset/nice-bugs-beg.md deleted file mode 100644 index c41b54b220..0000000000 --- a/.changeset/nice-bugs-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': minor ---- - -Exported and renamed components from the `@backstage/plugin-user-settings` plugin , to be able to use it in the consumer side and customize the `SettingPage` diff --git a/.changeset/polite-spies-judge.md b/.changeset/polite-spies-judge.md deleted file mode 100644 index 075bcf1598..0000000000 --- a/.changeset/polite-spies-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-rails': patch ---- - -updated paths to consider differences between platform (windows corrected) diff --git a/.changeset/poor-otters-buy.md b/.changeset/poor-otters-buy.md deleted file mode 100644 index b60cbddc1b..0000000000 --- a/.changeset/poor-otters-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Add new `fetch:template` action which handles the same responsibilities as `fetch:cookiecutter` without the external dependency on `cookiecutter`. For information on migrating from `fetch:cookiecutter` to `fetch:template`, see the [migration guide](https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetch-cookiecutter-to-fetch-template) in the docs. diff --git a/.changeset/pretty-drinks-serve.md b/.changeset/pretty-drinks-serve.md deleted file mode 100644 index a012aba11f..0000000000 --- a/.changeset/pretty-drinks-serve.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Scaffolder: Added an 'eq' handlebars helper for use in software template YAML files. This can be used to execute a step depending on the value of an input, e.g.: - -```yaml -steps: - id: 'conditional-step' - action: 'custom-action' - if: '{{ eq parameters.myvalue "custom" }}', -``` diff --git a/.changeset/real-plums-vanish.md b/.changeset/real-plums-vanish.md deleted file mode 100644 index a653a6bdf2..0000000000 --- a/.changeset/real-plums-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Add Sign In and Handler resolver for Microsoft provider diff --git a/.changeset/search-mighty-mice-collect.md b/.changeset/search-mighty-mice-collect.md deleted file mode 100644 index bad91ff853..0000000000 --- a/.changeset/search-mighty-mice-collect.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-search-backend-node': minor ---- - -Change return value of `SearchEngine.index` to `Promise` to support -implementation of external search engines. diff --git a/.changeset/shy-rules-design.md b/.changeset/shy-rules-design.md deleted file mode 100644 index 4f009e5aa8..0000000000 --- a/.changeset/shy-rules-design.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Updated inputs for the `publish:github:pull-request` action. - -Now requires a `repoUrl` instead of separate `owner` and `repo` inputs. This aligns with the output of the `RepoUrlPicker` ui field used by the pull-request sample template. diff --git a/.changeset/slimy-elephants-sell.md b/.changeset/slimy-elephants-sell.md new file mode 100644 index 0000000000..c002e8e950 --- /dev/null +++ b/.changeset/slimy-elephants-sell.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes-common': patch +'@backstage/plugin-kubernetes': patch +--- + +chore(deps): bump `@kubernetes/client-node` from 0.14.3 to 0.15.0 diff --git a/.changeset/small-emus-sin.md b/.changeset/small-emus-sin.md new file mode 100644 index 0000000000..e5683d18f9 --- /dev/null +++ b/.changeset/small-emus-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Fix `EntityListProvider` to not update url if unmounted diff --git a/.changeset/techdocs-she-was-gone.md b/.changeset/techdocs-she-was-gone.md new file mode 100644 index 0000000000..a6821abcf6 --- /dev/null +++ b/.changeset/techdocs-she-was-gone.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +TechDocs now uses a "safe by default" sanitization library, rather than relying on its own, hard-coded list of allowable tags and attributes. diff --git a/.changeset/techdocs-typescript-isnt-fun.md b/.changeset/techdocs-typescript-isnt-fun.md deleted file mode 100644 index 9341c1c14a..0000000000 --- a/.changeset/techdocs-typescript-isnt-fun.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -It is no longer required to provide a generator and a preparer to the TechDocs -router factory when running TechDocs in the "recommended" (e.g. externally -prepared and generated docs) configuration. diff --git a/.changeset/wise-rockets-smoke.md b/.changeset/wise-rockets-smoke.md deleted file mode 100644 index c81d20519d..0000000000 --- a/.changeset/wise-rockets-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Document the default behavior of `statusCheck` option in `createStatusCheckRouter`. diff --git a/.changeset/young-tables-reply.md b/.changeset/young-tables-reply.md deleted file mode 100644 index e08a875c14..0000000000 --- a/.changeset/young-tables-reply.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/cli': patch -'@backstage/config-loader': patch -'@backstage/create-app': patch -'@backstage/techdocs-common': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-kubernetes-backend': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-techdocs-backend': patch ---- - -Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index e6af881f1e..48740ef367 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -1,5 +1,6 @@ abc accessors +Anddddd Apdex api Api @@ -212,6 +213,7 @@ repos rerender Reusability reusability +roadmaps rollbar Rollbar Rollup diff --git a/ADOPTERS.md b/ADOPTERS.md index 1dcd4a774f..041242c083 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -37,3 +37,5 @@ | [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | | [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | | [RedVentures](https://www.redventures.com/) | [Chris Diaz](https://github.com/codingdiaz) | Developer portal that brings everything an engineer needs to provide value into a single pane of glass. | +| [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | +| [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | diff --git a/app-config.yaml b/app-config.yaml index b50de963ed..074e28c086 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -101,8 +101,10 @@ organization: # https://backstage.io/docs/features/techdocs/how-to-guides#how-to-migrate-from-techdocs-basic-to-recommended-deployment-approach techdocs: builder: 'local' # Alternatives - 'external' - generators: - techdocs: 'docker' # Alternatives - 'local' + generator: + runIn: 'docker' + # dockerImage: my-org/techdocs # use a custom docker image + # pullImage: true # or false to disable automatic pulling of image (e.g. if custom docker login is required) publisher: type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift'. Read documentation for using alternatives. @@ -312,13 +314,20 @@ auth: # # scope: saml-login-selector openid profile email oidc: + # Note that you must define a session secret (see above) since the oidc provider requires session support. + # Note that by default, this provider will use the 'none' prompt which assumes that your are already logged on in the IDP. + # You should set prompt to: + # - auto: will let the IDP decide if you need to log on or if you can skip login when you have an active SSO session + # - login: will force the IDP to always present a login form to the user development: metadataUrl: ${AUTH_OIDC_METADATA_URL} clientId: ${AUTH_OIDC_CLIENT_ID} clientSecret: ${AUTH_OIDC_CLIENT_SECRET} authorizationUrl: ${AUTH_OIDC_AUTH_URL} tokenUrl: ${AUTH_OIDC_TOKEN_URL} - tokenSignedResponseAlg: ${AUTH_OIDC_TOKEN_SIGNED_RESPONSE_ALG} + tokenSignedResponseAlg: ${AUTH_OIDC_TOKEN_SIGNED_RESPONSE_ALG} # default='RS256' + scope: ${AUTH_OIDC_SCOPE} # default='openid profile email' + prompt: ${AUTH_OIDC_PROMPT} # default=none (allowed values: auto, none, consent, login) auth0: development: clientId: ${AUTH_AUTH0_CLIENT_ID} @@ -370,3 +379,9 @@ homepage: timezone: 'Asia/Tokyo' pagerduty: eventsBaseUrl: 'https://events.pagerduty.com/v2' +jenkins: + instances: + - name: default + baseUrl: https://jenkins.example.com + username: backstage-bot + apiKey: 123456789abcdef0123456789abcedf012 diff --git a/contrib/docs/tutorials/aws-deployment.md b/contrib/docs/tutorials/aws-deployment.md index 47170658a3..3a76da2ed3 100644 --- a/contrib/docs/tutorials/aws-deployment.md +++ b/contrib/docs/tutorials/aws-deployment.md @@ -5,7 +5,7 @@ image](https://backstage.io/docs/getting-started/deployment-docker); this tutorial shows how to deploy that Docker image to AWS using Elastic Container Registry (ECR) and Elastic Kubernetes Service (EKS). Amazon also supports deployments with Helm, covered in the [Helm -Kubernetes](../kubernetes/basic_kubernetes_example_with_helm) example. +Kubernetes](../../kubernetes/basic_kubernetes_example_with_helm) example. The basic workflow for this method is to build a Backstage Docker image, upload the new version to a container registry, and update a Kubernetes deployment with diff --git a/docs/features/software-catalog/catalog-customization.md b/docs/features/software-catalog/catalog-customization.md index e0228bf7d0..0c11d8f476 100644 --- a/docs/features/software-catalog/catalog-customization.md +++ b/docs/features/software-catalog/catalog-customization.md @@ -27,27 +27,33 @@ default catalog page and create a component in a ```tsx // imports, etc omitted for brevity. for full source see: // https://github.com/backstage/backstage/blob/master/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx -export const CustomCatalogPage = () => { +export const CustomCatalogPage = ({ + columns, + actions, + initiallySelectedFilter = 'owned', +}: CatalogPageProps) => { return ( - + All your software catalog entities -
- -
+ + +
- -
-
+ + + + + +
-
+ ); }; ``` @@ -137,19 +143,27 @@ export const EntitySecurityTierPicker = () => { Now we can add the component to `CustomCatalogPage`: ```diff -export const CustomCatalogPage = () => { +export const CustomCatalogPage = ({ + columns, + actions, + initiallySelectedFilter = 'owned', +}: CatalogPageProps) => { return ( ... - -
+ + +
- -
+ + + + + + ... }; ``` diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index a502c2aee5..f5296f38e1 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -8,12 +8,11 @@ description: The Backstage Software Catalog ## What is a Software Catalog? -The Backstage Software Catalog — actually, a software catalog, since it includes -more than just services — is a centralized system that keeps track of ownership -and metadata for all the software in your ecosystem (services, websites, -libraries, data pipelines, etc). The catalog is built around the concept of -[metadata YAML files](descriptor-format.md) stored together with the code, which -are then harvested and visualized in Backstage. +The Backstage Software Catalog is a centralized system that keeps track of +ownership and metadata for all the software in your ecosystem (services, +websites, libraries, data pipelines, etc). The catalog is built around the +concept of [metadata YAML files](descriptor-format.md) stored together with the +code, which are then harvested and visualized in Backstage. ![software-catalog](https://backstage.io/blog/assets/6/header.png) diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 67b92bd941..5f474624f9 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -101,18 +101,22 @@ repository itself. If the URL points to a folder, it is important that it is suffixed with a `'/'` in order for relative path resolution to work consistently. -### jenkins.io/github-folder +### jenkins.io/job-full-name ```yaml # Example: metadata: annotations: - jenkins.io/github-folder: folder-name/job-name + jenkins.io/job-full-name: folder-name/job-name ``` The value of this annotation is the path to a job on Jenkins, that builds this entity. +The value can be the format of just `[folder-path]` or +`[instanceName]:[folder-path]`, if multiple instances are configured in +`app-config.yaml` + Specifying this annotation may enable Jenkins related features in Backstage for that entity. @@ -304,6 +308,10 @@ This annotation allowed to load the API definition from another location. Use [substitution](./descriptor-format.md#substitutions-in-the-descriptor-format) instead. +### jenkins.io/github-folder + +Use the `jenkins.io/job-full-name` instead. + ## Links - [Descriptor Format: annotations](descriptor-format.md#annotations-optional) diff --git a/docs/features/software-templates/configuration.md b/docs/features/software-templates/configuration.md index cdd8166889..737f77f67b 100644 --- a/docs/features/software-templates/configuration.md +++ b/docs/features/software-templates/configuration.md @@ -33,9 +33,12 @@ scaffolder: ### Disabling Docker in Docker situation (Optional) -Software Templates use -[Cookiecutter](https://github.com/cookiecutter/cookiecutter) as a templating -library. By default it will use the +Software templates use the `fetch:template` action by default, which requires no +external dependencies and offers a +[Cookiecutter-compatible mode](https://backstage.io/docs/features/software-templates/builtin-actions#using-cookiecuttercompat-mode). +There is also a `fetch:cookiecutter` action, which uses +[Cookiecutter](https://github.com/cookiecutter/cookiecutter) directly for +templating. By default, the `fetch:cookiecutter` action will use the [scaffolder-backend/Cookiecutter](https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile) docker image. diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 7b475f2fa1..02bb698f64 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -227,6 +227,33 @@ spec: inputType: tel ``` +#### Hide or mask sensitive data on Review step + +Sometimes, specially in custom fields, you collect some data on Create form that +must not be shown to the user on Review step. To hide or mask this data, you can +use `ui:widget: password` or set some properties of `ui:backstage`: + +```yaml +- title: Hide or mask values + properties: + password: + title: Password + type: string + ui:widget: password # will print '******' as value for property 'password' on Review Step + masked: + title: Masked + type: string + ui:backstage: + review: + mask: '' # will print '' as value for property 'Masked' on Review Step + hidden: + title: Hidden + type: string + ui:backstage: + review: + show: false # wont print any info about 'hidden' property on Review Step +``` + #### The Repository Picker So in order to make working with repository providers easier, we've built a diff --git a/docs/features/techdocs/configuration.md b/docs/features/techdocs/configuration.md index aa38dd42b1..ef6a80d44c 100644 --- a/docs/features/techdocs/configuration.md +++ b/docs/features/techdocs/configuration.md @@ -13,14 +13,29 @@ configuration options for TechDocs. # File: app-config.yaml techdocs: - # generators.techdocs can have two values: 'docker' or 'local'. This is to determine how to run the generator - whether to - # spin up the techdocs-container docker image or to run mkdocs locally (assuming all the dependencies are taken care of). - # You want to change this to 'local' if you are running Backstage using your own custom Docker setup and want to avoid running - # into Docker in Docker situation. Read more here - # https://backstage.io/docs/features/techdocs/getting-started#disable-docker-in-docker-situation-optional + # techdocs.generator is used to configure how documentation sites are generated using MkDocs. - generators: - techdocs: 'docker' + generator: + # techdocs.generator.runIn can be either 'docker' or 'local'. This is to determine how to run the generator - whether to + # spin up the techdocs-container docker image or to run mkdocs locally (assuming all the dependencies are taken care of). + # You want to change this to 'local' if you are running Backstage using your own custom Docker setup and want to avoid running + # into Docker in Docker situation. Read more here + # https://backstage.io/docs/features/techdocs/getting-started#disable-docker-in-docker-situation-optional + + runIn: 'docker' + + # techdocs.generator.dockerImage can be used to control the docker image used during documentation generation. This can be useful + # if you want to use MkDocs plugins or other packages that are not included in the default techdocs-container (spotify/techdocs). + # NOTE: This setting is only used when techdocs.generator.runIn is set to 'docker'. + + dockerImage: 'spotify/techdocs' + + # techdocs.generator.pullImage can be used to disable pulling the latest docker image by default. This can be useful when you are + # using a custom techdocs.generator.dockerImage and you have a custom docker login requirement. For example, you need to login to + # AWS ECR to pull the docker image. + # NOTE: Disabling this requires the docker image was pulled by other means before running the techdocs generator. + + pullImage: true # techdocs.builder can be either 'local' or 'external. # If builder is set to 'local' and you open a TechDocs page, techdocs-backend will try to generate the docs, publish to storage diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index 119629915e..159137c879 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -150,6 +150,39 @@ permissions to: - `s3:ListBucket` - To retrieve bucket metadata - `s3:GetObject` - To retrieve files from the bucket +> Note: If you need to migrate documentation objects from an older-style path +> format including case-sensitive entity metadata, you will need to add some +> additional permissions to be able to perform the migration, including: +> +> - `s3:PutBucketAcl` (for copying files, +> [more info here](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectAcl.html)) +> - `s3:DeleteObject` and `s3:DeleteObjectVersion` (for deleting migrated files, +> [more info here](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html)) +> +> ...And you will need to ensure the permissions apply to the bucket itself, as +> well as all resources under the bucket. See the example policy below. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "TechDocsWithMigration", + "Effect": "Allow", + "Action": [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObjectVersion", + "s3:ListBucket", + "s3:DeleteObject", + "s3:PutObjectAcl" + ], + "Resource": ["arn:aws:s3:::your-bucket", "arn:aws:s3:::your-bucket/*"] + } + ] +} +``` + **4a. (Recommended) Setup authentication the AWS way, using environment variables** diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 13b52181df..b01afac5c3 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -24,11 +24,9 @@ an easier path to make Pull Requests. Backstage provides the `@backstage/create-app` package to scaffold standalone instances of Backstage. You will need to have [Node.js](https://nodejs.org/en/download/) Active LTS Release installed -(currently v14), [Yarn](https://classic.yarnpkg.com/en/docs/install) and -[Python](https://www.python.org/downloads/) (although you likely have it -already). You will also need to have -[Docker](https://docs.docker.com/engine/install/) installed to use some features -like Software Templates and TechDocs. +(currently v14) and [Yarn](https://classic.yarnpkg.com/en/docs/install). You +will also need to have [Docker](https://docs.docker.com/engine/install/) +installed to use some features like Software Templates and TechDocs. Using `npx` you can then run the following to create an app in a chosen subdirectory of your current working directory: diff --git a/docs/integrations/bitbucket/discovery.md b/docs/integrations/bitbucket/discovery.md index fadb7c9f06..1538806e39 100644 --- a/docs/integrations/bitbucket/discovery.md +++ b/docs/integrations/bitbucket/discovery.md @@ -38,7 +38,11 @@ The target is composed of four parts: repositories prefixed with `service-`. - The path within each repository to find the catalog YAML file. This will usually be `/catalog-info.yaml` or a similar variation for catalog files - stored in the root directory of each repository. + stored in the root directory of each repository. If omitted, the default value + `catalog-info.yaml` will be used. E.g. given that `my-project`and `service-a` + exists, `https://bitbucket.mycompany.com/projects/my-project/repos/service-*/` + will result in: + `https://bitbucket.mycompany.com/projects/my-project/repos/service-a/catalog-info.yaml`. ## Custom repository processing diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 0013c99c6b..fa34ca3d9a 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -1,116 +1,185 @@ --- id: roadmap -title: Project roadmap -description: Roadmap of Backstage Project +title: Roadmap +description: Roadmap of Backstage --- -## Current status +## The Backstage Roadmap -> Backstage is currently under rapid development. This means that you can expect -> APIs and features to evolve. It is also recommended that teams who adopt -> Backstage today [upgrade their installation](../cli/commands.md#versionsbump) -> as new [releases](https://github.com/backstage/backstage/releases) become -> available, as Backwards compatibility is not yet guaranteed. +Backstage is currently under rapid development. This page details the project’s +public roadmap, the result of ongoing collaboration between the core maintainers +and the broader Backstage community. Treat the roadmap as an ever-evolving guide +to keep us aligned as a community on: -## Phases +- Upcoming enhancements and benefits, +- Planning contributions and support, +- Planning the project’s adoption, +- Understanding what things are coming soon, +- Avoiding duplication of work -We have divided the project into three high-level _phases_: +### How to influence the roadmap -- 🐣 **Phase 1:** Extensible frontend platform (Done ✅) - You will be able to - easily create a single consistent UI layer for your internal infrastructure - and tools. A set of reusable - [UX patterns and components](https://backstage.io/storybook) help ensure a - consistent experience between tools. +As we evolve Backstage, we want you to contribute actively in the journey to +define the most effective developer experience in the world. -- 🐢 **Phase 2:** Software Catalog - ([alpha released](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha)) - - With a single catalog, Backstage makes it easy for a team to manage ten - services — and makes it possible for your company to manage thousands of them. +A roadmap is only useful if it captures real needs. If you have success stories, +feedback, or ideas, we want to hear from you! If you plan to work (or are +already working) on a new or existing feature, please let us know, so that we +can update the roadmap accordingly. We are also happy to share knowledge and +context that will help your feature land successfully. -- 🐇 **Phase 3:** Ecosystem (ongoing, see - [Plugin Marketplace](https://backstage.io/plugins)) - Everyone's - infrastructure stack is different. By fostering a vibrant community of - contributors we hope to provide an ecosystem of Open Source - plugins/integrations that allows you to pick the tools that match your stack. - -## Detailed roadmap - -If you have questions about the roadmap or want to provide feedback, we would -love to hear from you! Please create an -[Issue](https://github.com/backstage/backstage/issues/new/choose), ping us on -[Discord](https://discord.gg/EBHEGzX) or reach out directly at -[backstage-interest@spotify.com](mailto:backstage-interest@spotify.com). - -Want to help out? Awesome ❤️ Head over to +You can also head over to the [CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guidelines to get started. -### Ongoing work 🚧 +If you have specific questions about the roadmap, please create an +[issue](https://github.com/backstage/backstage/issues/new/choose), ping us on +[Discord](https://discord.gg/awD6SxgQ), or +[book time](http://calendly.com/spotify-backstage) with the Spotify team. -- **[Platform stabilization](https://github.com/backstage/backstage/milestone/19)** - - Stabilize the core of Backstage, including its core features, so that the - platform can be depended on for production use. After this, plugins will - require little-to-no maintenance. +### How to read the roadmap -- **[Kubernetes plugin for service owners](https://github.com/backstage/backstage/issues/2857)** - - Improve native support for Kubernetes, making it easier for service owners to - see and manage their services running in K8s, regardless if that's locally, in - AWS, GCS, Azure, or elsewhere. +The Backstage roadmap lays out both [“what’s next”](#whats-next) and +[“future work”](#future-work). With "next" we mean features planned for release +within the ongoing quarter starting in July until September 2021 included. With +"future" we mean features in the radar, but not yet scheduled. -- **[Search platform](../features/search/README.md)** - Evolve the basic search - functionality currently available into a platform that **a)** enables search - across the software catalog, TechDocs, and any other information exposed by - plugins, and **b)** supports a variety of search engine technologies. +The long-term roadmap (12 - 36 months) is not detailed in the public roadmap. +Third-party contributions are also not currently included in the roadmap. Let us +know about any ongoing developments and we’re happy to include it here as well. -- **[Software Templates V2](https://github.com/backstage/backstage/issues/2771)** - - Expand the templates to make the steps more composable by adding the ability - to add more steps for custom logic, including webhooks and using authorization - from integrations. +### Roadmap evolution -### Future work 🔮 +Will this roadmap change? Obviously! -- **Golden Path for Plugin Development** - Create an easy, standardized way for - developers to build plugins that will encourage contributions and lead to a - richer ecosystem for everyone. +Roadmap are always evolving and ours is no different; you can expect to see this +updated roughly every month. -- **[GraphQL API](https://github.com/backstage/backstage/milestone/13)** - A - GraphQL API will open up the rich metadata provided by Backstage in a single - query. Plugins can easily query this API as well as extend the model where - needed. +## What’s next -- **Inter-Plugin Communication** - **[Under consideration]** Establish more - clearly defined patterns for plugins to communicate. +The feature set below is planned for the ongoing quarter, and grouped by theme. +The list order doesn’t necessarily reflect priority, and the development/release +cycle will vary based on maintainer schedules. -- **Improved Access Control** - **[Under consideration]** Provide finer grained - access controls and management for better control of the platform user - experience. +### Backstage Core -### Plugins +The following features are planned for release: -Building and maintaining [plugins](https://backstage.io/plugins) is the work of -the entire Backstage community. +- **Composable homepage:** We’re seeing lots of interest from the community in + reusable components to build a homepage experience where users can easily + surface what they might find useful to start their tasks. Check out the + [milestone](https://github.com/backstage/backstage/milestone/34) for further + details. +- **Improved responsiveness:** Check out the + [RFC here](https://github.com/backstage/backstage/issues/6318) for further + details on how to improve the responsiveness for Backstage's UI. -A list of plugins that are in development is -[available here](https://github.com/backstage/backstage/issues?q=is%3Aissue+is%3Aopen+label%3Aplugin+sort%3Areactions-%2B1-desc). -We strongly recommend to upvote 👍 plugins you are interested in. This helps us -and the community prioritize what plugins to build. +### Software Templates -Are you missing a plugin for your favorite tool? Please -[suggest a new one](https://github.com/backstage/backstage/issues/new?labels=plugin&template=plugin_template.md&title=%5BPlugin%5D+THE+PLUGIN+NAME). -Chances are that someone will jump in and help build it. +The following features are planned for release:: -### Community Initiatives 🧑‍🤝‍🧑 +- **Re-creation/resubmission in case of failure:** Speed up productivity by + allowing developers to relaunch a project after a failure or any unexpected + problem. In the current version, this task requires retyping and a full + re-creation from scratch. +- **Performance and usability improvements for contributors:** Reach a relevant + improvement in templating's performance through the replacement of + [handlebars](https://handlebarsjs.com/). Other replacements will be considered + as part of this task (possibly + [cookiecutter](https://cookiecutter.readthedocs.io/)) for easier software + template creation, allowing more contributors to reach their goals without + having to learn new tooling. +- **Improved extensibility through inclusion:** Make software templates more + maintainable and extensible by adding `$include` support for parameters. +- **Authenticated job creation:** Created jobs will be able to run with an + authenticated user with all actions tracked for future consumption and + evidence. Track users creating jobs and make “jobs created by me” reporting + available. -- [**Backstage Community Sessions**](https://github.com/backstage/community#meetups) - - A monthly meetup for the community to come together to share and learn about - the latest happenings in Backstage. +### Software Catalog -- **Backstage Hackathons** - (Coming soon) Open to everyone in our Backstage - community, a celebration of you, the project and building awesome things - together +The following features are planned for release: -### Completed milestones ✅ +- **Request For Comments (RFC) for composability improvements (routing):** + Enable plugins to be auto-added and make plugin installation and upgrades + easier for all Backstage users. This includes information card layouts, entity + pages containing content and hooking the external header, considering the + support of a separate deployment, and configuration for plugins. +- **Removing duplicated entities in catalog:** As any adopter knows, a software + catalog can contain thousands or more entities and it is very important to + avoid duplications in naming to prevent failures. With this development task, + two entities with the same name won't be allowed as described + [here](https://github.com/backstage/backstage/issues/4760). +- **Connecting identity to ownership to prepare for role-based access control + ([RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)):** This is a + first step to supporting RBAC for the software catalog (see the + [future work section](#future-work) for further details). Provide each entity + within the software catalog with a recognized owner. +- **Catalog performance improvements through improved caching:** Fix the + performance gaps in the catalog processor, which currently doesn’t have a + strong caching mechanism. The current version often requires fetching a + relevant amount of data, especially at scale. +### Search + +The following features are planned for release: + +- ElasticSearch integration: Add ElasticSearch to the Search Platform as the + underlying search engine. Check out the + [milestone here](https://github.com/backstage/backstage/milestone/27) for + further details. + +### TechDocs + +The following features are planned for release: + +- **TechDocs beta release:** Fix remaining bugs to get TechDocs to Beta. Check + out the [milestone here](https://github.com/backstage/backstage/milestone/29) + for further details. + +## Future work + +The following feature list doesn’t represent a commitment to develop and the +list order doesn’t reflect any priority or importance. But these features are on +the maintainers’ radar, with clear interest expressed by the community. + +- **Improved UX design:** Provide a better Backstage user experience through + visual guidelines and templates, especially navigation across plug-ins and + portal functionalities. +- **Catalog composability (routing):** Follow up development after the RFC + planned for the ongoing quarter (see [what’s next](#whats-next) for further + details). +- **Catalog-import improvements:** Provide a faster (scalability) and better + (more features like move/rename) way to import entities into the Software + Catalog. Importing items in the Software Catalog is crucial for creating a + Backstage proof-of-concept or testing/planning for broader organizational + adoption. This enhancement better supports getting developers to use Backstage + with less effort and customization. +- **Catalog improvements:** Add pagination and sourcing to Software Catalog. +- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query + Backstage backend services with a standard query language for APIs. +- **Software templates performance improvements through decoupling a separate + worker:** Improve performance through decoupling resource-consuming services + and making them asynchronous. In the current version, project auto-creation + through the Software Templating system can consume a lot of resources and + bottleneck many concurrent projects created simultaneously. +- **API discovery and documentation:** Add better support for the + [gRPC](https://grpc.io/). +- **Adding TechDocs search to the Search Platform:** Having this capability in + place will provide a better and new major version of the Search Platform + (v3.0). You can refer to the + [milestone here](https://github.com/backstage/backstage/milestone/28) for + further details. +- **TechDocs GA release:** Work toward enhancements necessary to get TechDocs to + general availability. Check out the + [milestone here](https://github.com/backstage/backstage/milestone/30) for + further details. + +## Completed milestones + +Read more about the completed (and released) features for reference. + +- [[Search] Out-of-the-Box Implementation (Alpha)](https://github.com/backstage/backstage/milestone/26) - [Deploy a product demo at `demo.backstage.io`](https://demo.backstage.io) - [Kubernetes plugin - v1](https://github.com/backstage/backstage/tree/master/plugins/kubernetes) - [Helm charts](https://github.com/backstage/backstage/tree/master/contrib/chart/backstage) diff --git a/docs/plugins/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md index beffcdb313..99a55745c5 100644 --- a/docs/plugins/structure-of-a-plugin.md +++ b/docs/plugins/structure-of-a-plugin.md @@ -99,15 +99,14 @@ You may tweak these components, rename them and/or replace them completely. ## Connecting the plugin to the Backstage app -There are three things needed for a Backstage app to start making use of a -plugin. +There are two things needed for a Backstage app to start making use of a plugin. 1. Add plugin as dependency in `app/package.json` 2. Import and use one or more plugin extensions, for example in `app/src/App.tsx`. -Luckily these three steps happen automatically when you create a plugin with the -Backstage CLI. +Luckily both of these steps happen automatically when you create a plugin with +the Backstage CLI. ## Talking to the outside world diff --git a/microsite/blog/2021-07-26-software-templates-are-now-in-beta.md b/microsite/blog/2021-07-26-software-templates-are-now-in-beta.md new file mode 100644 index 0000000000..b646fbdb22 --- /dev/null +++ b/microsite/blog/2021-07-26-software-templates-are-now-in-beta.md @@ -0,0 +1,68 @@ +--- +title: Software Templates is now in Beta +author: Ben Lambert, Spotify +authorURL: https://github.com/benjdlambert +authorImageURL: https://avatars.githubusercontent.com/u/3645856?v=4 +--- + +![Software Templates in Beta](assets/21-07-26/backstage-software-templates-beta.gif) + +**TLDR**; At a high level, Backstage performs three main jobs: [Create, Manage, and Explore](https://backstage.io/blog/2021/05/20/adopting-backstage#three-jobs-create-manage-explore). Throughout the year, we’ve been focussed on improving the stability and scalability of these areas while enhancing the feature sets based upon requests from the community. Software Templates — which enable push-button deployments with all your standards and best practices built-in — is our first feature-complete release candidate to graduate to beta. Read on to learn how this release improves template customization and flexibility, as well as performance. + + + +Let’s rewind a little bit, around a year in fact. Backstage had been open sourced, and we released our first version of Software Templates and our Scaffolder plugin. + +In case you missed it, [Software Templates](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates) are part of the Create feature set, where you can set up Backstage to give your engineers the ability to spin up a new microservice, website, or any other software component with your organization’s best practices built-in, right from the start. + +Back when we released the first version of these templates, with the `apiVersion` `backstage.io/v1alpha1`, they were a little restrictive in what they could do. The generated repository could only have one template source and it was restricted to only being [cookiecutter](https://github.com/cookiecutter/cookiecutter) templates. And it had to fit into the three stages of templating which was `Prepare`, `Template`, and `Publish`. + +We [opened up for feedback](https://github.com/backstage/backstage/issues/2771) from the community knowing that this wasn’t going to be the final version, but something of a stepping stone to where we want to be in the future. + +And now we come to today… + +## Anddddd… Action(s)! 🎬 + +Following the removal of the previously deprecated `backstage.io/v1alpha1` templates, today we’re officially announcing the beta version of Software Templates, or `backstage.io/v1beta2`. + +With it comes an entirely new architecture to the Software Templates functionality, and a load of new exciting features, which I’ll dig into a little deeper. + +### Template actions + +We’ve totally opened up the ability to compose your own workflows for each Template that you have registered in Software Templates. This means no restrictions on where your template skeleton is kept, and no restrictions on what your workflow should look like. + +Template Actions are customizable JavaScript functions that allow you to extend the capabilities of the Software Templates by adding your own business logic or reusing some of our Built-In actions. + +![New template syntax with actions](assets/21-07-26/example-template-code.png) + +_An example of the new template syntax with steps and parameters_ + +The workflow for Software Templates are now step-based, and each Software Template can have a customizable amount of steps which will in turn call these new actions. + +![New steps being rendered in the frontend](assets/21-07-26/steps-running.gif) + +_An example of a Template with customizable steps_ + +These actions are very heavily inspired by GitHub Actions and GitHub Workflows, and we’ve given back a lot of control to the template authors so they can build the boilerplate and push the code however they want. + +### Custom UI extensions + +A large part of the Software Templates feature is the form that we use to collect user input which is then passed on to these new actions. Sometimes, it makes sense that Template Authors want to provide their own React components which can be rendered in the frontend to maybe do some more complex things like calling internal APIs to provide autocomplete. + +This is now possible with the new `ScaffolderFieldExtensions`. + +![Custom UI extensions](assets/21-07-26/custom-components.gif) + +_An example of `ScaffolderFieldExtension` is the `OwnerPicker` component which can automatically add options and suggest typeahead with data from an async API request_ + +Template Authors can create their own components to provide data to the custom actions in any way you see fit, and even override our own built in ones too like the `RepoUrlPicker`. + +### Horizontal scalability + +The Scaffolder Service and Software Templates are now horizontally scalable, coordinating work between your deployed backend machines. This means now that the number of instances of the `@backstage/plugin-scaffolder-backend` plugin you have, is the same amount of jobs that you can have in parallel. This ensures that your tasks are handled quickly and reliably, while providing great feedback in the process. + +## Try out the new templates + +We’re still on the journey to making the Software Templates a great user experience for both authors and developers — but we’re not done yet, so watch this space for new exciting features to come! + +In the meantime, make sure to visit the documentation for [building templates](https://backstage.io/docs/features/software-templates/adding-templates) or creating your own [custom actions](https://backstage.io/docs/features/software-templates/writing-custom-actions). diff --git a/microsite/blog/assets/21-07-26/backstage-software-templates-beta.gif b/microsite/blog/assets/21-07-26/backstage-software-templates-beta.gif new file mode 100644 index 0000000000..f67113f44c Binary files /dev/null and b/microsite/blog/assets/21-07-26/backstage-software-templates-beta.gif differ diff --git a/microsite/blog/assets/21-07-26/custom-components.gif b/microsite/blog/assets/21-07-26/custom-components.gif new file mode 100644 index 0000000000..d9c78db33f Binary files /dev/null and b/microsite/blog/assets/21-07-26/custom-components.gif differ diff --git a/microsite/blog/assets/21-07-26/example-template-code.png b/microsite/blog/assets/21-07-26/example-template-code.png new file mode 100644 index 0000000000..1205adf657 Binary files /dev/null and b/microsite/blog/assets/21-07-26/example-template-code.png differ diff --git a/microsite/blog/assets/21-07-26/steps-running.gif b/microsite/blog/assets/21-07-26/steps-running.gif new file mode 100644 index 0000000000..d824464bac Binary files /dev/null and b/microsite/blog/assets/21-07-26/steps-running.gif differ diff --git a/microsite/core/Footer.js b/microsite/core/Footer.js index 8e69b7ff6f..1ea591b1d5 100644 --- a/microsite/core/Footer.js +++ b/microsite/core/Footer.js @@ -44,6 +44,10 @@ class Footer extends React.Component { Contributing + Adopting + + Community Sessions + Subscribe to our newsletter diff --git a/microsite/package.json b/microsite/package.json index eee549fb70..532bb62d15 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -16,7 +16,7 @@ "lock:check": "yarn-lock-check" }, "devDependencies": { - "@spotify/prettier-config": "^10.0.0", + "@spotify/prettier-config": "^11.0.0", "docusaurus": "^2.0.0-alpha.70", "js-yaml": "^4.1.0", "prettier": "^2.3.2", diff --git a/microsite/pages/en/index.js b/microsite/pages/en/index.js index d7325bb234..ec80bbc01f 100644 --- a/microsite/pages/en/index.js +++ b/microsite/pages/en/index.js @@ -212,9 +212,9 @@ class Index extends React.Component { Backstage Software Templates{' '} - (alpha) + (beta) Standards can set you free @@ -292,7 +292,7 @@ class Index extends React.Component { Build your own software templates Contribute diff --git a/microsite/static/img/backstage-k8s.svg b/microsite/static/img/backstage-k8s.svg index 2796a396e7..28b6ef8f92 100644 --- a/microsite/static/img/backstage-k8s.svg +++ b/microsite/static/img/backstage-k8s.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/backstage-software-catalog.svg b/microsite/static/img/backstage-software-catalog.svg index 101439497b..5cad498d35 100644 --- a/microsite/static/img/backstage-software-catalog.svg +++ b/microsite/static/img/backstage-software-catalog.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/backstage-software-templates.svg b/microsite/static/img/backstage-software-templates.svg index 3984cd2870..e64428de82 100644 --- a/microsite/static/img/backstage-software-templates.svg +++ b/microsite/static/img/backstage-software-templates.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/backstage-techdocs.svg b/microsite/static/img/backstage-techdocs.svg index aa9362b1bc..643cde325c 100644 --- a/microsite/static/img/backstage-techdocs.svg +++ b/microsite/static/img/backstage-techdocs.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/rails-icon.png b/microsite/static/img/rails-icon.png index f7b8b69bd9..04f63766fe 100644 Binary files a/microsite/static/img/rails-icon.png and b/microsite/static/img/rails-icon.png differ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 1da9288808..0f7ddb9c60 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -909,10 +909,10 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@spotify/prettier-config@^10.0.0": - version "10.0.0" - resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-10.0.0.tgz#fa076d98d2e7e6c53dd3d86a696307a7010bd056" - integrity sha512-VYOdo8P7lIScAkl02nB9KpUAuOYMManryBIBuKJkAw5D3aVtLobfmdIKvdV6MqEmGMEQPbn7w/UpnjJYhUH+IA== +"@spotify/prettier-config@^11.0.0": + version "11.0.0" + resolved "https://registry.npmjs.org/@spotify/prettier-config/-/prettier-config-11.0.0.tgz#d91e0546a8c1c0f7299e2edc7e44306e9be210f6" + integrity sha512-dOI13j1uHMZkRxhZuge/ugOE7Aqcg7Nxki932lDZuXyY4G8CGxkc/66PeQ8pR4PCzThHORXo7Ptvau6bh101lQ== "@types/cheerio@^0.22.8": version "0.22.23" diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index cc67b78f44..494e7dbad2 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,35 @@ # example-app +## 0.2.37 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@0.6.7 + - @backstage/plugin-catalog-react@0.3.0 + - @backstage/plugin-scaffolder@0.10.1 + - @backstage/plugin-techdocs@0.10.0 + - @backstage/plugin-jenkins@0.5.0 + - @backstage/plugin-user-settings@0.3.0 + - @backstage/plugin-search@0.4.3 + - @backstage/cli@0.7.4 + - @backstage/plugin-api-docs@0.6.2 + - @backstage/plugin-badges@0.2.5 + - @backstage/plugin-catalog-import@0.5.13 + - @backstage/plugin-circleci@0.2.19 + - @backstage/plugin-cloudbuild@0.2.19 + - @backstage/plugin-code-coverage@0.1.7 + - @backstage/plugin-explore@0.3.10 + - @backstage/plugin-github-actions@0.4.13 + - @backstage/plugin-kafka@0.2.11 + - @backstage/plugin-kubernetes@0.4.8 + - @backstage/plugin-lighthouse@0.2.20 + - @backstage/plugin-org@0.3.17 + - @backstage/plugin-pagerduty@0.3.8 + - @backstage/plugin-rollbar@0.3.9 + - @backstage/plugin-sentry@0.3.15 + - @backstage/plugin-todo@0.1.5 + ## 0.2.36 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index b26e94fc26..388635e56b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,52 +1,54 @@ { "name": "example-app", - "version": "0.2.36", + "version": "0.2.37", "private": true, "bundled": true, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/cli": "^0.7.2", + "@backstage/cli": "^0.7.4", "@backstage/core-app-api": "^0.1.3", "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-api-docs": "^0.6.1", - "@backstage/plugin-badges": "^0.2.4", - "@backstage/plugin-catalog": "^0.6.6", - "@backstage/plugin-catalog-import": "^0.5.12", - "@backstage/plugin-catalog-react": "^0.2.6", - "@backstage/plugin-circleci": "^0.2.18", - "@backstage/plugin-cloudbuild": "^0.2.18", - "@backstage/plugin-code-coverage": "^0.1.6", + "@backstage/plugin-api-docs": "^0.6.2", + "@backstage/plugin-badges": "^0.2.5", + "@backstage/plugin-catalog": "^0.6.7", + "@backstage/plugin-catalog-import": "^0.5.13", + "@backstage/plugin-catalog-react": "^0.3.0", + "@backstage/plugin-circleci": "^0.2.19", + "@backstage/plugin-cloudbuild": "^0.2.19", + "@backstage/plugin-code-coverage": "^0.1.7", "@backstage/plugin-cost-insights": "^0.11.0", - "@backstage/plugin-explore": "^0.3.9", + "@backstage/plugin-explore": "^0.3.10", "@backstage/plugin-gcp-projects": "^0.3.0", - "@backstage/plugin-github-actions": "^0.4.12", + "@backstage/plugin-github-actions": "^0.4.13", "@backstage/plugin-graphiql": "^0.2.12", - "@backstage/plugin-jenkins": "^0.4.7", - "@backstage/plugin-kafka": "^0.2.10", - "@backstage/plugin-kubernetes": "^0.4.7", - "@backstage/plugin-lighthouse": "^0.2.19", + "@backstage/plugin-jenkins": "^0.5.0", + "@backstage/plugin-kafka": "^0.2.11", + "@backstage/plugin-kubernetes": "^0.4.8", + "@backstage/plugin-lighthouse": "^0.2.20", "@backstage/plugin-newrelic": "^0.3.0", - "@backstage/plugin-org": "^0.3.16", - "@backstage/plugin-pagerduty": "0.3.7", - "@backstage/plugin-rollbar": "^0.3.8", - "@backstage/plugin-scaffolder": "^0.10.0", - "@backstage/plugin-search": "^0.4.2", - "@backstage/plugin-sentry": "^0.3.14", + "@backstage/plugin-org": "^0.3.17", + "@backstage/plugin-pagerduty": "0.3.8", + "@backstage/plugin-rollbar": "^0.3.9", + "@backstage/plugin-scaffolder": "^0.10.1", + "@backstage/plugin-search": "^0.4.3", + "@backstage/plugin-sentry": "^0.3.15", "@backstage/plugin-shortcuts": "^0.1.4", "@backstage/plugin-tech-radar": "^0.4.1", - "@backstage/plugin-techdocs": "^0.9.9", - "@backstage/plugin-todo": "^0.1.4", - "@backstage/plugin-user-settings": "^0.2.12", + "@backstage/plugin-techdocs": "^0.10.0", + "@backstage/plugin-todo": "^0.1.5", + "@backstage/plugin-user-settings": "^0.3.0", + "@backstage/search-common": "^0.1.2", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", "@octokit/rest": "^18.5.3", - "@roadiehq/backstage-plugin-buildkite": "^1.0.4", - "@roadiehq/backstage-plugin-github-insights": "^1.1.15", - "@roadiehq/backstage-plugin-github-pull-requests": "^1.0.8", - "@roadiehq/backstage-plugin-travis-ci": "^1.0.4", + "@roadiehq/backstage-plugin-buildkite": "^1.0.6", + "@roadiehq/backstage-plugin-github-insights": "^1.1.20", + "@roadiehq/backstage-plugin-github-pull-requests": "^1.0.10", + "@roadiehq/backstage-plugin-travis-ci": "^1.0.8", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 16107a6cc0..4e2e32cc86 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -135,6 +135,13 @@ const EntityLayoutWrapper = (props: { children?: ReactNode }) => { ); }; +/** + * NOTE: This page is designed to work on small screens such as mobile devices. + * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`, + * since this does not default. If no breakpoints are used, the items will equitably share the asvailable space. + * https://material-ui.com/components/grid/#basic-grid. + */ + export const cicdContent = ( @@ -292,10 +299,10 @@ const serviceEntityPage = ( - + - + @@ -303,10 +310,10 @@ const serviceEntityPage = ( - + - + @@ -431,15 +438,17 @@ const apiPage = ( - + - - - - - - + + + + + + + + diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 35c4a4ac1c..6fe02de837 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -14,17 +14,20 @@ * limitations under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core'; - +import Pagination from '@material-ui/lab/Pagination'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; import { SearchBar, SearchFilter, SearchResult, + SearchType, DefaultResultListItem, } from '@backstage/plugin-search'; import { Content, Header, Lifecycle, Page } from '@backstage/core-components'; +import { DocsResultListItem } from '@backstage/plugin-techdocs'; +import { SearchResultSet } from '@backstage/search-common'; const useStyles = makeStyles((theme: Theme) => ({ bar: { @@ -34,15 +37,63 @@ const useStyles = makeStyles((theme: Theme) => ({ padding: theme.spacing(2), }, filter: { - '& + &': { - marginTop: theme.spacing(2.5), - }, + marginTop: theme.spacing(2.5), }, })); +// TODO: Move this into the search plugin once pagination is natively supported. +// See: https://github.com/backstage/backstage/issues/6062 +const SearchResultList = ({ results }: SearchResultSet) => { + const pageSize = 10; + const [page, setPage] = useState(1); + const changePage = (_: any, pageIndex: number) => { + setPage(pageIndex); + }; + const pageAmount = Math.ceil((results.length || 0) / pageSize); + return ( + <> + + {results + .slice(pageSize * (page - 1), pageSize * page) + .map(({ type, document }) => { + switch (type) { + case 'software-catalog': + return ( + + ); + case 'techdocs': + return ( + + ); + default: + return ( + + ); + } + })} + + + + ); +}; + const SearchPage = () => { const classes = useStyles(); - return (
} /> @@ -55,6 +106,11 @@ const SearchPage = () => { + { - {({ results }) => ( - - {results.map(({ type, document }) => { - switch (type) { - case 'software-catalog': - return ( - - ); - default: - return ( - - ); - } - })} - - )} + {({ results }) => } diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 93016bd13c..582badb685 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/backend-common +## 0.8.7 + +### Patch Changes + +- f25357273: Implement the etag functionality in the `readUrl` method of `FetchUrlReader`. +- bdd6ab5f1: It's possible to customize the request logging handler when building the service. For example in your `backend` + + ``` + const service = createServiceBuilder(module) + .loadConfig(config) + .setRequestLoggingHandler((logger?: Logger): RequestHandler => { + const actualLogger = (logger || getRootLogger()).child({ + type: 'incomingRequest', + }); + return expressWinston.logger({ ... + ``` + +## 0.8.6 + +### Patch Changes + +- 5f6f2fd96: Support a `ensureExists` config option to skip ensuring a configured database exists. This allows deployment scenarios where + limited permissions are given for provisioned databases without privileges to create new databases. If set to `false`, the + database connection will not be validated prior to use which means the backend will not attempt to create the database if it + doesn't exist. You can configure this in your app-config.yaml: + + ```yaml + backend: + database: + ensureExists: false + ``` + + This defaults to `true` if unspecified. You can also configure this per plugin connection and will override the base option. + +- ad93bb035: Document the default behavior of `statusCheck` option in `createStatusCheckRouter`. +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/config-loader@0.6.5 + ## 0.8.5 ### Patch Changes diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 94f1147b5a..2652dad122 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -32,6 +32,8 @@ import { Server } from 'http'; import * as winston from 'winston'; import { Writable } from 'stream'; +// Warning: (ae-missing-release-tag) "AzureUrlReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class AzureUrlReader implements UrlReader { constructor( @@ -40,20 +42,31 @@ export class AzureUrlReader implements UrlReader { treeResponseFactory: ReadTreeResponseFactory; }, ); + // Warning: (ae-forgotten-export) The symbol "ReaderFactory" needs to be exported by the entry point index.d.ts + // // (undocumented) static factory: ReaderFactory; // (undocumented) read(url: string): Promise; + // Warning: (ae-forgotten-export) The symbol "ReadTreeOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) readTree(url: string, options?: ReadTreeOptions): Promise; + // Warning: (ae-forgotten-export) The symbol "ReadUrlOptions" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "ReadUrlResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) readUrl(url: string, _options?: ReadUrlOptions): Promise; + // Warning: (ae-forgotten-export) The symbol "SearchOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) search(url: string, options?: SearchOptions): Promise; // (undocumented) toString(): string; } +// Warning: (ae-missing-release-tag) "BitbucketUrlReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class BitbucketUrlReader implements UrlReader { constructor( @@ -76,60 +89,92 @@ export class BitbucketUrlReader implements UrlReader { toString(): string; } +// Warning: (ae-missing-release-tag) "CacheClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface CacheClient { delete(key: string): Promise; get(key: string): Promise; + // Warning: (ae-forgotten-export) The symbol "CacheSetOptions" needs to be exported by the entry point index.d.ts set(key: string, value: JsonValue, options?: CacheSetOptions): Promise; } +// Warning: (ae-missing-release-tag) "CacheManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class CacheManager { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen forPlugin(pluginId: string): PluginCacheManager; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (ae-forgotten-export) The symbol "CacheManagerOptions" needs to be exported by the entry point index.d.ts static fromConfig( config: Config, options?: CacheManagerOptions, ): CacheManager; } +// Warning: (ae-missing-release-tag) "coloredFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const coloredFormat: winston.Logform.Format; +// Warning: (ae-missing-release-tag) "ContainerRunner" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ContainerRunner { // (undocumented) runContainer(opts: RunContainerOptions): Promise; } +// Warning: (ae-missing-release-tag) "createDatabase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated export const createDatabase: typeof createDatabaseClient; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "createDatabaseClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createDatabaseClient( dbConfig: Config, overrides?: Partial, ): Knex; +// Warning: (ae-missing-release-tag) "createRootLogger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRootLogger( options?: winston.LoggerOptions, env?: NodeJS.ProcessEnv, ): winston.Logger; +// Warning: (ae-forgotten-export) The symbol "ServiceBuilderImpl" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createServiceBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createServiceBuilder(_module: NodeModule): ServiceBuilderImpl; +// Warning: (ae-forgotten-export) The symbol "StatusCheckRouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createStatusCheckRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createStatusCheckRouter( options: StatusCheckRouterOptions, ): Promise; +// Warning: (ae-missing-release-tag) "DatabaseManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DatabaseManager { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen forPlugin(pluginId: string): PluginDatabaseManager; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static fromConfig(config: Config): DatabaseManager; } +// Warning: (ae-missing-release-tag) "DockerContainerRunner" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DockerContainerRunner implements ContainerRunner { constructor({ dockerClient }: { dockerClient: Docker }); @@ -142,20 +187,27 @@ export class DockerContainerRunner implements ContainerRunner { mountDirs, workingDir, envVars, + pullImage, }: RunContainerOptions): Promise; } +// Warning: (ae-missing-release-tag) "ensureDatabaseExists" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function ensureDatabaseExists( dbConfig: Config, ...databases: Array ): Promise; +// Warning: (ae-missing-release-tag) "errorHandler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function errorHandler( options?: ErrorHandlerOptions, ): ErrorRequestHandler; +// Warning: (ae-missing-release-tag) "ErrorHandlerOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ErrorHandlerOptions = { showStackTraces?: boolean; @@ -163,12 +215,18 @@ export type ErrorHandlerOptions = { logClientErrors?: boolean; }; +// Warning: (ae-missing-release-tag) "getRootLogger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getRootLogger(): winston.Logger; +// Warning: (ae-missing-release-tag) "getVoidLogger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getVoidLogger(): winston.Logger; +// Warning: (ae-missing-release-tag) "Git" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class Git { // (undocumented) @@ -273,6 +331,8 @@ export class Git { resolveRef({ dir, ref }: { dir: string; ref: string }): Promise; } +// Warning: (ae-missing-release-tag) "GithubUrlReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class GithubUrlReader implements UrlReader { constructor( @@ -296,6 +356,8 @@ export class GithubUrlReader implements UrlReader { toString(): string; } +// Warning: (ae-missing-release-tag) "GitlabUrlReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GitlabUrlReader implements UrlReader { constructor( @@ -320,28 +382,41 @@ export class GitlabUrlReader implements UrlReader { export { isChildPath }; +// Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "loadBackendConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function loadBackendConfig(options: Options): Promise; +// Warning: (ae-missing-release-tag) "notFoundHandler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function notFoundHandler(): RequestHandler; +// Warning: (ae-missing-release-tag) "PluginCacheManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type PluginCacheManager = { getClient: (options?: ClientOptions) => CacheClient; }; +// Warning: (ae-missing-release-tag) "PluginDatabaseManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface PluginDatabaseManager { getClient(): Promise; } +// Warning: (ae-missing-release-tag) "PluginEndpointDiscovery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type PluginEndpointDiscovery = { getBaseUrl(pluginId: string): Promise; getExternalBaseUrl(pluginId: string): Promise; }; +// Warning: (ae-missing-release-tag) "ReadTreeResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ReadTreeResponse = { files(): Promise; @@ -350,21 +425,41 @@ export type ReadTreeResponse = { etag: string; }; +// Warning: (ae-missing-release-tag) "ReadTreeResponseFile" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ReadTreeResponseFile = { path: string; content(): Promise; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "requestLoggingHandler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function requestLoggingHandler(logger?: Logger_2): RequestHandler; +// Warning: (ae-missing-release-tag) "RequestLoggingHandlerFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RequestLoggingHandlerFactory = ( + logger?: Logger_2, +) => RequestHandler; + +// Warning: (ae-missing-release-tag) "resolvePackagePath" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function resolvePackagePath(name: string, ...paths: string[]): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "resolveSafeChildPath" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function resolveSafeChildPath(base: string, path: string): string; +// Warning: (ae-missing-release-tag) "RunContainerOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type RunContainerOptions = { imageName: string; @@ -374,20 +469,27 @@ export type RunContainerOptions = { mountDirs?: Record; workingDir?: string; envVars?: Record; + pullImage?: boolean; }; +// Warning: (ae-missing-release-tag) "SearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type SearchResponse = { files: SearchResponseFile[]; etag: string; }; +// Warning: (ae-missing-release-tag) "SearchResponseFile" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type SearchResponseFile = { url: string; content(): Promise; }; +// Warning: (ae-missing-release-tag) "ServiceBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ServiceBuilder = { loadConfig(config: ConfigReader): ServiceBuilder; @@ -397,15 +499,24 @@ export type ServiceBuilder = { enableCors(options: cors.CorsOptions): ServiceBuilder; setHttpsSettings(settings: HttpsSettings): ServiceBuilder; addRouter(root: string, router: Router | RequestHandler): ServiceBuilder; + setRequestLoggingHandler( + requestLoggingHandler: RequestLoggingHandlerFactory, + ): ServiceBuilder; start(): Promise; }; +// Warning: (ae-missing-release-tag) "setRootLogger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function setRootLogger(newLogger: winston.Logger): void; +// Warning: (ae-missing-release-tag) "SingleConnectionDatabaseManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated export const SingleConnectionDatabaseManager: typeof DatabaseManager; +// Warning: (ae-missing-release-tag) "SingleHostDiscovery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class SingleHostDiscovery implements PluginEndpointDiscovery { static fromConfig( @@ -420,19 +531,28 @@ export class SingleHostDiscovery implements PluginEndpointDiscovery { getExternalBaseUrl(pluginId: string): Promise; } +// Warning: (ae-missing-release-tag) "StatusCheck" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type StatusCheck = () => Promise; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "statusCheckHandler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function statusCheckHandler( options?: StatusCheckHandlerOptions, ): Promise; +// Warning: (ae-missing-release-tag) "StatusCheckHandlerOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface StatusCheckHandlerOptions { statusCheck?: StatusCheck; } +// Warning: (ae-missing-release-tag) "UrlReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type UrlReader = { read(url: string): Promise; @@ -441,20 +561,49 @@ export type UrlReader = { search(url: string, options?: SearchOptions): Promise; }; +// Warning: (ae-missing-release-tag) "UrlReaders" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class UrlReaders { + // Warning: (ae-forgotten-export) The symbol "CreateOptions" needs to be exported by the entry point index.d.ts static create({ logger, config, factories }: CreateOptions): UrlReader; static default({ logger, config, factories }: CreateOptions): UrlReader; } +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "useHotCleanup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function useHotCleanup( _module: NodeModule, cancelEffect: () => void, ): void; +// Warning: (tsdoc-undefined-tag) The TSDoc tag "@warning" is not defined in this configuration +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "useHotMemoize" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function useHotMemoize(_module: NodeModule, valueFactory: () => T): T; +// Warnings were encountered during analysis: +// +// src/cache/types.d.ts:34:5 - (ae-forgotten-export) The symbol "ClientOptions" needs to be exported by the entry point index.d.ts +// src/middleware/errorHandler.d.ts:17:26 - (tsdoc-malformed-html-name) Invalid HTML element: A space is not allowed here +// src/reading/AzureUrlReader.d.ts:9:9 - (ae-forgotten-export) The symbol "ReadTreeResponseFactory" needs to be exported by the entry point index.d.ts +// src/reading/types.d.ts:106:5 - (ae-forgotten-export) The symbol "ReadTreeResponseDirOptions" needs to be exported by the entry point index.d.ts +// src/service/types.d.ts:12:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:22:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:30:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:38:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:47:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:55:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:57:5 - (ae-forgotten-export) The symbol "HttpsSettings" needs to be exported by the entry point index.d.ts +// src/service/types.d.ts:61:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:62:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/service/types.d.ts:70:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 6cf20b8234..51ef8128e7 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.8.5", + "version": "0.8.7", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -31,7 +31,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.2", "@backstage/config": "^0.1.5", - "@backstage/config-loader": "^0.6.4", + "@backstage/config-loader": "^0.6.5", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", "@google-cloud/storage": "^5.8.0", @@ -76,8 +76,8 @@ } }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/test-utils": "^0.1.12", + "@backstage/cli": "^0.7.5", + "@backstage/test-utils": "^0.1.15", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", diff --git a/packages/backend-common/src/reading/FetchUrlReader.test.ts b/packages/backend-common/src/reading/FetchUrlReader.test.ts index 169cbfbf66..e8c16c5f0a 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.test.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.test.ts @@ -15,12 +15,16 @@ */ import { ConfigReader } from '@backstage/config'; +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { msw } from '@backstage/test-utils'; +import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { getVoidLogger } from '../logging'; import { FetchUrlReader } from './FetchUrlReader'; import { DefaultReadTreeResponseFactory } from './tree'; +const fetchUrlReader = new FetchUrlReader(); + describe('FetchUrlReader', () => { const worker = setupServer(); @@ -30,6 +34,39 @@ describe('FetchUrlReader', () => { jest.clearAllMocks(); }); + beforeEach(() => { + worker.use( + rest.get('https://backstage.io/some-resource', (req, res, ctx) => { + if (req.headers.get('if-none-match') === 'foo') { + return res( + ctx.status(304), + ctx.set('Content-Type', 'text/plain'), + ctx.set('etag', 'foo'), + ); + } + + return res( + ctx.status(200), + ctx.set('Content-Type', 'text/plain'), + ctx.set('etag', 'foo'), + ctx.body('content foo'), + ); + }), + ); + + worker.use( + rest.get('https://backstage.io/not-exists', (_req, res, ctx) => { + return res(ctx.status(404)); + }), + ); + + worker.use( + rest.get('https://backstage.io/error', (_req, res, ctx) => { + return res(ctx.status(500), ctx.body('An internal error occured')); + }), + ); + }); + it('factory should create a single entry with a predicate that matches config', async () => { const entries = FetchUrlReader.factory({ config: new ConfigReader({ @@ -70,4 +107,55 @@ describe('FetchUrlReader', () => { expect(predicate(new URL('https://a.examples.org:700/test'))).toBe(true); expect(predicate(new URL('https://a.b.examples.org:700/test'))).toBe(true); }); + + describe('read', () => { + it('should return etag from the response', async () => { + const buffer = await fetchUrlReader.read( + 'https://backstage.io/some-resource', + ); + expect(buffer.toString()).toBe('content foo'); + }); + + it('should throw NotFound if server responds with 404', async () => { + await expect( + fetchUrlReader.read('https://backstage.io/not-exists'), + ).rejects.toThrow(NotFoundError); + }); + + it('should throw Error if server responds with 500', async () => { + await expect( + fetchUrlReader.read('https://backstage.io/error'), + ).rejects.toThrow(Error); + }); + }); + + describe('readUrl', () => { + it('should throw NotModified if server responds with 304', async () => { + await expect( + fetchUrlReader.readUrl('https://backstage.io/some-resource', { + etag: 'foo', + }), + ).rejects.toThrow(NotModifiedError); + }); + + it('should return etag from the response', async () => { + const response = await fetchUrlReader.readUrl( + 'https://backstage.io/some-resource', + ); + expect(response.etag).toBe('foo'); + expect((await response.buffer()).toString()).toEqual('content foo'); + }); + + it('should throw NotFound if server responds with 404', async () => { + await expect( + fetchUrlReader.readUrl('https://backstage.io/not-exists'), + ).rejects.toThrow(NotFoundError); + }); + + it('should throw Error if server responds with 500', async () => { + await expect( + fetchUrlReader.readUrl('https://backstage.io/error'), + ).rejects.toThrow(Error); + }); + }); }); diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index 30468158aa..3177ee1f8e 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -14,8 +14,8 @@ * limitations under the License. */ +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import fetch from 'cross-fetch'; -import { NotFoundError } from '@backstage/errors'; import { ReaderFactory, ReadTreeResponse, @@ -57,15 +57,34 @@ export class FetchUrlReader implements UrlReader { }; async read(url: string): Promise { + const response = await this.readUrl(url); + return response.buffer(); + } + + async readUrl( + url: string, + options?: ReadUrlOptions, + ): Promise { let response: Response; try { - response = await fetch(url); + response = await fetch(url, { + headers: { + ...(options?.etag && { 'If-None-Match': options.etag }), + }, + }); } catch (e) { throw new Error(`Unable to read ${url}, ${e}`); } + if (response.status === 304) { + throw new NotModifiedError(); + } + if (response.ok) { - return Buffer.from(await response.text()); + return { + buffer: async () => Buffer.from(await response.text()), + etag: response.headers.get('ETag') ?? undefined, + }; } const message = `could not read ${url}, ${response.status} ${response.statusText}`; @@ -75,15 +94,6 @@ export class FetchUrlReader implements UrlReader { throw new Error(message); } - async readUrl( - url: string, - _options?: ReadUrlOptions, - ): Promise { - // TODO etag is not implemented yet. - const buffer = await this.read(url); - return { buffer: async () => buffer }; - } - async readTree(): Promise { throw new Error('FetchUrlReader does not implement readTree'); } diff --git a/packages/backend-common/src/service/index.ts b/packages/backend-common/src/service/index.ts index 4eb0bf4a5a..d01f25fad3 100644 --- a/packages/backend-common/src/service/index.ts +++ b/packages/backend-common/src/service/index.ts @@ -16,4 +16,4 @@ export { createServiceBuilder } from './createServiceBuilder'; export { createStatusCheckRouter } from './createStatusCheckRouter'; -export type { ServiceBuilder } from './types'; +export type { ServiceBuilder, RequestLoggingHandlerFactory } from './types'; diff --git a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts index 380d61abc8..7a3ebfed70 100644 --- a/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts +++ b/packages/backend-common/src/service/lib/ServiceBuilderImpl.ts @@ -27,9 +27,9 @@ import { getRootLogger } from '../../logging'; import { errorHandler, notFoundHandler, - requestLoggingHandler, + requestLoggingHandler as defaultRequestLoggingHandler, } from '../../middleware'; -import { ServiceBuilder } from '../types'; +import { RequestLoggingHandlerFactory, ServiceBuilder } from '../types'; import { CspOptions, HttpsSettings, @@ -65,6 +65,7 @@ export class ServiceBuilderImpl implements ServiceBuilder { private cspOptions: Record | undefined; private httpsSettings: HttpsSettings | undefined; private routers: [string, Router][]; + private requestLoggingHandler: RequestLoggingHandlerFactory | undefined; // Reference to the module where builder is created - needed for hot module // reloading private module: NodeModule; @@ -144,6 +145,13 @@ export class ServiceBuilderImpl implements ServiceBuilder { return this; } + setRequestLoggingHandler( + requestLoggingHandler: RequestLoggingHandlerFactory, + ) { + this.requestLoggingHandler = requestLoggingHandler; + return this; + } + async start(): Promise { const app = express(); const { @@ -160,7 +168,9 @@ export class ServiceBuilderImpl implements ServiceBuilder { app.use(cors(corsOptions)); } app.use(compression()); - app.use(requestLoggingHandler(logger)); + app.use( + (this.requestLoggingHandler ?? defaultRequestLoggingHandler)(logger), + ); for (const [root, route] of this.routers) { app.use(root, route); } diff --git a/packages/backend-common/src/service/types.ts b/packages/backend-common/src/service/types.ts index 70f62acfca..f845397ca2 100644 --- a/packages/backend-common/src/service/types.ts +++ b/packages/backend-common/src/service/types.ts @@ -85,8 +85,21 @@ export type ServiceBuilder = { */ addRouter(root: string, router: Router | RequestHandler): ServiceBuilder; + /** + * Set the request logging handler + * + * If no handler is given the default one is used + * + * @param requestLoggingHandler a factory function that given a logger returns an handler + */ + setRequestLoggingHandler( + requestLoggingHandler: RequestLoggingHandlerFactory, + ): ServiceBuilder; + /** * Starts the server using the given settings. */ start(): Promise; }; + +export type RequestLoggingHandlerFactory = (logger?: Logger) => RequestHandler; diff --git a/packages/backend-common/src/util/ContainerRunner.ts b/packages/backend-common/src/util/ContainerRunner.ts index 80ac4e3954..8a73be42ba 100644 --- a/packages/backend-common/src/util/ContainerRunner.ts +++ b/packages/backend-common/src/util/ContainerRunner.ts @@ -24,6 +24,7 @@ export type RunContainerOptions = { mountDirs?: Record; workingDir?: string; envVars?: Record; + pullImage?: boolean; }; export interface ContainerRunner { diff --git a/packages/backend-common/src/util/DockerContainerRunner.test.ts b/packages/backend-common/src/util/DockerContainerRunner.test.ts index 0ed41d5cd3..8833e4518b 100644 --- a/packages/backend-common/src/util/DockerContainerRunner.test.ts +++ b/packages/backend-common/src/util/DockerContainerRunner.test.ts @@ -58,6 +58,7 @@ describe('DockerContainerRunner', () => { }); afterEach(() => { + jest.clearAllMocks(); mockFs.restore(); }); @@ -86,6 +87,17 @@ describe('DockerContainerRunner', () => { expect(mockDocker.run).toHaveBeenCalled(); }); + it('should not pull the docker container when pullImage is false', async () => { + await containerTaskApi.runContainer({ + imageName, + args, + pullImage: false, + }); + + expect(mockDocker.pull).not.toHaveBeenCalled(); + expect(mockDocker.run).toHaveBeenCalled(); + }); + it('should call the dockerClient run command with the correct arguments passed through', async () => { await containerTaskApi.runContainer({ imageName, diff --git a/packages/backend-common/src/util/DockerContainerRunner.ts b/packages/backend-common/src/util/DockerContainerRunner.ts index 6ec366eb94..8dbc5335c9 100644 --- a/packages/backend-common/src/util/DockerContainerRunner.ts +++ b/packages/backend-common/src/util/DockerContainerRunner.ts @@ -38,6 +38,7 @@ export class DockerContainerRunner implements ContainerRunner { mountDirs = {}, workingDir, envVars = {}, + pullImage = true, }: RunContainerOptions) { // Show a better error message when Docker is unavailable. try { @@ -48,15 +49,17 @@ export class DockerContainerRunner implements ContainerRunner { ); } - await new Promise((resolve, reject) => { - this.dockerClient.pull(imageName, {}, (err, stream) => { - if (err) return reject(err); - stream.pipe(logStream, { end: false }); - stream.on('end', () => resolve()); - stream.on('error', (error: Error) => reject(error)); - return undefined; + if (pullImage) { + await new Promise((resolve, reject) => { + this.dockerClient.pull(imageName, {}, (err, stream) => { + if (err) return reject(err); + stream.pipe(logStream, { end: false }); + stream.on('end', () => resolve()); + stream.on('error', (error: Error) => reject(error)); + return undefined; + }); }); - }); + } const userOptions: UserOptions = {}; if (process.getuid && process.getgid) { diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index 184ed74978..411ecc7ddf 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -5,9 +5,13 @@ ```ts import { Knex } from 'knex'; +// Warning: (ae-missing-release-tag) "isDockerDisabledForTests" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function isDockerDisabledForTests(): boolean; +// Warning: (ae-missing-release-tag) "TestDatabaseId" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type TestDatabaseId = | 'POSTGRES_13' @@ -15,6 +19,8 @@ export type TestDatabaseId = | 'MYSQL_8' | 'SQLITE_3'; +// Warning: (ae-missing-release-tag) "TestDatabases" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class TestDatabases { static create(options?: { @@ -23,6 +29,7 @@ export class TestDatabases { }): TestDatabases; // (undocumented) eachSupportedId(): [TestDatabaseId][]; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen init(id: TestDatabaseId): Promise; // (undocumented) supports(id: TestDatabaseId): boolean; diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 47edc5107f..a3e08db6b3 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,36 @@ # example-backend +## 0.2.38 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-backend@0.3.11 + - @backstage/catalog-client@0.3.17 + - @backstage/plugin-auth-backend@0.3.18 + - @backstage/plugin-jenkins-backend@0.1.2 + - @backstage/backend-common@0.8.7 + - @backstage/plugin-techdocs-backend@0.9.0 + - @backstage/plugin-scaffolder-backend@0.14.1 + +## 0.2.37 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.8.6 + - @backstage/plugin-scaffolder-backend@0.14.0 + - @backstage/plugin-catalog-backend@0.13.0 + - @backstage/plugin-auth-backend@0.3.17 + - @backstage/plugin-scaffolder-backend-module-rails@0.1.3 + - @backstage/plugin-search-backend-node@0.4.0 + - @backstage/plugin-techdocs-backend@0.8.7 + - @backstage/plugin-app-backend@0.3.15 + - @backstage/plugin-kubernetes-backend@0.3.10 + - @backstage/plugin-rollbar-backend@0.1.13 + - example-app@0.2.37 + - @backstage/plugin-search-backend@0.2.3 + ## 0.2.36 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 4338f3e2b7..9fc455801b 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.36", + "version": "0.2.38", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,32 +27,33 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", - "@backstage/catalog-client": "^0.3.16", + "@backstage/backend-common": "^0.8.7", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/integration": "^0.5.8", - "@backstage/plugin-app-backend": "^0.3.13", - "@backstage/plugin-auth-backend": "^0.3.16", + "@backstage/plugin-app-backend": "^0.3.15", + "@backstage/plugin-auth-backend": "^0.3.18", "@backstage/plugin-badges-backend": "^0.1.8", - "@backstage/plugin-catalog-backend": "^0.12.0", + "@backstage/plugin-catalog-backend": "^0.13.0", "@backstage/plugin-code-coverage-backend": "^0.1.8", "@backstage/plugin-graphql-backend": "^0.1.8", - "@backstage/plugin-kubernetes-backend": "^0.3.9", + "@backstage/plugin-jenkins-backend": "^0.1.2", + "@backstage/plugin-kubernetes-backend": "^0.3.11", "@backstage/plugin-kafka-backend": "^0.2.8", "@backstage/plugin-proxy-backend": "^0.2.9", - "@backstage/plugin-rollbar-backend": "^0.1.11", - "@backstage/plugin-scaffolder-backend": "^0.13.0", - "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.2", - "@backstage/plugin-search-backend": "^0.2.2", - "@backstage/plugin-search-backend-node": "^0.3.0", - "@backstage/plugin-techdocs-backend": "^0.8.6", + "@backstage/plugin-rollbar-backend": "^0.1.13", + "@backstage/plugin-scaffolder-backend": "^0.14.1", + "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.3", + "@backstage/plugin-search-backend": "^0.2.3", + "@backstage/plugin-search-backend-node": "^0.4.0", + "@backstage/plugin-techdocs-backend": "^0.9.0", "@backstage/plugin-todo-backend": "^0.1.8", "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^10.2.2", "dockerode": "^3.2.1", - "example-app": "^0.2.36", + "example-app": "^0.2.37", "express": "^4.17.1", "express-promise-router": "^4.1.0", "knex": "^0.95.1", @@ -62,7 +63,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.5", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 168f6aa2d9..aecb233581 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -50,6 +50,7 @@ import todo from './plugins/todo'; import graphql from './plugins/graphql'; import app from './plugins/app'; import badges from './plugins/badges'; +import jenkins from './plugins/jenkins'; import { PluginEnvironment } from './types'; function makeCreateEnv(config: Config) { @@ -101,6 +102,7 @@ async function main() { const graphqlEnv = useHotMemoize(module, () => createEnv('graphql')); const appEnv = useHotMemoize(module, () => createEnv('app')); const badgesEnv = useHotMemoize(module, () => createEnv('badges')); + const jenkinsEnv = useHotMemoize(module, () => createEnv('jenkins')); const apiRouter = Router(); apiRouter.use('/catalog', await catalog(catalogEnv)); @@ -116,6 +118,7 @@ async function main() { apiRouter.use('/proxy', await proxy(proxyEnv)); apiRouter.use('/graphql', await graphql(graphqlEnv)); apiRouter.use('/badges', await badges(badgesEnv)); + apiRouter.use('/jenkins', await jenkins(jenkinsEnv)); apiRouter.use(notFoundHandler()); const service = createServiceBuilder(module) diff --git a/packages/backend/src/plugins/jenkins.ts b/packages/backend/src/plugins/jenkins.ts new file mode 100644 index 0000000000..5420eb0df6 --- /dev/null +++ b/packages/backend/src/plugins/jenkins.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createRouter, + DefaultJenkinsInfoProvider, +} from '@backstage/plugin-jenkins-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; +import { CatalogClient } from '@backstage/catalog-client'; + +export default async function createPlugin({ + logger, + config, + discovery, +}: PluginEnvironment): Promise { + const catalog = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + logger, + jenkinsInfoProvider: DefaultJenkinsInfoProvider.fromConfig({ + catalog, + config, + }), + }); +} diff --git a/packages/backend/src/plugins/search.ts b/packages/backend/src/plugins/search.ts index 4a1e415c74..7e7f6ae400 100644 --- a/packages/backend/src/plugins/search.ts +++ b/packages/backend/src/plugins/search.ts @@ -21,6 +21,7 @@ import { } from '@backstage/plugin-search-backend-node'; import { PluginEnvironment } from '../types'; import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend'; +import { DefaultTechDocsCollator } from '@backstage/plugin-techdocs-backend'; export default async function createPlugin({ logger, @@ -37,6 +38,11 @@ export default async function createPlugin({ collator: new DefaultCatalogCollator({ discovery }), }); + indexBuilder.addCollator({ + defaultRefreshIntervalSeconds: 600, + collator: new DefaultTechDocsCollator({ discovery, logger }), + }); + // The scheduler controls when documents are gathered from collators and sent // to the search engine for indexing. const { scheduler } = await indexBuilder.build(); diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 70b79b961f..d417f74f96 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/catalog-client +## 0.3.17 + +### Patch Changes + +- 71c936eb6: Export `CatalogRequestOptions` type + ## 0.3.16 ### Patch Changes diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index 2abdeb8c75..c356deed4f 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -7,6 +7,8 @@ import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { Location as Location_2 } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "AddLocationRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AddLocationRequest = { type?: string; @@ -15,12 +17,16 @@ export type AddLocationRequest = { presence?: 'optional' | 'required'; }; +// Warning: (ae-missing-release-tag) "AddLocationResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AddLocationResponse = { location: Location_2; entities: Entity[]; }; +// Warning: (ae-missing-release-tag) "CatalogApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CatalogApi { // (undocumented) @@ -65,6 +71,8 @@ export interface CatalogApi { ): Promise; } +// Warning: (ae-missing-release-tag) "CatalogClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CatalogClient implements CatalogApi { constructor(options: { discoveryApi: DiscoveryApi }); @@ -110,6 +118,8 @@ export class CatalogClient implements CatalogApi { ): Promise; } +// Warning: (ae-missing-release-tag) "CatalogEntitiesRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogEntitiesRequest = { filter?: @@ -119,14 +129,29 @@ export type CatalogEntitiesRequest = { fields?: string[] | undefined; }; +// Warning: (ae-missing-release-tag) "CatalogListResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogListResponse = { items: T[]; }; +// Warning: (ae-missing-release-tag) "CatalogRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type CatalogRequestOptions = { + token?: string; +}; + +// Warning: (ae-missing-release-tag) "ENTITY_STATUS_CATALOG_PROCESSING_TYPE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = 'backstage.io/catalog-processing'; +// Warnings were encountered during analysis: +// +// src/CatalogClient.d.ts:7:9 - (ae-forgotten-export) The symbol "DiscoveryApi" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index fbb37d2449..a114871a6e 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "0.3.16", + "version": "0.3.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,7 +35,7 @@ "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.5", "@types/jest": "^26.0.7", "msw": "^0.29.0" }, diff --git a/packages/catalog-client/src/types/index.ts b/packages/catalog-client/src/types/index.ts index 842d0393a4..6ec17fbae1 100644 --- a/packages/catalog-client/src/types/index.ts +++ b/packages/catalog-client/src/types/index.ts @@ -20,5 +20,6 @@ export type { CatalogApi, CatalogEntitiesRequest, CatalogListResponse, + CatalogRequestOptions, } from './api'; export { ENTITY_STATUS_CATALOG_PROCESSING_TYPE } from './status'; diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 74027716de..437a0be02b 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -9,6 +9,9 @@ import { JsonValue } from '@backstage/config'; import { SerializedError } from '@backstage/errors'; import * as yup from 'yup'; +// Warning: (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative +// Warning: (ae-missing-release-tag) "analyzeLocationSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated (undocumented) export const analyzeLocationSchema: yup.ObjectSchema< { @@ -17,6 +20,8 @@ export const analyzeLocationSchema: yup.ObjectSchema< object >; +// Warning: (ae-missing-release-tag) "ApiEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface ApiEntityV1alpha1 extends Entity { // (undocumented) @@ -35,24 +40,43 @@ interface ApiEntityV1alpha1 extends Entity { export { ApiEntityV1alpha1 as ApiEntity }; export { ApiEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "apiEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const apiEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "CommonValidatorFunctions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class CommonValidatorFunctions { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static isJsonSafe(value: unknown): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static isValidDnsLabel(value: unknown): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static isValidDnsSubdomain(value: unknown): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static isValidPrefixAndOrSuffix( value: unknown, separator: string, isValidPrefix: (value: string) => boolean, isValidSuffix: (value: string) => boolean, ): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static isValidString(value: unknown): boolean; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static isValidUrl(value: unknown): boolean; } +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-forgotten-export) The symbol "EntityRefContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "compareEntityToRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function compareEntityToRef( entity: Entity, @@ -60,6 +84,8 @@ export function compareEntityToRef( context?: EntityRefContext, ): boolean; +// Warning: (ae-missing-release-tag) "ComponentEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface ComponentEntityV1alpha1 extends Entity { // (undocumented) @@ -81,9 +107,13 @@ interface ComponentEntityV1alpha1 extends Entity { export { ComponentEntityV1alpha1 as ComponentEntity }; export { ComponentEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "componentEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const componentEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "DefaultNamespaceEntityPolicy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class DefaultNamespaceEntityPolicy implements EntityPolicy { constructor(namespace?: string); @@ -91,6 +121,8 @@ export class DefaultNamespaceEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } +// Warning: (ae-missing-release-tag) "DomainEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface DomainEntityV1alpha1 extends Entity { // (undocumented) @@ -105,12 +137,18 @@ interface DomainEntityV1alpha1 extends Entity { export { DomainEntityV1alpha1 as DomainEntity }; export { DomainEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "domainEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const domainEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "EDIT_URL_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EDIT_URL_ANNOTATION = 'backstage.io/edit-url'; +// Warning: (ae-missing-release-tag) "Entity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Entity = { apiVersion: string; @@ -121,9 +159,13 @@ export type Entity = { status?: UNSTABLE_EntityStatus; }; +// Warning: (ae-missing-release-tag) "ENTITY_DEFAULT_NAMESPACE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ENTITY_DEFAULT_NAMESPACE = 'default'; +// Warning: (ae-missing-release-tag) "ENTITY_META_GENERATED_FIELDS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ENTITY_META_GENERATED_FIELDS: readonly [ 'uid', @@ -131,6 +173,8 @@ export const ENTITY_META_GENERATED_FIELDS: readonly [ 'generation', ]; +// Warning: (ae-missing-release-tag) "EntityEnvelope" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityEnvelope = { apiVersion: string; @@ -141,19 +185,35 @@ export type EntityEnvelope = { }; }; +// Warning: (ae-missing-release-tag) "entityEnvelopeSchemaValidator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/catalog-model" does not have an export "TypeError" +// // @public export function entityEnvelopeSchemaValidator< T extends EntityEnvelope = EntityEnvelope >(schema?: unknown): (data: unknown) => T; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "entityHasChanges" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function entityHasChanges(previous: Entity, next: Entity): boolean; +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (ae-missing-release-tag) "entityKindSchemaValidator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/catalog-model" does not have an export "TypeError" +// // @public export function entityKindSchemaValidator( schema: unknown, ): (data: unknown) => T | false; +// Warning: (ae-missing-release-tag) "EntityLink" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityLink = { url: string; @@ -161,6 +221,8 @@ export type EntityLink = { icon?: string; }; +// Warning: (ae-missing-release-tag) "EntityMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityMeta = JsonObject & { uid?: string; @@ -175,6 +237,8 @@ export type EntityMeta = JsonObject & { links?: EntityLink[]; }; +// Warning: (ae-missing-release-tag) "EntityName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityName = { kind: string; @@ -182,17 +246,23 @@ export type EntityName = { name: string; }; +// Warning: (ae-missing-release-tag) "EntityPolicies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityPolicies: { allOf(policies: EntityPolicy[]): AllEntityPolicies; oneOf(policies: EntityPolicy[]): AnyEntityPolicy; }; +// Warning: (ae-missing-release-tag) "EntityPolicy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityPolicy = { enforce(entity: Entity): Promise; }; +// Warning: (ae-missing-release-tag) "EntityRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityRef = | string @@ -202,12 +272,16 @@ export type EntityRef = name: string; }; +// Warning: (ae-missing-release-tag) "EntityRelation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityRelation = { type: string; target: EntityName; }; +// Warning: (ae-missing-release-tag) "EntityRelationSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityRelationSpec = { source: EntityName; @@ -215,11 +289,16 @@ export type EntityRelationSpec = { target: EntityName; }; +// Warning: (ae-missing-release-tag) "entitySchemaValidator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/catalog-model" does not have an export "TypeError" +// // @public export function entitySchemaValidator( schema?: unknown, ): (data: unknown) => T; +// Warning: (ae-missing-release-tag) "FieldFormatEntityPolicy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class FieldFormatEntityPolicy implements EntityPolicy { constructor(validators?: Validators); @@ -227,18 +306,32 @@ export class FieldFormatEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } +// Warning: (ae-missing-release-tag) "generateEntityEtag" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function generateEntityEtag(): string; +// Warning: (ae-missing-release-tag) "generateEntityUid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function generateEntityUid(): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "generateUpdatedEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function generateUpdatedEntity(previous: Entity, next: Entity): Entity; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getEntityName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getEntityName(entity: Entity): EntityName; +// Warning: (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" +// Warning: (ae-missing-release-tag) "getEntitySourceLocation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getEntitySourceLocation( entity: Entity, @@ -247,6 +340,8 @@ export function getEntitySourceLocation( target: string; }; +// Warning: (ae-missing-release-tag) "GroupEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface GroupEntityV1alpha1 extends Entity { // (undocumented) @@ -269,20 +364,28 @@ interface GroupEntityV1alpha1 extends Entity { export { GroupEntityV1alpha1 as GroupEntity }; export { GroupEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "groupEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const groupEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "JSONSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type JSONSchema = JSONSchema7 & { [key in string]?: JsonValue; }; +// Warning: (ae-missing-release-tag) "KindValidator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type KindValidator = { check(entity: Entity): Promise; }; +// Warning: (ae-missing-release-tag) "KubernetesValidatorFunctions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class KubernetesValidatorFunctions { // (undocumented) @@ -303,15 +406,21 @@ export class KubernetesValidatorFunctions { static isValidObjectName(value: unknown): boolean; } +// Warning: (ae-missing-release-tag) "Location" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type Location_2 = { id: string; } & LocationSpec; export { Location_2 as Location }; +// Warning: (ae-missing-release-tag) "LOCATION_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location'; +// Warning: (ae-missing-release-tag) "LocationEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface LocationEntityV1alpha1 extends Entity { // (undocumented) @@ -328,12 +437,19 @@ interface LocationEntityV1alpha1 extends Entity { export { LocationEntityV1alpha1 as LocationEntity }; export { LocationEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "locationEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const locationEntityV1alpha1Validator: KindValidator; +// Warning: (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative +// Warning: (ae-missing-release-tag) "locationSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated (undocumented) export const locationSchema: yup.ObjectSchema; +// Warning: (ae-missing-release-tag) "LocationSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LocationSpec = { type: string; @@ -341,12 +457,19 @@ export type LocationSpec = { presence?: 'optional' | 'required'; }; +// Warning: (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative +// Warning: (ae-missing-release-tag) "locationSpecSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated (undocumented) export const locationSpecSchema: yup.ObjectSchema; +// Warning: (ae-missing-release-tag) "makeValidator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function makeValidator(overrides?: Partial): Validators; +// Warning: (ae-missing-release-tag) "NoForeignRootFieldsEntityPolicy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class NoForeignRootFieldsEntityPolicy implements EntityPolicy { constructor(knownFields?: string[]); @@ -354,16 +477,28 @@ export class NoForeignRootFieldsEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } +// Warning: (ae-missing-release-tag) "ORIGIN_LOCATION_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ORIGIN_LOCATION_ANNOTATION = 'backstage.io/managed-by-origin-location'; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "parseEntityName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function parseEntityName( ref: EntityRef, context?: EntityRefContext, ): EntityName; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "parseEntityRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "parseEntityRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "parseEntityRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function parseEntityRef( ref: EntityRef, @@ -401,6 +536,11 @@ export function parseEntityRef( name: string; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (ae-missing-release-tag) "parseLocationReference" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function parseLocationReference( ref: string, @@ -409,48 +549,78 @@ export function parseLocationReference( target: string; }; +// Warning: (ae-missing-release-tag) "RELATION_API_CONSUMED_BY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_API_CONSUMED_BY = 'apiConsumedBy'; +// Warning: (ae-missing-release-tag) "RELATION_API_PROVIDED_BY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_API_PROVIDED_BY = 'apiProvidedBy'; +// Warning: (ae-missing-release-tag) "RELATION_CHILD_OF" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_CHILD_OF = 'childOf'; +// Warning: (ae-missing-release-tag) "RELATION_CONSUMES_API" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const RELATION_CONSUMES_API = 'consumesApi'; +// Warning: (ae-missing-release-tag) "RELATION_DEPENDENCY_OF" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_DEPENDENCY_OF = 'dependencyOf'; +// Warning: (ae-missing-release-tag) "RELATION_DEPENDS_ON" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const RELATION_DEPENDS_ON = 'dependsOn'; +// Warning: (ae-missing-release-tag) "RELATION_HAS_MEMBER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_HAS_MEMBER = 'hasMember'; +// Warning: (ae-missing-release-tag) "RELATION_HAS_PART" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_HAS_PART = 'hasPart'; +// Warning: (ae-missing-release-tag) "RELATION_MEMBER_OF" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const RELATION_MEMBER_OF = 'memberOf'; +// Warning: (ae-missing-release-tag) "RELATION_OWNED_BY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const RELATION_OWNED_BY = 'ownedBy'; +// Warning: (ae-missing-release-tag) "RELATION_OWNER_OF" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_OWNER_OF = 'ownerOf'; +// Warning: (ae-missing-release-tag) "RELATION_PARENT_OF" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const RELATION_PARENT_OF = 'parentOf'; +// Warning: (ae-missing-release-tag) "RELATION_PART_OF" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const RELATION_PART_OF = 'partOf'; +// Warning: (ae-missing-release-tag) "RELATION_PROVIDES_API" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RELATION_PROVIDES_API = 'providesApi'; +// Warning: (ae-missing-release-tag) "ResourceEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface ResourceEntityV1alpha1 extends Entity { // (undocumented) @@ -468,15 +638,22 @@ interface ResourceEntityV1alpha1 extends Entity { export { ResourceEntityV1alpha1 as ResourceEntity }; export { ResourceEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "resourceEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const resourceEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "SchemaValidEntityPolicy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class SchemaValidEntityPolicy implements EntityPolicy { // (undocumented) enforce(entity: Entity): Promise; } +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "serializeEntityRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated export function serializeEntityRef( ref: @@ -488,9 +665,14 @@ export function serializeEntityRef( }, ): EntityRef; +// Warning: (ae-missing-release-tag) "SOURCE_LOCATION_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SOURCE_LOCATION_ANNOTATION = 'backstage.io/source-location'; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "stringifyEntityRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function stringifyEntityRef( ref: @@ -502,12 +684,19 @@ export function stringifyEntityRef( }, ): string; +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "stringifyLocationReference" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function stringifyLocationReference(ref: { type: string; target: string; }): string; +// Warning: (ae-missing-release-tag) "SystemEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface SystemEntityV1alpha1 extends Entity { // (undocumented) @@ -523,9 +712,13 @@ interface SystemEntityV1alpha1 extends Entity { export { SystemEntityV1alpha1 as SystemEntity }; export { SystemEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "systemEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const systemEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "TemplateEntityV1beta2" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TemplateEntityV1beta2 extends Entity { // (undocumented) @@ -554,6 +747,8 @@ export interface TemplateEntityV1beta2 extends Entity { }; } +// Warning: (ae-missing-release-tag) "templateEntityV1beta2Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const templateEntityV1beta2Validator: KindValidator; @@ -573,6 +768,8 @@ export type UNSTABLE_EntityStatusItem = { // @alpha export type UNSTABLE_EntityStatusLevel = 'info' | 'warning' | 'error'; +// Warning: (ae-missing-release-tag) "UserEntityV1alpha1" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) interface UserEntityV1alpha1 extends Entity { // (undocumented) @@ -592,9 +789,13 @@ interface UserEntityV1alpha1 extends Entity { export { UserEntityV1alpha1 as UserEntity }; export { UserEntityV1alpha1 }; +// Warning: (ae-missing-release-tag) "userEntityV1alpha1Validator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const userEntityV1alpha1Validator: KindValidator; +// Warning: (ae-missing-release-tag) "Validators" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Validators = { isValidApiVersion(value: unknown): boolean; @@ -608,8 +809,18 @@ export type Validators = { isValidTag(value: unknown): boolean; }; +// Warning: (ae-missing-release-tag) "VIEW_URL_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const VIEW_URL_ANNOTATION = 'backstage.io/view-url'; +// Warnings were encountered during analysis: +// +// src/EntityPolicies.d.ts:13:5 - (ae-forgotten-export) The symbol "AllEntityPolicies" needs to be exported by the entry point index.d.ts +// src/EntityPolicies.d.ts:14:5 - (ae-forgotten-export) The symbol "AnyEntityPolicy" needs to be exported by the entry point index.d.ts +// src/entity/Entity.d.ts:37: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/policies/types.d.ts:10:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/kinds/types.d.ts:9:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/cli-common/api-report.md b/packages/cli-common/api-report.md index 7141d27f3a..d6d1c4fc36 100644 --- a/packages/cli-common/api-report.md +++ b/packages/cli-common/api-report.md @@ -3,26 +3,33 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - +// Warning: (ae-missing-release-tag) "findPaths" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function findPaths(searchDir: string): Paths; +// Warning: (ae-missing-release-tag) "isChildPath" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function isChildPath(base: string, path: string): boolean; +// Warning: (ae-missing-release-tag) "Paths" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Paths = { - ownDir: string; - ownRoot: string; - targetDir: string; - targetRoot: string; - resolveOwn: ResolveFunc; - resolveOwnRoot: ResolveFunc; - resolveTarget: ResolveFunc; - resolveTargetRoot: ResolveFunc; + ownDir: string; + ownRoot: string; + targetDir: string; + targetRoot: string; + resolveOwn: ResolveFunc; + resolveOwnRoot: ResolveFunc; + resolveTarget: ResolveFunc; + resolveTargetRoot: ResolveFunc; }; +// Warnings were encountered during analysis: +// +// src/paths.d.ts:7:5 - (ae-forgotten-export) The symbol "ResolveFunc" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index a8cacedf22..b2877572fe 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/cli +## 0.7.5 + +### Patch Changes + +- 9a96b5da7: chore: bump `eslint` to `7.30.0` + +## 0.7.4 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/config-loader@0.6.5 + ## 0.7.3 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index a7032879bc..1adaeaf725 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.3", + "version": "0.7.5", "private": false, "publishConfig": { "access": "public" @@ -32,7 +32,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.4.4", "@backstage/cli-common": "^0.1.2", "@backstage/config": "^0.1.5", - "@backstage/config-loader": "^0.6.4", + "@backstage/config-loader": "^0.6.5", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -53,8 +53,8 @@ "@types/start-server-webpack-plugin": "^2.2.0", "@types/webpack-env": "^1.15.2", "@types/webpack-node-externals": "^2.5.0", - "@typescript-eslint/eslint-plugin": "^v4.27.0", - "@typescript-eslint/parser": "^v4.27.0", + "@typescript-eslint/eslint-plugin": "^v4.28.3", + "@typescript-eslint/parser": "^v4.28.3", "@yarnpkg/lockfile": "^1.1.0", "babel-plugin-dynamic-import-node": "^2.3.3", "bfj": "^7.0.2", @@ -65,7 +65,7 @@ "dashify": "^2.0.0", "diff": "^5.0.0", "esbuild": "^0.8.56", - "eslint": "^7.1.0", + "eslint": "^7.30.0", "eslint-config-prettier": "^8.3.0", "eslint-formatter-friendly": "^7.0.0", "eslint-plugin-import": "^2.20.2", @@ -118,13 +118,13 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.8.4", + "@backstage/backend-common": "^0.8.7", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", - "@backstage/test-utils": "^0.1.14", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@backstage/theme": "^0.2.8", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index dc1f694dca..0c104d6c33 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/codemods +## 0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/core-app-api@0.1.5 + ## 0.1.5 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 9cf5730126..19c71c843c 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.5", + "version": "0.1.6", "private": false, "publishConfig": { "access": "public", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index e0be1eebc8..d06568bf47 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/config-loader +## 0.6.5 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. + ## 0.6.4 ### Patch Changes diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index ffd15b0c9a..37b9880448 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -7,6 +7,8 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/config'; import { JSONSchema7 } from 'json-schema'; +// Warning: (ae-missing-release-tag) "ConfigSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ConfigSchema = { process( @@ -16,12 +18,19 @@ export type ConfigSchema = { serialize(): JsonObject; }; +// Warning: (ae-forgotten-export) The symbol "CONFIG_VISIBILITIES" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ConfigVisibility" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ConfigVisibility = typeof CONFIG_VISIBILITIES[number]; +// Warning: (ae-missing-release-tag) "loadConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function loadConfig(options: LoadConfigOptions): Promise; +// Warning: (ae-missing-release-tag) "LoadConfigOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LoadConfigOptions = { configRoot: string; @@ -30,16 +39,28 @@ export type LoadConfigOptions = { experimentalEnvFunc?: EnvFunc; }; +// Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "loadConfigSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function loadConfigSchema(options: Options): Promise; +// Warning: (ae-missing-release-tag) "mergeConfigSchemas" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function mergeConfigSchemas(schemas: JSONSchema7[]): JSONSchema7; +// Warning: (ae-missing-release-tag) "readEnvConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readEnvConfig(env: { [name: string]: string | undefined; }): AppConfig[]; +// Warnings were encountered during analysis: +// +// src/lib/schema/types.d.ts:77:5 - (ae-forgotten-export) The symbol "ConfigProcessingOptions" needs to be exported by the entry point index.d.ts +// src/loader.d.ts:13:5 - (ae-forgotten-export) The symbol "EnvFunc" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index cff324484a..7eb0a78f3f 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.4", + "version": "0.6.5", "private": false, "publishConfig": { "access": "public", diff --git a/packages/config/api-report.md b/packages/config/api-report.md index 0c243aab48..50d2af0f1f 100644 --- a/packages/config/api-report.md +++ b/packages/config/api-report.md @@ -3,12 +3,16 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +// Warning: (ae-missing-release-tag) "AppConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppConfig = { context: string; data: JsonObject; }; +// Warning: (ae-missing-release-tag) "Config" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Config = { has(key: string): boolean; @@ -29,6 +33,8 @@ export type Config = { getOptionalStringArray(key: string): string[] | undefined; }; +// Warning: (ae-missing-release-tag) "ConfigReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ConfigReader implements Config { constructor( @@ -73,17 +79,25 @@ export class ConfigReader implements Config { keys(): string[]; } +// Warning: (ae-missing-release-tag) "JsonArray" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface JsonArray extends Array {} +// Warning: (ae-missing-release-tag) "JsonObject" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type JsonObject = { [key in string]?: JsonValue; }; +// Warning: (ae-missing-release-tag) "JsonPrimitive" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type JsonPrimitive = number | string | boolean | null; +// Warning: (ae-missing-release-tag) "JsonValue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type JsonValue = JsonObject | JsonArray | JsonPrimitive; diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 4f09f231c3..8e188a6577 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/core-app-api +## 0.1.5 + +### Patch Changes + +- ea249c6e6: Fix a bug in `FlatRoutes` that prevented outlets from working with the root route, as well as matching root routes too broadly. +- Updated dependencies + - @backstage/core-components@0.1.6 + ## 0.1.4 ### Patch Changes diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index af86f042a0..a7a3845dd7 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -54,6 +54,8 @@ import { StorageApi } from '@backstage/core-plugin-api'; import { StorageValueChange } from '@backstage/core-plugin-api'; import { SubRouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "AlertApiForwarder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class AlertApiForwarder implements AlertApi { // (undocumented) @@ -62,6 +64,8 @@ export class AlertApiForwarder implements AlertApi { post(alert: AlertMessage): void; } +// Warning: (ae-missing-release-tag) "ApiFactoryHolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiFactoryHolder = { get( @@ -77,6 +81,8 @@ export type ApiFactoryHolder = { | undefined; }; +// Warning: (ae-missing-release-tag) "ApiFactoryRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class ApiFactoryRegistry implements ApiFactoryHolder { // (undocumented) @@ -93,6 +99,7 @@ export class ApiFactoryRegistry implements ApiFactoryHolder { | undefined; // (undocumented) getAllApis(): Set; + // Warning: (ae-forgotten-export) The symbol "ApiFactoryScope" needs to be exported by the entry point index.d.ts register< Api, Impl extends Api, @@ -102,6 +109,8 @@ export class ApiFactoryRegistry implements ApiFactoryHolder { >(scope: ApiFactoryScope, factory: ApiFactory): boolean; } +// Warning: (ae-missing-release-tag) "ApiProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ApiProvider: { ({ apis, children }: PropsWithChildren): JSX.Element; @@ -115,19 +124,31 @@ export const ApiProvider: { }; }; +// Warning: (ae-missing-release-tag) "ApiRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ApiRegistry implements ApiHolder { constructor(apis: Map); + // Warning: (ae-forgotten-export) The symbol "ApiRegistryBuilder" needs to be exported by the entry point index.d.ts + // // (undocumented) static builder(): ApiRegistryBuilder; + // Warning: (ae-forgotten-export) The symbol "ApiImpl" needs to be exported by the entry point index.d.ts + // // (undocumented) static from(apis: ApiImpl[]): ApiRegistry; // (undocumented) get(api: ApiRef): T | undefined; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen static with(api: ApiRef, impl: T): ApiRegistry; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen with(api: ApiRef, impl: T): ApiRegistry; } +// Warning: (ae-missing-release-tag) "ApiResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ApiResolver implements ApiHolder { constructor(factories: ApiFactoryHolder); @@ -139,6 +160,8 @@ export class ApiResolver implements ApiHolder { ): void; } +// Warning: (ae-missing-release-tag) "AppComponents" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppComponents = { NotFoundErrorPage: ComponentType<{}>; @@ -149,9 +172,13 @@ export type AppComponents = { SignInPage?: ComponentType; }; +// Warning: (ae-missing-release-tag) "AppConfigLoader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AppConfigLoader = () => Promise; +// Warning: (ae-missing-release-tag) "AppContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppContext = { getPlugins(): BackstagePlugin[]; @@ -159,6 +186,8 @@ export type AppContext = { getComponents(): AppComponents; }; +// Warning: (ae-missing-release-tag) "AppOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppOptions = { apis?: Iterable; @@ -173,6 +202,11 @@ export type AppOptions = { bindRoutes?(context: { bind: AppRouteBinder }): void; }; +// Warning: (ae-forgotten-export) The symbol "PartialKeys" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "TargetRouteMap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "KeysWithType" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AppRouteBinder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppRouteBinder = < ExternalRoutes extends { @@ -186,6 +220,8 @@ export type AppRouteBinder = < >, ) => void; +// Warning: (ae-missing-release-tag) "AppThemeSelector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class AppThemeSelector implements AppThemeApi { constructor(themes: AppTheme[]); @@ -201,8 +237,12 @@ export class AppThemeSelector implements AppThemeApi { setActiveThemeId(themeId?: string): void; } +// 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, @@ -213,6 +253,8 @@ export class Auth0Auth { }: OAuthApiCreateOptions): typeof auth0AuthApiRef.T; } +// Warning: (ae-missing-release-tag) "BackstageApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstageApp = { getPlugins(): BackstagePlugin[]; @@ -221,6 +263,8 @@ export type BackstageApp = { getRouter(): ComponentType<{}>; }; +// Warning: (ae-missing-release-tag) "BackstagePluginWithAnyOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstagePluginWithAnyOutput = Omit< BackstagePlugin, @@ -229,6 +273,8 @@ export type BackstagePluginWithAnyOutput = Omit< output(): (PluginOutput | UnknownPluginOutput)[]; }; +// Warning: (ae-missing-release-tag) "BootErrorPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BootErrorPageProps = { step: 'load-config' | 'load-chunk'; @@ -237,12 +283,19 @@ export type BootErrorPageProps = { export { ConfigReader }; +// Warning: (ae-forgotten-export) The symbol "PrivateAppImpl" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createApp(options?: AppOptions): PrivateAppImpl; +// Warning: (ae-missing-release-tag) "defaultConfigLoader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const defaultConfigLoader: AppConfigLoader; +// Warning: (ae-missing-release-tag) "ErrorAlerter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class ErrorAlerter implements ErrorApi { constructor(alertApi: AlertApi, errorApi: ErrorApi); @@ -259,6 +312,8 @@ export class ErrorAlerter implements ErrorApi { post(error: Error, context?: ErrorContext): void; } +// Warning: (ae-missing-release-tag) "ErrorApiForwarder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class ErrorApiForwarder implements ErrorApi { // (undocumented) @@ -270,6 +325,8 @@ export class ErrorApiForwarder implements ErrorApi { post(error: Error, context?: ErrorContext): void; } +// Warning: (ae-missing-release-tag) "ErrorBoundaryFallbackProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ErrorBoundaryFallbackProps = { plugin?: BackstagePlugin; @@ -277,9 +334,13 @@ export type ErrorBoundaryFallbackProps = { resetError: () => void; }; +// Warning: (ae-missing-release-tag) "FeatureFlagged" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const FeatureFlagged: (props: FeatureFlaggedProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "FeatureFlaggedProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type FeatureFlaggedProps = { children: ReactNode; @@ -292,11 +353,17 @@ export type FeatureFlaggedProps = { } ); +// Warning: (ae-forgotten-export) The symbol "FlatRoutesProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "FlatRoutes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const FlatRoutes: (props: FlatRoutesProps) => JSX.Element | null; +// Warning: (ae-missing-release-tag) "GithubAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GithubAuth implements OAuthApi, SessionApi { + // Warning: (ae-forgotten-export) The symbol "SessionManager" needs to be exported by the entry point index.d.ts constructor(sessionManager: SessionManager); // (undocumented) static create({ @@ -324,6 +391,8 @@ export class GithubAuth implements OAuthApi, SessionApi { signOut(): Promise; } +// Warning: (ae-missing-release-tag) "GithubSession" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GithubSession = { providerInfo: { @@ -335,6 +404,8 @@ export type GithubSession = { backstageIdentity: BackstageIdentity; }; +// Warning: (ae-missing-release-tag) "GitlabAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GitlabAuth { // (undocumented) @@ -347,6 +418,8 @@ export class GitlabAuth { }: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T; } +// Warning: (ae-missing-release-tag) "GoogleAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GoogleAuth { // (undocumented) @@ -359,6 +432,8 @@ export class GoogleAuth { }: OAuthApiCreateOptions): typeof googleAuthApiRef.T; } +// Warning: (ae-missing-release-tag) "LocalStorageFeatureFlags" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class LocalStorageFeatureFlags implements FeatureFlagsApi { // (undocumented) @@ -371,6 +446,8 @@ export class LocalStorageFeatureFlags implements FeatureFlagsApi { save(options: FeatureFlagsSaveOptions): void; } +// Warning: (ae-missing-release-tag) "MicrosoftAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class MicrosoftAuth { // (undocumented) @@ -383,6 +460,8 @@ export class MicrosoftAuth { }: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T; } +// Warning: (ae-missing-release-tag) "OAuth2" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class OAuth2 implements @@ -391,7 +470,10 @@ export class OAuth2 ProfileInfoApi, BackstageIdentityApi, SessionApi { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options); + // Warning: (ae-forgotten-export) The symbol "CreateOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static create({ discoveryApi, @@ -422,6 +504,8 @@ export class OAuth2 signOut(): Promise; } +// Warning: (ae-missing-release-tag) "OAuth2Session" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuth2Session = { providerInfo: { @@ -434,6 +518,8 @@ export type OAuth2Session = { backstageIdentity: BackstageIdentity; }; +// Warning: (ae-missing-release-tag) "OAuthRequestManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class OAuthRequestManager implements OAuthRequestApi { // (undocumented) @@ -442,6 +528,8 @@ export class OAuthRequestManager implements OAuthRequestApi { createAuthRequester(options: AuthRequesterOptions): AuthRequester; } +// Warning: (ae-missing-release-tag) "OktaAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class OktaAuth { // (undocumented) @@ -454,8 +542,12 @@ export class OktaAuth { }: OAuthApiCreateOptions): typeof oktaAuthApiRef.T; } +// Warning: (ae-missing-release-tag) "OneLoginAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class OneLoginAuth { + // Warning: (ae-forgotten-export) The symbol "CreateOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static create({ discoveryApi, @@ -465,10 +557,15 @@ export class OneLoginAuth { }: CreateOptions_2): typeof oneloginAuthApiRef.T; } +// Warning: (ae-missing-release-tag) "SamlAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionApi { + // Warning: (ae-forgotten-export) The symbol "SamlSession" needs to be exported by the entry point index.d.ts constructor(sessionManager: SessionManager); + // Warning: (ae-forgotten-export) The symbol "AuthApiCreateOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static create({ discoveryApi, @@ -489,11 +586,15 @@ export class SamlAuth signOut(): Promise; } +// Warning: (ae-missing-release-tag) "SignInPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SignInPageProps = { onResult(result: SignInResult): void; }; +// Warning: (ae-missing-release-tag) "SignInResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SignInResult = { userId: string; @@ -502,11 +603,15 @@ export type SignInResult = { signOut?: () => Promise; }; +// Warning: (ae-missing-release-tag) "UnhandledErrorForwarder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class UnhandledErrorForwarder { static forward(errorApi: ErrorApi, errorContext: ErrorContext): void; } +// Warning: (ae-missing-release-tag) "UrlPatternDiscovery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class UrlPatternDiscovery implements DiscoveryApi { static compile(pattern: string): UrlPatternDiscovery; @@ -514,9 +619,13 @@ export class UrlPatternDiscovery implements DiscoveryApi { getBaseUrl(pluginId: string): Promise; } +// Warning: (ae-missing-release-tag) "WebStorage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class WebStorage implements StorageApi { constructor(namespace: string, errorApi: ErrorApi); + // Warning: (ae-forgotten-export) The symbol "CreateStorageApiOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static create(options: CreateStorageApiOptions): WebStorage; // (undocumented) @@ -531,5 +640,11 @@ export class WebStorage implements StorageApi { set(key: string, data: T): Promise; } +// Warnings were encountered during analysis: +// +// src/apis/system/ApiProvider.d.ts:9:5 - (ae-forgotten-export) The symbol "ApiProviderProps" needs to be exported by the entry point index.d.ts +// src/app/types.d.ts:88:5 - (ae-forgotten-export) The symbol "UnknownPluginOutput" needs to be exported by the entry point index.d.ts +// src/app/types.d.ts:99:5 - (ae-forgotten-export) The symbol "AppIcons" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 0f6773497b..41e7c52393 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.4", + "version": "0.1.5", "private": false, "publishConfig": { "access": "public", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.6", "@backstage/config": "^0.1.3", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", @@ -44,8 +44,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/test-utils": "^0.1.15", "@backstage/test-utils-core": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core-app-api/src/routing/FlatRoutes.test.tsx b/packages/core-app-api/src/routing/FlatRoutes.test.tsx index a73534a272..d5201a6f57 100644 --- a/packages/core-app-api/src/routing/FlatRoutes.test.tsx +++ b/packages/core-app-api/src/routing/FlatRoutes.test.tsx @@ -100,7 +100,6 @@ describe('FlatRoutes', () => { return <>Outlet: {useOutlet()}; }; - // The '/*' suffixes here are intentional and will be ignored by FlatRoutes const routes = ( <> }> @@ -112,11 +111,15 @@ describe('FlatRoutes', () => { }> b + }> + c + ); const renderRoute = makeRouteRenderer({routes}); expect(renderRoute('/a').getByText('Outlet: a')).toBeInTheDocument(); expect(renderRoute('/a/b').getByText('Outlet: a-b')).toBeInTheDocument(); expect(renderRoute('/b').getByText('Outlet: b')).toBeInTheDocument(); + expect(renderRoute('/').getByText('Outlet: c')).toBeInTheDocument(); }); }); diff --git a/packages/core-app-api/src/routing/FlatRoutes.tsx b/packages/core-app-api/src/routing/FlatRoutes.tsx index 6ba82203fd..315ec91cdb 100644 --- a/packages/core-app-api/src/routing/FlatRoutes.tsx +++ b/packages/core-app-api/src/routing/FlatRoutes.tsx @@ -49,8 +49,10 @@ export const FlatRoutes = (props: FlatRoutesProps): JSX.Element | null => { element: child, children: child.props.children ? [ + // These are the children of each route, which we all add in under a catch-all + // subroute in order to make them available to `useOutlet` { - path: '/*', + path: path === '/' ? '/' : '/*', // The root path must require an exact match element: child.props.children, }, ] diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 1536cf3fb2..8372b25563 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/core-components +## 0.1.6 + +### Patch Changes + +- 9a751bb28: Increase the vertical padding of the sidebar search input field to match the height of the parent anchor tag. This prevents users from accidentally navigating to the search page when they actually wanted to use the search input directly. +- 45b5fc3a8: Updated the layout of catalog and API index pages to handle smaller screen sizes. This adds responsive wrappers to the entity tables, and switches filters to a drawer when width-constrained. If you have created a custom catalog or API index page, you will need to update the page structure to match the updated [catalog customization](https://backstage.io/docs/features/software-catalog/catalog-customization) documentation. +- 03bf17e9b: Improve the responsiveness of the EntityPage UI. With this the Header component should scale with the screen size & wrapping should not cause overflowing/blocking of links. Additionally enforce the Pages using the Grid Layout to use it across all screen sizes & to wrap as intended. + + To benefit from the improved responsive layout, the `EntityPage` in existing Backstage applications should be updated to set the `xs` column size on each grid item in the page, as this does not default. For example: + + ```diff + - + + + ``` + ## 0.1.5 ### Patch Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 3d8e60d4fb..ccafa4cfa7 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -47,9 +47,13 @@ import { Theme } from '@material-ui/core'; import { TooltipProps } from '@material-ui/core'; import { WithStyles } from '@material-ui/core'; +// Warning: (ae-missing-release-tag) "AlertDisplay" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const AlertDisplay: () => JSX.Element | null; +// Warning: (ae-missing-release-tag) "Alignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) enum Alignment { // (undocumented) @@ -62,6 +66,9 @@ enum Alignment { UP_RIGHT = 'UR', } +// Warning: (ae-forgotten-export) The symbol "AvatarProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Avatar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Avatar: ({ displayName, @@ -69,12 +76,20 @@ export const Avatar: ({ customStyles, }: AvatarProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Breadcrumbs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Breadcrumbs: ({ children, ...props }: Props_25) => JSX.Element; +// Warning: (ae-missing-release-tag) "BrokenImageIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const BrokenImageIcon: IconComponent; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Button" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const Button: React_2.ForwardRefExoticComponent< Pick< @@ -384,18 +399,28 @@ export const Button: React_2.ForwardRefExoticComponent< React_2.RefAttributes >; +// Warning: (ae-forgotten-export) The symbol "CardTabProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "CardTab" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CardTab: ({ children, ...props }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "CatalogIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CatalogIcon: IconComponent; +// Warning: (ae-missing-release-tag) "ChatIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ChatIcon: IconComponent; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "CodeSnippet" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CodeSnippet: ({ text, @@ -406,6 +431,9 @@ export const CodeSnippet: ({ customStyle, }: Props_2) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Content" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Content: ({ className, @@ -415,6 +443,9 @@ export const Content: ({ ...props }: PropsWithChildren) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "ContentHeaderProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ContentHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ContentHeader: ({ description, @@ -424,6 +455,8 @@ export const ContentHeader: ({ textAlign, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "CopyTextButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CopyTextButton: { (props: Props_3): JSX.Element; @@ -434,9 +467,14 @@ export const CopyTextButton: { }; }; +// Warning: (ae-missing-release-tag) "DashboardIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DashboardIcon: IconComponent; +// Warning: (ae-forgotten-export) The symbol "CustomType" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "DependencyEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type DependencyEdge = T & { from: string; @@ -444,6 +482,9 @@ type DependencyEdge = T & { label?: string; }; +// Warning: (ae-forgotten-export) The symbol "DependencyGraphProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "DependencyGraph" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function DependencyGraph({ edges, @@ -486,11 +527,15 @@ declare namespace DependencyGraphTypes { } export { DependencyGraphTypes }; +// Warning: (ae-missing-release-tag) "DependencyNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type DependencyNode = T & { id: string; }; +// Warning: (ae-missing-release-tag) "Direction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) enum Direction { // (undocumented) @@ -503,6 +548,9 @@ enum Direction { TOP_BOTTOM = 'TB', } +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "DismissableBanner" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DismissableBanner: ({ variant, @@ -511,9 +559,13 @@ export const DismissableBanner: ({ fixed, }: Props_4) => JSX.Element; +// Warning: (ae-missing-release-tag) "DocsIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DocsIcon: IconComponent; +// Warning: (ae-missing-release-tag) "EdgeProperties" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type EdgeProperties = { label?: string; @@ -526,9 +578,14 @@ type EdgeProperties = { [customKey: string]: any; }; +// Warning: (ae-missing-release-tag) "EmailIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EmailIcon: IconComponent; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmptyState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EmptyState: ({ title, @@ -537,15 +594,23 @@ export const EmptyState: ({ action, }: Props_5) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "State" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ErrorBoundary" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ErrorBoundary: ComponentClass; +// Warning: (ae-missing-release-tag) "ErrorBoundaryProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ErrorBoundaryProps = { slackChannel?: string | SlackChannel; onError?: (error: Error, errorInfo: string) => null; }; +// Warning: (ae-forgotten-export) The symbol "IErrorPageProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ErrorPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ErrorPage: ({ status, @@ -553,6 +618,8 @@ export const ErrorPage: ({ additionalInfo, }: IErrorPageProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "ErrorPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ErrorPanel: ({ title, @@ -561,6 +628,8 @@ export const ErrorPanel: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "ErrorPanelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ErrorPanelProps = { error: Error; @@ -568,6 +637,9 @@ export type ErrorPanelProps = { title?: string; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "FeatureCalloutCircular" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const FeatureCalloutCircular: ({ featureId, @@ -576,26 +648,43 @@ export const FeatureCalloutCircular: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Gauge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Gauge: (props: Props_14) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "GaugeCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GaugeCard: (props: Props_13) => JSX.Element; +// Warning: (ae-missing-release-tag) "GitHubIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GitHubIcon: IconComponent; +// Warning: (ae-missing-release-tag) "GraphEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type GraphEdge = DependencyEdge & dagre_2.GraphEdge & EdgeProperties; +// Warning: (ae-missing-release-tag) "GraphNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type GraphNode = dagre_2.Node>; +// Warning: (ae-missing-release-tag) "GroupIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GroupIcon: IconComponent; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Header" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Header: ({ children, @@ -608,9 +697,15 @@ export const Header: ({ typeLink, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "HeaderIconLinkRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HeaderIconLinkRow: ({ links }: Props_8) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "HeaderLabelProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "HeaderLabel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HeaderLabel: ({ label, @@ -618,6 +713,9 @@ export const HeaderLabel: ({ url, }: HeaderLabelProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "HeaderTabsProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "HeaderTabs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HeaderTabs: ({ tabs, @@ -625,17 +723,26 @@ export const HeaderTabs: ({ selectedIndex, }: HeaderTabsProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "HelpIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HelpIcon: IconComponent; +// Warning: (ae-missing-release-tag) "HomepageTimer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HomepageTimer: () => JSX.Element | null; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "HorizontalScrollGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HorizontalScrollGrid: ( props: PropsWithChildren, ) => JSX.Element; +// Warning: (ae-missing-release-tag) "IconLinkVerticalProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type IconLinkVerticalProps = { color?: 'primary' | 'secondary'; @@ -647,6 +754,9 @@ export type IconLinkVerticalProps = { title?: string; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "InfoCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const InfoCard: ({ title, @@ -669,12 +779,22 @@ export const InfoCard: ({ titleTypographyProps, }: Props_20) => JSX.Element; +// Warning: (ae-missing-release-tag) "InfoCardVariants" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type InfoCardVariants = 'flex' | 'fullHeight' | 'gridItem'; +// Warning: (ae-forgotten-export) The symbol "IntroCardProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "IntroCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const IntroCard: (props: IntroCardProps) => JSX.Element; +// Warning: (tsdoc-malformed-html-name) Invalid HTML element: Expecting an HTML name +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (ae-forgotten-export) The symbol "ItemCardProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ItemCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated export const ItemCard: ({ description, @@ -687,17 +807,39 @@ export const ItemCard: ({ href, }: ItemCardProps) => JSX.Element; +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-html-tag-missing-string) The HTML element has an invalid attribute: Expecting an HTML string starting with a single-quote or double-quote character +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (ae-missing-release-tag) "ItemCardGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ItemCardGrid: (props: ItemCardGridProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "styles" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ItemCardGridProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ItemCardGridProps = Partial> & { children?: React_2.ReactNode; }; +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-html-tag-missing-string) The HTML element has an invalid attribute: Expecting an HTML string starting with a single-quote or double-quote character +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (ae-missing-release-tag) "ItemCardHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ItemCardHeader: (props: ItemCardHeaderProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "styles" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ItemCardHeaderProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ItemCardHeaderProps = Partial> & { title?: React_2.ReactNode; @@ -705,6 +847,8 @@ export type ItemCardHeaderProps = Partial> & { children?: React_2.ReactNode; }; +// Warning: (ae-missing-release-tag) "LabelPosition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) enum LabelPosition { // (undocumented) @@ -715,12 +859,20 @@ enum LabelPosition { RIGHT = 'r', } +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Lifecycle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Lifecycle: (props: Props_10) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "LinearGauge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LinearGauge: ({ value }: Props_15) => JSX.Element | null; +// Warning: (ae-missing-release-tag) "Link" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const Link: React_2.ForwardRefExoticComponent< Pick< @@ -1008,37 +1160,67 @@ export const Link: React_2.ForwardRefExoticComponent< React_2.RefAttributes >; +// Warning: (ae-missing-release-tag) "LinkProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LinkProps = LinkProps_2 & LinkProps_3 & { component?: ElementType; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "MarkdownContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const MarkdownContent: ({ content, dialect }: Props_11) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "MissingAnnotationEmptyState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const MissingAnnotationEmptyState: ({ annotation, }: Props_6) => JSX.Element; +// Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const OAuthRequestDialog: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "OverflowTooltip" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const OverflowTooltip: (props: Props_12) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Page" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Page: ({ themeId, children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "PageWithHeaderProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PageWithHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const PageWithHeader: ({ + themeId, + children, + ...props +}: PropsWithChildren) => JSX.Element; + +// Warning: (ae-missing-release-tag) "Progress" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Progress: ( props: PropsWithChildren, ) => JSX.Element; +// Warning: (ae-missing-release-tag) "Ranker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) enum Ranker { // (undocumented) @@ -1049,22 +1231,32 @@ enum Ranker { TIGHT_TREE = 'tight-tree', } +// Warning: (ae-missing-release-tag) "RenderLabelFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type RenderLabelFunction = (props: RenderLabelProps) => React.ReactNode; +// Warning: (ae-missing-release-tag) "RenderLabelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type RenderLabelProps = { edge: DependencyEdge; }; +// Warning: (ae-missing-release-tag) "RenderNodeFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type RenderNodeFunction = (props: RenderNodeProps) => React.ReactNode; +// Warning: (ae-missing-release-tag) "RenderNodeProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) type RenderNodeProps = { node: DependencyNode; }; +// Warning: (ae-missing-release-tag) "ResponseErrorPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ResponseErrorPanel: ({ title, @@ -1072,9 +1264,14 @@ export const ResponseErrorPanel: ({ defaultExpanded, }: ErrorPanelProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "RoutedTabs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RoutedTabs: ({ routes }: { routes: SubRoute_2[] }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "SelectProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SelectComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Select: ({ multiple, @@ -1086,6 +1283,9 @@ export const Select: ({ triggerReset, }: SelectProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Sidebar: ({ openDelayMs, @@ -1093,10 +1293,14 @@ export const Sidebar: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; +// Warning: (ae-missing-release-tag) "sidebarConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const sidebarConfig: { drawerWidthClosed: number; @@ -1113,14 +1317,20 @@ export const sidebarConfig: { userBadgeDiameter: number; }; +// Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarContext: Context; +// Warning: (ae-missing-release-tag) "SidebarContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SidebarContextType = { isOpen: boolean; }; +// Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarDivider: React_2.ComponentType< Pick< @@ -1388,26 +1598,39 @@ export const SidebarDivider: React_2.ComponentType< } >; +// Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarIntro: () => JSX.Element | null; +// Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SidebarItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarItem: React_2.ForwardRefExoticComponent< SidebarItemProps & React_2.RefAttributes >; +// Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarPage: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "SidebarPinStateContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarPinStateContext: React_2.Context; +// Warning: (ae-missing-release-tag) "SidebarPinStateContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SidebarPinStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; }; +// Warning: (ae-missing-release-tag) "SidebarScrollWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarScrollWrapper: React_2.ComponentType< Pick< @@ -1675,11 +1898,16 @@ export const SidebarScrollWrapper: React_2.ComponentType< } >; +// Warning: (ae-forgotten-export) The symbol "SidebarSearchFieldProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SidebarSearchField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarSearchField: ( props: SidebarSearchFieldProps, ) => JSX.Element; +// Warning: (ae-missing-release-tag) "SidebarSpace" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarSpace: React_2.ComponentType< Pick< @@ -1947,6 +2175,8 @@ export const SidebarSpace: React_2.ComponentType< } >; +// Warning: (ae-missing-release-tag) "SidebarSpacer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarSpacer: React_2.ComponentType< Pick< @@ -2214,9 +2444,14 @@ export const SidebarSpacer: React_2.ComponentType< } >; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SignInPage: (props: Props_23) => JSX.Element; +// Warning: (ae-missing-release-tag) "SignInProviderConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SignInProviderConfig = { id: string; @@ -2225,6 +2460,9 @@ export type SignInProviderConfig = { apiRef: ApiRef; }; +// Warning: (ae-forgotten-export) The symbol "StepperProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SimpleStepper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SimpleStepper: ({ children, @@ -2233,6 +2471,9 @@ export const SimpleStepper: ({ activeStep, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "StepProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SimpleStepperStep" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SimpleStepperStep: ({ title, @@ -2242,24 +2483,39 @@ export const SimpleStepperStep: ({ ...muiProps }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "StatusAborted" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StatusAborted: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "StatusError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StatusError: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "StatusOK" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StatusOK: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "StatusPending" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StatusPending: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "StatusRunning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StatusRunning: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "StatusWarning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StatusWarning: (props: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "StructuredMetadataTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StructuredMetadataTable: ({ metadata, @@ -2267,24 +2523,34 @@ export const StructuredMetadataTable: ({ options, }: Props_16) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "SubvalueCellProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SubvalueCell" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SubvalueCell: ({ value, subvalue, }: SubvalueCellProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "SupportButtonProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SupportButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SupportButton: ({ title, children, }: SupportButtonProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "SupportConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SupportConfig = { url: string; items: SupportItem[]; }; +// Warning: (ae-missing-release-tag) "SupportItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SupportItem = { title: string; @@ -2292,12 +2558,16 @@ export type SupportItem = { links: SupportItemLink[]; }; +// Warning: (ae-missing-release-tag) "SupportItemLink" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SupportItemLink = { url: string; title: string; }; +// Warning: (ae-missing-release-tag) "Tab" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Tab = { id: string; @@ -2310,6 +2580,9 @@ export type Tab = { >; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TabbedCard: ({ slackChannel, @@ -2321,12 +2594,16 @@ export const TabbedCard: ({ onChange, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "TabbedLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const TabbedLayout: { ({ children }: PropsWithChildren<{}>): JSX.Element; Route: (props: SubRoute) => null; }; +// Warning: (ae-missing-release-tag) "Table" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function Table({ columns, @@ -2340,6 +2617,8 @@ export function Table({ ...props }: TableProps): JSX.Element; +// Warning: (ae-missing-release-tag) "TableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TableColumn extends Column { // (undocumented) @@ -2348,12 +2627,16 @@ export interface TableColumn extends Column { width?: string; } +// Warning: (ae-missing-release-tag) "TableFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TableFilter = { column: string; type: 'select' | 'multiple-select' | 'checkbox-tree'; }; +// Warning: (ae-missing-release-tag) "TableProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TableProps extends MaterialTableProps { @@ -2371,6 +2654,8 @@ export interface TableProps subtitle?: string; } +// Warning: (ae-missing-release-tag) "TableState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TableState = { search?: string; @@ -2378,9 +2663,14 @@ export type TableState = { filters?: SelectedFilters; }; +// Warning: (ae-forgotten-export) The symbol "TabsProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Tabs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Tabs: ({ tabs }: TabsProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "TrendLine" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TrendLine: ( props: SparklinesProps & @@ -2389,23 +2679,55 @@ export const TrendLine: ( }, ) => JSX.Element | null; +// Warning: (ae-forgotten-export) The symbol "SetQueryParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "useQueryParamState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useQueryParamState( stateName: string, debounceTime?: number, ): [T | undefined, SetQueryParams]; +// Warning: (ae-missing-release-tag) "UserIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserIcon: IconComponent; +// Warning: (ae-missing-release-tag) "useSupportConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useSupportConfig(): SupportConfig; +// Warning: (ae-missing-release-tag) "WarningIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const WarningIcon: IconComponent; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "WarningPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const WarningPanel: (props: Props_17) => JSX.Element; +// Warnings were encountered during analysis: +// +// src/components/CopyTextButton/CopyTextButton.d.ts:24:5 - (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// src/components/TabbedLayout/RoutedTabs.d.ts:9:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts +// src/components/TabbedLayout/TabbedLayout.d.ts:29:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts +// src/components/Table/Table.d.ts:15:5 - (ae-forgotten-export) The symbol "SelectedFilters" needs to be exported by the entry point index.d.ts +// src/layout/ErrorBoundary/ErrorBoundary.d.ts:7:5 - (ae-forgotten-export) The symbol "SlackChannel" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/core-components/package.json b/packages/core-components/package.json index b9d514d074..944002c74f 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.1.5", + "version": "0.1.6", "private": false, "publishConfig": { "access": "public", @@ -70,15 +70,15 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/core-app-api": "^0.1.4", - "@backstage/cli": "^0.7.3", - "@backstage/test-utils": "^0.1.13", + "@backstage/core-app-api": "^0.1.5", + "@backstage/cli": "^0.7.5", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", "@types/classnames": "^2.2.9", "@types/d3-selection": "^2.0.0", - "@types/d3-shape": "^2.0.0", + "@types/d3-shape": "^3.0.1", "@types/d3-zoom": "^2.0.0", "@types/google-protobuf": "^3.7.2", "@types/jest": "^26.0.7", diff --git a/packages/core-components/src/components/EmptyState/EmptyState.tsx b/packages/core-components/src/components/EmptyState/EmptyState.tsx index 1d8d5a798f..5d55c5da93 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.tsx @@ -49,15 +49,17 @@ export const EmptyState = ({ title, description, missing, action }: Props) => { className={classes.root} spacing={2} > - - - {title} - - - {description} - - - {action} + + + + {title} + + + {description} + + + {action} + diff --git a/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx b/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx index a76f0863f7..660e7fc947 100644 --- a/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx @@ -29,7 +29,7 @@ const useStyles = makeStyles({ generalImg: { width: '95%', zIndex: 2, - position: 'absolute', + position: 'relative', left: '50%', top: '50%', transform: 'translate(-50%, 15%)', diff --git a/packages/core-components/src/components/SupportButton/SupportButton.tsx b/packages/core-components/src/components/SupportButton/SupportButton.tsx index 2ffef89878..3e719acb95 100644 --- a/packages/core-components/src/components/SupportButton/SupportButton.tsx +++ b/packages/core-components/src/components/SupportButton/SupportButton.tsx @@ -28,7 +28,7 @@ import { makeStyles, Popover, } from '@material-ui/core'; -import React, { Fragment, MouseEventHandler, useState } from 'react'; +import React, { MouseEventHandler, useState } from 'react'; import { SupportItem, SupportItemLink, useSupportConfig } from '../../hooks'; import { Link } from '../Link'; @@ -94,17 +94,17 @@ export const SupportButton = ({ title, children }: SupportButtonProps) => { }; return ( - - + <> + + + { - + ); }; diff --git a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx index 1e2ad0b5d4..62cb80cffa 100644 --- a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx +++ b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx @@ -18,7 +18,7 @@ * TODO favoriteable capability */ -import React, { ComponentType, Fragment, PropsWithChildren } from 'react'; +import React, { ComponentType, PropsWithChildren } from 'react'; import { Typography, makeStyles } from '@material-ui/core'; import { Helmet } from 'react-helmet'; @@ -57,15 +57,15 @@ const useStyles = (props: ContentHeaderProps) => }, })); -type DefaultTitleProps = { +type ContentHeaderTitleProps = { title?: string; - className: string; + className?: string; }; -const DefaultTitle = ({ +const ContentHeaderTitle = ({ title = 'Unknown page', className, -}: DefaultTitleProps) => ( +}: ContentHeaderTitleProps) => ( ) : ( - + ); return ( - + <>
@@ -111,6 +111,6 @@ export const ContentHeader = ({
{children}
-
+ ); }; diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 96d024f524..227c6d1a60 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -16,17 +16,20 @@ import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, Tooltip, Typography } from '@material-ui/core'; +import { Box, Grid, makeStyles, Tooltip, Typography } from '@material-ui/core'; import React, { CSSProperties, PropsWithChildren, ReactNode } from 'react'; import { Helmet } from 'react-helmet'; import { Link } from '../../components/Link'; import { Breadcrumbs } from '../Breadcrumbs'; +const minHeaderHeight = 118; + const useStyles = makeStyles(theme => ({ header: { gridArea: 'pageHeader', padding: theme.spacing(3), - minHeight: 118, + height: 'fit-content', + minHeight: minHeaderHeight, width: '100%', boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', position: 'relative', @@ -34,26 +37,21 @@ const useStyles = makeStyles(theme => ({ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', - justifyContent: 'flex-end', alignItems: 'center', backgroundImage: theme.page.backgroundImage, backgroundPosition: 'center', backgroundSize: 'cover', }, leftItemsBox: { - flex: '1 1 auto', + maxWidth: '100%', + flexGrow: 1, + marginBottom: theme.spacing(1), }, rightItemsBox: { - flex: '0 1 auto', - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - alignItems: 'center', - marginRight: theme.spacing(1), + width: 'auto', }, title: { color: theme.palette.bursts.fontColor, - lineHeight: '1.0em', wordBreak: 'break-all', fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', marginBottom: theme.spacing(1), @@ -199,7 +197,7 @@ export const Header = ({ <>
-
+ -
-
{children}
+ + + {children} +
); diff --git a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx index 1534656f24..8557ceddf7 100644 --- a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx +++ b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx @@ -14,29 +14,25 @@ * limitations under the License. */ -import { Link, makeStyles, Typography } from '@material-ui/core'; +import { Link, makeStyles, Typography, Grid } from '@material-ui/core'; import React from 'react'; const useStyles = makeStyles(theme => ({ root: { textAlign: 'left', - margin: theme.spacing(2), - display: 'inline-block', }, label: { - color: '#FFFFFF', + color: theme.palette.common.white, fontWeight: 'bold', - lineHeight: '16px', letterSpacing: 0, - fontSize: 14, - height: '16px', - marginBottom: 2, + fontSize: theme.typography.fontSize, + marginBottom: theme.spacing(1) / 2, + lineHeight: 1, }, value: { color: 'rgba(255, 255, 255, 0.8)', - lineHeight: '16px', - fontSize: 14, - height: '16px', + fontSize: theme.typography.fontSize, + lineHeight: 1, }, })); @@ -64,9 +60,11 @@ export const HeaderLabel = ({ label, value, url }: HeaderLabelProps) => { /> ); return ( - - {label} - {url ? {content} : content} - + + + {label} + {url ? {content} : content} + + ); }; diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerLayout.tsx b/packages/core-components/src/layout/Page/PageWithHeader.tsx similarity index 50% rename from plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerLayout.tsx rename to packages/core-components/src/layout/Page/PageWithHeader.tsx index 5638c23634..70a6c1f693 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerLayout.tsx +++ b/packages/core-components/src/layout/Page/PageWithHeader.tsx @@ -14,27 +14,22 @@ * limitations under the License. */ -import React from 'react'; +import React, { PropsWithChildren, ComponentProps } from 'react'; -import { Header, Page } from '@backstage/core-components'; -import { useApi, configApiRef } from '@backstage/core-plugin-api'; +import { Header } from '../Header'; +import { Page } from './Page'; -type Props = { - children?: React.ReactNode; -}; -export const ApiExplorerLayout = ({ children }: Props) => { - const configApi = useApi(configApiRef); - const generatedSubtitle = `${ - configApi.getOptionalString('organization.name') ?? 'Backstage' - } API Explorer`; - return ( - -
- {children} - - ); +type PageWithHeaderProps = ComponentProps & { + themeId: string; }; + +export const PageWithHeader = ({ + themeId, + children, + ...props +}: PropsWithChildren) => ( + +
+ {children} + +); diff --git a/packages/core-components/src/layout/Page/index.ts b/packages/core-components/src/layout/Page/index.ts index d2523e8467..91db73657e 100644 --- a/packages/core-components/src/layout/Page/index.ts +++ b/packages/core-components/src/layout/Page/index.ts @@ -15,3 +15,4 @@ */ export { Page } from './Page'; +export { PageWithHeader } from './PageWithHeader'; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 28abbce53c..3944df0b50 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -44,7 +44,6 @@ const useStyles = makeStyles(theme => { drawerWidthOpen, iconContainerWidth, } = sidebarConfig; - return { root: { color: theme.palette.navigation.color, @@ -97,6 +96,9 @@ const useStyles = makeStyles(theme => { fontWeight: 'bold', fontSize: theme.typography.fontSize, }, + searchFieldHTMLInput: { + padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + }, searchContainer: { width: drawerWidthOpen - iconContainerWidth, }, @@ -271,6 +273,9 @@ export const SidebarSearchField = (props: SidebarSearchFieldProps) => { disableUnderline: true, className: classes.searchField, }} + inputProps={{ + className: classes.searchFieldHTMLInput, + }} /> diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index f3cb134fe1..f02d3ae140 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -13,21 +13,29 @@ import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { SvgIconProps } from '@material-ui/core'; +// Warning: (ae-missing-release-tag) "AlertApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AlertApi = { post(alert: AlertMessage): void; alert$(): Observable; }; +// Warning: (ae-missing-release-tag) "alertApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const alertApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "AlertMessage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AlertMessage = { message: string; severity?: 'success' | 'info' | 'warning' | 'error'; }; +// Warning: (ae-missing-release-tag) "AnyApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AnyApiFactory = ApiFactory< unknown, @@ -37,9 +45,13 @@ export type AnyApiFactory = ApiFactory< } >; +// Warning: (ae-missing-release-tag) "AnyApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AnyApiRef = ApiRef; +// Warning: (ae-missing-release-tag) "ApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiFactory< Api, @@ -53,17 +65,23 @@ export type ApiFactory< factory(deps: Deps): Impl; }; +// Warning: (ae-missing-release-tag) "ApiHolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiHolder = { get(api: ApiRef): T | undefined; }; +// Warning: (ae-missing-release-tag) "ApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiRef = { id: string; T: T; }; +// Warning: (ae-missing-release-tag) "ApiRefsToTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiRefsToTypes< T extends { @@ -73,9 +91,13 @@ export type ApiRefsToTypes< [key in keyof T]: ApiRefType; }; +// Warning: (ae-missing-release-tag) "ApiRefType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiRefType = T extends ApiRef ? U : never; +// Warning: (ae-missing-release-tag) "AppComponents" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppComponents = { NotFoundErrorPage: ComponentType<{}>; @@ -86,6 +108,8 @@ export type AppComponents = { SignInPage?: ComponentType; }; +// Warning: (ae-missing-release-tag) "AppContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AppContext = { getPlugins(): BackstagePlugin[]; @@ -93,6 +117,8 @@ export type AppContext = { getComponents(): AppComponents; }; +// Warning: (ae-missing-release-tag) "AppTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AppTheme = { id: string; @@ -102,6 +128,8 @@ export type AppTheme = { icon?: React.ReactElement; }; +// Warning: (ae-missing-release-tag) "AppThemeApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AppThemeApi = { getInstalledThemes(): AppTheme[]; @@ -110,9 +138,13 @@ export type AppThemeApi = { setActiveThemeId(themeId?: string): void; }; +// Warning: (ae-missing-release-tag) "appThemeApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const appThemeApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "attachComponentData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function attachComponentData

( component: ComponentType

, @@ -120,40 +152,55 @@ export function attachComponentData

( data: unknown, ): void; +// Warning: (ae-missing-release-tag) "auth0AuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const auth0AuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; +// Warning: (ae-missing-release-tag) "AuthProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AuthProvider = { title: string; icon: IconComponent; }; +// Warning: (ae-missing-release-tag) "AuthRequester" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AuthRequester = ( scopes: Set, ) => Promise; +// Warning: (ae-missing-release-tag) "AuthRequesterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AuthRequesterOptions = { provider: AuthProvider; onAuthRequest(scopes: Set): Promise; }; +// Warning: (ae-missing-release-tag) "AuthRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AuthRequestOptions = { optional?: boolean; instantPopup?: boolean; }; +// Warning: (ae-missing-release-tag) "BackstageIdentity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstageIdentity = { id: string; idToken: string; }; +// Warning: (tsdoc-undefined-tag) The TSDoc tag "@IdentityApi" is not defined in this configuration +// Warning: (ae-missing-release-tag) "BackstageIdentityApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type BackstageIdentityApi = { getBackstageIdentity( @@ -161,6 +208,10 @@ export type BackstageIdentityApi = { ): Promise; }; +// Warning: (ae-forgotten-export) The symbol "AnyRoutes" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "AnyExternalRoutes" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "BackstagePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstagePlugin< Routes extends AnyRoutes = {}, @@ -174,18 +225,27 @@ export type BackstagePlugin< externalRoutes: ExternalRoutes; }; +// Warning: (ae-missing-release-tag) "BootErrorPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BootErrorPageProps = { step: 'load-config' | 'load-chunk'; error: Error; }; +// Warning: (ae-missing-release-tag) "ConfigApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ConfigApi = Config; +// Warning: (ae-missing-release-tag) "configApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const configApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "createApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "createApiFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createApiFactory< Api, @@ -201,14 +261,22 @@ export function createApiFactory( instance: Impl, ): ApiFactory; +// Warning: (ae-forgotten-export) The symbol "ApiRefConfig" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createApiRef(config: ApiRefConfig): ApiRef; +// Warning: (ae-missing-release-tag) "createComponentExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createComponentExtension< T extends (props: any) => JSX.Element | null >(options: { component: ComponentLoader }): Extension; +// Warning: (ae-forgotten-export) The symbol "OptionalParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createExternalRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createExternalRouteRef< Params extends { @@ -222,6 +290,8 @@ export function createExternalRouteRef< optional?: Optional; }): ExternalRouteRef, Optional>; +// Warning: (ae-missing-release-tag) "createPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createPlugin< Routes extends AnyRoutes = {}, @@ -230,6 +300,8 @@ export function createPlugin< config: PluginConfig, ): BackstagePlugin; +// Warning: (ae-missing-release-tag) "createReactExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createReactExtension< T extends (props: any) => JSX.Element | null @@ -238,11 +310,15 @@ export function createReactExtension< data?: Record; }): Extension; +// Warning: (ae-missing-release-tag) "createRoutableExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRoutableExtension< T extends (props: any) => JSX.Element | null >(options: { component: () => Promise; mountPoint: RouteRef }): Extension; +// Warning: (ae-missing-release-tag) "createRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouteRef< Params extends { @@ -257,6 +333,11 @@ export function createRouteRef< title?: string; }): RouteRef>; +// Warning: (ae-forgotten-export) The symbol "AnyParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MakeSubRouteRef" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "PathParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createSubRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createSubRouteRef< Path extends string, @@ -267,14 +348,20 @@ export function createSubRouteRef< parent: RouteRef; }): MakeSubRouteRef, ParentParams>; +// Warning: (ae-missing-release-tag) "DiscoveryApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type DiscoveryApi = { getBaseUrl(pluginId: string): Promise; }; +// Warning: (ae-missing-release-tag) "discoveryApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const discoveryApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "ElementCollection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface ElementCollection { findComponentData(query: { key: string }): T[]; @@ -289,6 +376,8 @@ export interface ElementCollection { }): ElementCollection; } +// Warning: (ae-missing-release-tag) "ErrorApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ErrorApi = { post(error: Error_2, context?: ErrorContext): void; @@ -298,9 +387,13 @@ export type ErrorApi = { }>; }; +// Warning: (ae-missing-release-tag) "errorApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const errorApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "ErrorBoundaryFallbackProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ErrorBoundaryFallbackProps = { plugin?: BackstagePlugin; @@ -308,16 +401,22 @@ export type ErrorBoundaryFallbackProps = { resetError: () => void; }; +// Warning: (ae-missing-release-tag) "ErrorContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ErrorContext = { hidden?: boolean; }; +// Warning: (ae-missing-release-tag) "Extension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Extension = { expose(plugin: BackstagePlugin): T; }; +// Warning: (ae-missing-release-tag) "ExternalRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ExternalRouteRef< Params extends AnyParams = any, @@ -328,18 +427,24 @@ export type ExternalRouteRef< optional?: Optional; }; +// Warning: (ae-missing-release-tag) "FeatureFlag" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type FeatureFlag = { name: string; pluginId: string; }; +// Warning: (ae-missing-release-tag) "FeatureFlagOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type FeatureFlagOutput = { type: 'feature-flag'; name: string; }; +// Warning: (ae-missing-release-tag) "FeatureFlagsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface FeatureFlagsApi { getRegisteredFlags(): FeatureFlag[]; @@ -348,20 +453,28 @@ export interface FeatureFlagsApi { save(options: FeatureFlagsSaveOptions): void; } +// Warning: (ae-missing-release-tag) "featureFlagsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const featureFlagsApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "FeatureFlagsHooks" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type FeatureFlagsHooks = { register(name: string): void; }; +// Warning: (ae-missing-release-tag) "FeatureFlagsSaveOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type FeatureFlagsSaveOptions = { states: Record; merge?: boolean; }; +// Warning: (ae-missing-release-tag) "FeatureFlagState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum FeatureFlagState { // (undocumented) @@ -370,22 +483,30 @@ export enum FeatureFlagState { None = 0, } +// Warning: (ae-missing-release-tag) "getComponentData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getComponentData( node: ReactNode, type: string, ): T | undefined; +// Warning: (ae-missing-release-tag) "githubAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const githubAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; +// Warning: (ae-missing-release-tag) "gitlabAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const gitlabAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; +// Warning: (ae-missing-release-tag) "googleAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const googleAuthApiRef: ApiRef< OAuthApi & @@ -395,11 +516,15 @@ export const googleAuthApiRef: ApiRef< SessionApi >; +// Warning: (ae-missing-release-tag) "IconComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type IconComponent = ComponentType<{ fontSize?: 'default' | 'small' | 'large'; }>; +// Warning: (ae-missing-release-tag) "IdentityApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type IdentityApi = { getUserId(): string; @@ -408,9 +533,13 @@ export type IdentityApi = { signOut(): Promise; }; +// Warning: (ae-missing-release-tag) "identityApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const identityApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "microsoftAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const microsoftAuthApiRef: ApiRef< OAuthApi & @@ -420,6 +549,8 @@ export const microsoftAuthApiRef: ApiRef< SessionApi >; +// Warning: (ae-missing-release-tag) "oauth2ApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const oauth2ApiRef: ApiRef< OAuthApi & @@ -429,6 +560,8 @@ export const oauth2ApiRef: ApiRef< SessionApi >; +// Warning: (ae-missing-release-tag) "OAuthApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type OAuthApi = { getAccessToken( @@ -437,6 +570,8 @@ export type OAuthApi = { ): Promise; }; +// Warning: (ae-missing-release-tag) "OAuthRequestApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type OAuthRequestApi = { createAuthRequester( @@ -445,12 +580,18 @@ export type OAuthRequestApi = { authRequest$(): Observable; }; +// Warning: (ae-missing-release-tag) "oauthRequestApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const oauthRequestApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "OAuthScope" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type OAuthScope = string | string[]; +// Warning: (ae-missing-release-tag) "Observable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Observable = { [Symbol.observable](): Observable; @@ -462,6 +603,8 @@ export type Observable = { ): Subscription; }; +// Warning: (ae-missing-release-tag) "Observer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Observer = { next?(value: T): void; @@ -469,6 +612,8 @@ export type Observer = { complete?(): void; }; +// Warning: (ae-missing-release-tag) "oidcAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const oidcAuthApiRef: ApiRef< OAuthApi & @@ -478,6 +623,8 @@ export const oidcAuthApiRef: ApiRef< SessionApi >; +// Warning: (ae-missing-release-tag) "oktaAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const oktaAuthApiRef: ApiRef< OAuthApi & @@ -487,6 +634,8 @@ export const oktaAuthApiRef: ApiRef< SessionApi >; +// Warning: (ae-missing-release-tag) "oneloginAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const oneloginAuthApiRef: ApiRef< OAuthApi & @@ -496,11 +645,15 @@ export const oneloginAuthApiRef: ApiRef< SessionApi >; +// Warning: (ae-missing-release-tag) "OpenIdConnectApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type OpenIdConnectApi = { getIdToken(options?: AuthRequestOptions): Promise; }; +// Warning: (ae-missing-release-tag) "PendingAuthRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type PendingAuthRequest = { provider: AuthProvider; @@ -508,6 +661,8 @@ export type PendingAuthRequest = { trigger(): Promise; }; +// Warning: (ae-missing-release-tag) "PluginConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PluginConfig< Routes extends AnyRoutes, @@ -520,14 +675,20 @@ export type PluginConfig< externalRoutes?: ExternalRoutes; }; +// Warning: (ae-missing-release-tag) "PluginHooks" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PluginHooks = { featureFlags: FeatureFlagsHooks; }; +// Warning: (ae-missing-release-tag) "PluginOutput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PluginOutput = FeatureFlagOutput; +// Warning: (ae-missing-release-tag) "ProfileInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ProfileInfo = { email?: string; @@ -535,19 +696,27 @@ export type ProfileInfo = { picture?: string; }; +// Warning: (ae-missing-release-tag) "ProfileInfoApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ProfileInfoApi = { getProfile(options?: AuthRequestOptions): Promise; }; +// Warning: (ae-missing-release-tag) "RouteOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type RouteOptions = { exact?: boolean; }; +// Warning: (ae-missing-release-tag) "RoutePath" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type RoutePath = string; +// Warning: (ae-missing-release-tag) "RouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type RouteRef = { readonly [routeRefType]: 'absolute'; @@ -557,11 +726,15 @@ export type RouteRef = { title?: string; }; +// Warning: (ae-missing-release-tag) "samlAuthApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const samlAuthApiRef: ApiRef< ProfileInfoApi & BackstageIdentityApi & SessionApi >; +// Warning: (ae-missing-release-tag) "SessionApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type SessionApi = { signIn(): Promise; @@ -569,6 +742,8 @@ export type SessionApi = { sessionState$(): Observable; }; +// Warning: (ae-missing-release-tag) "SessionState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export enum SessionState { // (undocumented) @@ -577,11 +752,15 @@ export enum SessionState { SignedOut = 'SignedOut', } +// Warning: (ae-missing-release-tag) "SignInPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SignInPageProps = { onResult(result: SignInResult): void; }; +// Warning: (ae-missing-release-tag) "SignInResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SignInResult = { userId: string; @@ -590,24 +769,45 @@ export type SignInResult = { signOut?: () => Promise; }; +// Warning: (ae-missing-release-tag) "StorageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface StorageApi { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' forBucket(name: string): StorageApi; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-undefined-tag) The TSDoc tag "@return" is not defined in this configuration get(key: string): T | undefined; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' observe$(key: string): Observable>; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' remove(key: string): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' set(key: string, data: any): Promise; } +// Warning: (ae-missing-release-tag) "storageApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const storageApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "StorageValueChange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type StorageValueChange = { key: string; newValue?: T; }; +// Warning: (ae-missing-release-tag) "SubRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SubRouteRef = { readonly [routeRefType]: 'sub'; @@ -616,26 +816,38 @@ export type SubRouteRef = { params: ParamKeys; }; +// Warning: (ae-missing-release-tag) "Subscription" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Subscription = { unsubscribe(): void; readonly closed: boolean; }; +// Warning: (ae-missing-release-tag) "TypesToApiRefs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TypesToApiRefs = { [key in keyof T]: ApiRef; }; +// Warning: (ae-missing-release-tag) "useApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useApi(apiRef: ApiRef): T; +// Warning: (ae-missing-release-tag) "useApiHolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useApiHolder(): ApiHolder; +// Warning: (ae-missing-release-tag) "useApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const useApp: () => AppContext; +// Warning: (ae-missing-release-tag) "useElementFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function useElementFilter( node: ReactNode, @@ -643,9 +855,15 @@ export function useElementFilter( dependencies?: any[], ): T; +// Warning: (ae-missing-release-tag) "UserFlags" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type UserFlags = {}; +// Warning: (ae-forgotten-export) The symbol "RouteFunc" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "useRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "useRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useRouteRef( routeRef: ExternalRouteRef, @@ -656,11 +874,15 @@ export function useRouteRef( routeRef: RouteRef | SubRouteRef, ): RouteFunc; +// Warning: (ae-missing-release-tag) "useRouteRefParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useRouteRefParams( _routeRef: RouteRef | SubRouteRef, ): Params; +// Warning: (ae-missing-release-tag) "withApis" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function withApis( apis: TypesToApiRefs, @@ -671,5 +893,17 @@ export function withApis( displayName: string; }; +// Warnings were encountered during analysis: +// +// src/apis/definitions/ErrorApi.d.ts:37:5 - (ae-forgotten-export) The symbol "Error" needs to be exported by the entry point index.d.ts +// src/apis/definitions/auth.d.ts:29:8 - (tsdoc-undefined-tag) The TSDoc tag "@default" is not defined in this configuration +// src/apis/definitions/auth.d.ts:38:8 - (tsdoc-undefined-tag) The TSDoc tag "@default" is not defined in this configuration +// src/apis/definitions/auth.d.ts:96:68 - (tsdoc-undefined-tag) The TSDoc tag "@AuthRequestOptions" is not defined in this configuration +// src/apis/definitions/auth.d.ts:110:16 - (tsdoc-undefined-tag) The TSDoc tag "@IdentityApi" is not defined in this configuration +// src/apis/definitions/auth.d.ts:113:68 - (tsdoc-undefined-tag) The TSDoc tag "@AuthRequestOptions" is not defined in this configuration +// src/extensions/extensions.d.ts:14:5 - (ae-forgotten-export) The symbol "ComponentLoader" needs to be exported by the entry point index.d.ts +// src/routing/RouteRef.d.ts:34:5 - (ae-forgotten-export) The symbol "OldIconComponent" needs to be exported by the entry point index.d.ts +// src/routing/types.d.ts:30:5 - (ae-forgotten-export) The symbol "ParamKeys" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 91863f51d7..1bbb66710b 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/create-app +## 0.3.32 + +### Patch Changes + +- 03bf17e9b: Improve the responsiveness of the EntityPage UI. With this the Header component should scale with the screen size & wrapping should not cause overflowing/blocking of links. Additionally enforce the Pages using the Grid Layout to use it across all screen sizes & to wrap as intended. + + To benefit from the improved responsive layout, the `EntityPage` in existing Backstage applications should be updated to set the `xs` column size on each grid item in the page, as this does not default. For example: + + ```diff + - + + + ``` + +- eb740ee24: Moved sample software templates to the [backstage/software-templates](https://github.com/backstage/software-templates) repository. If you previously referenced the sample templates straight from `scaffolder-backend` plugin in the main [backstage/backstage](https://github.com/backstage/backstage) repository in your `app-config.yaml`, these references will need to be updated. + + See https://github.com/backstage/software-templates + +## 0.3.31 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. + ## 0.3.30 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index b3f587ac32..cedcba42c7 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.3.30", + "version": "0.3.32", "private": false, "publishConfig": { "access": "public" diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index 9a04f38c14..81e4c3a739 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -100,11 +100,11 @@ catalog: # Backstage example templates - type: url - target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml + target: https://github.com/backstage/software-templates/blob/master/scaffolder-templates/react-ssr-template/template.yaml rules: - allow: [Template] - type: url - target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml + target: https://github.com/backstage/software-templates/blob/master/scaffolder-templates/springboot-grpc-template/template.yaml rules: - allow: [Template] - type: url @@ -112,6 +112,6 @@ catalog: rules: - allow: [Template] - type: url - target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml + target: https://github.com/backstage/software-templates/blob/master/scaffolder-templates/docs-template/template.yaml rules: - allow: [Template] diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index e3dd5be3c7..2074a2712c 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -157,6 +157,13 @@ const websiteEntityPage = ( ); +/** + * NOTE: This page is designed to work on small screens such as mobile devices. + * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`, + * since this does not default. If no breakpoints are used, the items will equitably share the asvailable space. + * https://material-ui.com/components/grid/#basic-grid. + */ + const defaultEntityPage = ( diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 9cd431fba8..ea620350ef 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/dev-utils +## 0.2.3 + +### Patch Changes + +- 01001a324: Allow custom theme for dev app. +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/plugin-catalog-react@0.3.1 + - @backstage/core-app-api@0.1.5 + - @backstage/test-utils@0.1.15 + +## 0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.1 ### Patch Changes diff --git a/packages/dev-utils/api-report.md b/packages/dev-utils/api-report.md index 18bee415ff..5f58b297d5 100644 --- a/packages/dev-utils/api-report.md +++ b/packages/dev-utils/api-report.md @@ -6,6 +6,7 @@ /// import { ApiFactory } from '@backstage/core-plugin-api'; +import { AppTheme } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { createPlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; @@ -13,9 +14,14 @@ import { GridProps } from '@material-ui/core'; import { IconComponent } from '@backstage/core-plugin-api'; import { ReactNode } from 'react'; +// Warning: (ae-forgotten-export) The symbol "DevAppBuilder" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createDevApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createDevApp(): DevAppBuilder; +// Warning: (ae-missing-release-tag) "EntityGridItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityGridItem: ({ entity, diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 1f6833e573..a80745284c 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "0.2.1", + "version": "0.2.3", "private": false, "publishConfig": { "access": "public", @@ -29,13 +29,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-app-api": "^0.1.3", - "@backstage/core-components": "^0.1.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/catalog-model": "^0.9.0", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.6", - "@backstage/test-utils": "^0.1.14", + "@backstage/plugin-catalog-react": "^0.3.1", + "@backstage/test-utils": "^0.1.15", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -50,7 +50,7 @@ "react-router-dom": "6.0.0-beta.0" }, "devDependencies": { - "@backstage/cli": "^0.7.2", + "@backstage/cli": "^0.7.5", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 508eb105f1..e6338cd467 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -37,6 +37,7 @@ import { import { AnyApiFactory, ApiFactory, + AppTheme, attachComponentData, configApiRef, createApiFactory, @@ -79,6 +80,7 @@ class DevAppBuilder { private readonly sidebarItems = new Array(); private defaultPage?: string; + private themes?: Array; /** * Register one or more plugins to render in the dev app @@ -144,6 +146,14 @@ class DevAppBuilder { return this; } + /** + * Adds an array of themes to overide the default theme. + */ + addThemes(themes: AppTheme[]) { + this.themes = themes; + return this; + } + /** * Build a DevApp component using the resources registered so far */ @@ -166,6 +176,7 @@ class DevAppBuilder { const app = createApp({ apis, plugins: this.plugins, + themes: this.themes, bindRoutes: ({ bind }) => { for (const plugin of this.plugins ?? []) { const targets: Record> = {}; diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md index 019b6dd387..6aa69592b7 100644 --- a/packages/errors/api-report.md +++ b/packages/errors/api-report.md @@ -5,12 +5,18 @@ ```ts import { JsonObject } from '@backstage/config'; +// Warning: (ae-missing-release-tag) "AuthenticationError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class AuthenticationError extends CustomErrorBase {} +// Warning: (ae-missing-release-tag) "ConflictError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class ConflictError extends CustomErrorBase {} +// Warning: (ae-missing-release-tag) "CustomErrorBase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CustomErrorBase extends Error { constructor(message?: string, cause?: Error); @@ -18,11 +24,15 @@ export class CustomErrorBase extends Error { readonly cause?: Error; } +// Warning: (ae-missing-release-tag) "deserializeError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function deserializeError( data: SerializedError, ): T; +// Warning: (ae-missing-release-tag) "ErrorResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ErrorResponse = { error: SerializedError; @@ -35,21 +45,34 @@ export type ErrorResponse = { }; }; +// Warning: (ae-missing-release-tag) "InputError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class InputError extends CustomErrorBase {} +// Warning: (ae-missing-release-tag) "NotAllowedError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class NotAllowedError extends CustomErrorBase {} +// Warning: (ae-missing-release-tag) "NotFoundError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class NotFoundError extends CustomErrorBase {} +// Warning: (ae-missing-release-tag) "NotModifiedError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class NotModifiedError extends CustomErrorBase {} +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "parseErrorResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function parseErrorResponse(response: Response): Promise; +// Warning: (ae-missing-release-tag) "ResponseError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class ResponseError extends Error { constructor(props: { @@ -64,6 +87,8 @@ export class ResponseError extends Error { readonly response: Response; } +// Warning: (ae-missing-release-tag) "SerializedError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type SerializedError = JsonObject & { name: string; @@ -72,6 +97,10 @@ export type SerializedError = JsonObject & { code?: string; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters +// Warning: (ae-missing-release-tag) "serializeError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function serializeError( error: Error, diff --git a/packages/integration-react/api-report.md b/packages/integration-react/api-report.md index e2574196e0..ecd3ae54c3 100644 --- a/packages/integration-react/api-report.md +++ b/packages/integration-react/api-report.md @@ -9,6 +9,8 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { Config } from '@backstage/config'; import { ScmIntegrationRegistry } from '@backstage/integration'; +// Warning: (ae-missing-release-tag) "ScmIntegrationIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ScmIntegrationIcon: ({ type, @@ -16,12 +18,16 @@ export const ScmIntegrationIcon: ({ type?: string | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "ScmIntegrationsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ScmIntegrationsApi { // (undocumented) static fromConfig(config: Config): ScmIntegrationRegistry; } +// Warning: (ae-missing-release-tag) "scmIntegrationsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const scmIntegrationsApiRef: ApiRef; diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index fb06ca0de6..160a0a12ca 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -6,11 +6,15 @@ import { Config } from '@backstage/config'; import { RestEndpointMethodTypes } from '@octokit/rest'; +// Warning: (ae-missing-release-tag) "AzureIntegration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class AzureIntegration implements ScmIntegration { constructor(integrationConfig: AzureIntegrationConfig); // (undocumented) get config(): AzureIntegrationConfig; + // Warning: (ae-forgotten-export) The symbol "ScmIntegrationsFactory" needs to be exported by the entry point index.d.ts + // // (undocumented) static factory: ScmIntegrationsFactory; // (undocumented) @@ -27,12 +31,16 @@ export class AzureIntegration implements ScmIntegration { get type(): string; } +// Warning: (ae-missing-release-tag) "AzureIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type AzureIntegrationConfig = { host: string; token?: string; }; +// Warning: (ae-missing-release-tag) "BitbucketIntegration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class BitbucketIntegration implements ScmIntegration { constructor(integrationConfig: BitbucketIntegrationConfig); @@ -54,6 +62,8 @@ export class BitbucketIntegration implements ScmIntegration { get type(): string; } +// Warning: (ae-missing-release-tag) "BitbucketIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type BitbucketIntegrationConfig = { host: string; @@ -63,6 +73,8 @@ export type BitbucketIntegrationConfig = { appPassword?: string; }; +// Warning: (ae-missing-release-tag) "defaultScmResolveUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function defaultScmResolveUrl(options: { url: string; @@ -70,66 +82,125 @@ export function defaultScmResolveUrl(options: { lineNumber?: number; }): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getAzureCommitsUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getAzureCommitsUrl(url: string): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getAzureDownloadUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getAzureDownloadUrl(url: string): string; +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getAzureFileFetchUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getAzureFileFetchUrl(url: string): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getAzureRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getAzureRequestOptions( config: AzureIntegrationConfig, additionalHeaders?: Record, ): RequestInit; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getBitbucketDefaultBranch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getBitbucketDefaultBranch( url: string, config: BitbucketIntegrationConfig, ): Promise; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getBitbucketDownloadUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getBitbucketDownloadUrl( url: string, config: BitbucketIntegrationConfig, ): Promise; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getBitbucketFileFetchUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getBitbucketFileFetchUrl( url: string, config: BitbucketIntegrationConfig, ): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getBitbucketRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getBitbucketRequestOptions( config: BitbucketIntegrationConfig, ): RequestInit; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getGitHubFileFetchUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getGitHubFileFetchUrl( url: string, config: GitHubIntegrationConfig, ): string; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getGitHubRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getGitHubRequestOptions( config: GitHubIntegrationConfig, ): RequestInit; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getGitLabFileFetchUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getGitLabFileFetchUrl( url: string, config: GitLabIntegrationConfig, ): Promise; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "getGitLabRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getGitLabRequestOptions( config: GitLabIntegrationConfig, ): RequestInit; +// Warning: (ae-missing-release-tag) "GithubAppCredentialsMux" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GithubAppCredentialsMux { constructor(config: GitHubIntegrationConfig); @@ -141,16 +212,33 @@ export class GithubAppCredentialsMux { getAppToken(owner: string, repo?: string): Promise; } +// Warning: (ae-missing-release-tag) "GithubCredentialsProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GithubCredentialsProvider { // (undocumented) static create(config: GitHubIntegrationConfig): GithubCredentialsProvider; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-undefined-tag) The TSDoc tag "@type" is not defined in this configuration + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (ae-forgotten-export) The symbol "GithubCredentials" needs to be exported by the entry point index.d.ts getCredentials(opts: { url: string }): Promise; } +// Warning: (ae-missing-release-tag) "GithubCredentialType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GithubCredentialType = 'app' | 'token'; +// Warning: (ae-missing-release-tag) "GitHubIntegration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GitHubIntegration implements ScmIntegration { constructor(integrationConfig: GitHubIntegrationConfig); @@ -172,6 +260,8 @@ export class GitHubIntegration implements ScmIntegration { get type(): string; } +// Warning: (ae-missing-release-tag) "GitHubIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type GitHubIntegrationConfig = { host: string; @@ -181,6 +271,8 @@ export type GitHubIntegrationConfig = { apps?: GithubAppConfig[]; }; +// Warning: (ae-missing-release-tag) "GitLabIntegration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GitLabIntegration implements ScmIntegration { constructor(integrationConfig: GitLabIntegrationConfig); @@ -202,6 +294,8 @@ export class GitLabIntegration implements ScmIntegration { get type(): string; } +// Warning: (ae-missing-release-tag) "GitLabIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type GitLabIntegrationConfig = { host: string; @@ -210,60 +304,95 @@ export type GitLabIntegrationConfig = { baseUrl: string; }; +// Warning: (ae-missing-release-tag) "GoogleGcsIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type GoogleGcsIntegrationConfig = { clientEmail?: string; privateKey?: string; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readAzureIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readAzureIntegrationConfig( config: Config, ): AzureIntegrationConfig; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readAzureIntegrationConfigs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readAzureIntegrationConfigs( configs: Config[], ): AzureIntegrationConfig[]; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readBitbucketIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readBitbucketIntegrationConfig( config: Config, ): BitbucketIntegrationConfig; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readBitbucketIntegrationConfigs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readBitbucketIntegrationConfigs( configs: Config[], ): BitbucketIntegrationConfig[]; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readGitHubIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readGitHubIntegrationConfig( config: Config, ): GitHubIntegrationConfig; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readGitHubIntegrationConfigs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readGitHubIntegrationConfigs( configs: Config[], ): GitHubIntegrationConfig[]; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readGitLabIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readGitLabIntegrationConfig( config: Config, ): GitLabIntegrationConfig; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readGitLabIntegrationConfigs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readGitLabIntegrationConfigs( configs: Config[], ): GitLabIntegrationConfig[]; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readGoogleGcsIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readGoogleGcsIntegrationConfig( config: Config, ): GoogleGcsIntegrationConfig; +// Warning: (ae-missing-release-tag) "ScmIntegration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface ScmIntegration { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen resolveEditUrl(url: string): string; + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters resolveUrl(options: { url: string; base: string; @@ -273,6 +402,8 @@ export interface ScmIntegration { type: string; } +// Warning: (ae-missing-release-tag) "ScmIntegrationRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface ScmIntegrationRegistry extends ScmIntegrationsGroup { @@ -284,7 +415,11 @@ export interface ScmIntegrationRegistry github: ScmIntegrationsGroup; // (undocumented) gitlab: ScmIntegrationsGroup; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen resolveEditUrl(url: string): string; + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters resolveUrl(options: { url: string; base: string; @@ -292,8 +427,11 @@ export interface ScmIntegrationRegistry }): string; } +// Warning: (ae-missing-release-tag) "ScmIntegrations" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ScmIntegrations implements ScmIntegrationRegistry { + // Warning: (ae-forgotten-export) The symbol "IntegrationsByType" needs to be exported by the entry point index.d.ts constructor(integrationsByType: IntegrationsByType); // (undocumented) get azure(): ScmIntegrationsGroup; @@ -321,12 +459,22 @@ export class ScmIntegrations implements ScmIntegrationRegistry { }): string; } +// Warning: (ae-missing-release-tag) "ScmIntegrationsGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface ScmIntegrationsGroup { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen byHost(host: string): T | undefined; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen byUrl(url: string | URL): T | undefined; list(): T[]; } +// Warnings were encountered during analysis: +// +// src/github/config.d.ts:41:5 - (ae-forgotten-export) The symbol "GithubAppConfig" needs to be exported by the entry point index.d.ts +// src/gitlab/config.d.ts:27:68 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/gitlab/config.d.ts:27:63 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/search-common/api-report.md b/packages/search-common/api-report.md index 0ba995f673..504f9afb9c 100644 --- a/packages/search-common/api-report.md +++ b/packages/search-common/api-report.md @@ -5,6 +5,8 @@ ```ts import { JsonObject } from '@backstage/config'; +// Warning: (ae-missing-release-tag) "DocumentCollator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface DocumentCollator { // (undocumented) @@ -12,6 +14,8 @@ export interface DocumentCollator { readonly type: string; } +// Warning: (ae-missing-release-tag) "DocumentDecorator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface DocumentDecorator { // (undocumented) @@ -19,6 +23,8 @@ export interface DocumentDecorator { readonly types?: string[]; } +// Warning: (ae-missing-release-tag) "IndexableDocument" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface IndexableDocument { location: string; @@ -26,6 +32,8 @@ export interface IndexableDocument { title: string; } +// Warning: (ae-missing-release-tag) "SearchQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SearchQuery { // (undocumented) @@ -38,6 +46,8 @@ export interface SearchQuery { types?: string[]; } +// Warning: (ae-missing-release-tag) "SearchResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SearchResult { // (undocumented) @@ -46,6 +56,8 @@ export interface SearchResult { type: string; } +// Warning: (ae-missing-release-tag) "SearchResultSet" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SearchResultSet { // (undocumented) diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 02edf9c95f..ab5eff10e3 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,125 @@ # @backstage/techdocs-common +## 0.7.0 + +### Minor Changes + +- d32d01e5b: Improve the annotation `backstage.io/techdocs-ref: dir:` that links to a path that is relative to the source of the annotated entity. + This annotation works with the basic and the recommended flow, however, it will be most useful with the basic approach. + + This change remove the deprecation of the `dir` reference and provides first-class support for it. + In addition, this change removes the support of the deprecated `github`, `gitlab`, and `azure/api` locations from the `dir` reference preparer. + + #### Example Usage + + The annotation is convenient if the documentation is stored in the same location, i.e. the same git repository, as the `catalog-info.yaml`. + While it is still supported to add full URLs such as `backstage.io/techdocs-ref: url:https://...` for custom setups, documentation is mostly stored in the same repository as the entity definition. + By automatically resolving the target relative to the registration location of the entity, the configuration overhead for this default setup is minimized. + Since it leverages the `@backstage/integrations` package for the URL resolution, this is compatible with every supported source. + + Consider the following examples: + + 1. "I have a repository with a single `catalog-info.yaml` and a TechDocs page in the root folder!" + + ``` + https://github.com/backstage/example/tree/main/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: example + | > annotations: + | > backstage.io/techdocs-ref: dir:. # -> same folder + | > spec: {} + |- docs/ + |- mkdocs.yml + ``` + + 2. "I have a repository with a single `catalog-info.yaml` and my TechDocs page in located in a folder!" + + ``` + https://bitbucket.org/my-owner/my-project/src/master/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: example + | > annotations: + | > backstage.io/techdocs-ref: dir:./some-folder # -> subfolder + | > spec: {} + |- some-folder/ + |- docs/ + |- mkdocs.yml + ``` + + 3. "I have a mono repository that hosts multiple components!" + + ``` + https://dev.azure.com/organization/project/_git/repository + |- my-1st-module/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: my-1st-module + | > annotations: + | > backstage.io/techdocs-ref: dir:. # -> same folder + | > spec: {} + |- docs/ + |- mkdocs.yml + |- my-2nd-module/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: my-2nd-module + | > annotations: + | > backstage.io/techdocs-ref: dir:. # -> same folder + | > spec: {} + |- docs/ + |- mkdocs.yml + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Location + | > metadata: + | > name: example + | > spec: + | > targets: + | > - ./*/catalog-info.yaml + ``` + +### Patch Changes + +- 6e5aed1c9: Fix validation of mkdocs.yml docs_dir +- 250984333: Add link to https://backstage.io/docs/features/techdocs/configuration in the log warning message about updating techdocs.generate key. +- Updated dependencies + - @backstage/backend-common@0.8.7 + +## 0.6.8 + +### Patch Changes + +- d5eaab91d: Adds custom docker image support to the techdocs generator. This change adds a new `techdocs.generator` configuration key and deprecates the existing `techdocs.generators.techdocs` key. + + ```yaml + techdocs: + # recommended, going forward: + generator: + runIn: 'docker' # or 'local' + # New optional settings + dockerImage: my-org/techdocs # use a custom docker image + pullImage: false # disable automatic pulling of image (e.g. if custom docker login is required) + # legacy (deprecated): + generators: + techdocs: 'docker' # or 'local' + ``` + +- c18e8eb91: Provide optional `logger: Logger` and `logStream: Writable` arguments to the `GeneratorBase#run(...)` command. + They receive all log messages that are emitted during the generator run. +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + ## 0.6.7 ### Patch Changes diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md index 8cca624e11..6e845c9797 100644 --- a/packages/techdocs-common/api-report.md +++ b/packages/techdocs-common/api-report.md @@ -15,9 +15,12 @@ import { GitHubIntegrationConfig } from '@backstage/integration'; import { GitLabIntegrationConfig } from '@backstage/integration'; import { Logger as Logger_2 } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; +// Warning: (ae-missing-release-tag) "checkoutGitRepository" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const checkoutGitRepository: ( repoUrl: string, @@ -25,9 +28,13 @@ export const checkoutGitRepository: ( logger: Logger_2, ) => Promise; +// Warning: (ae-missing-release-tag) "CommonGitPreparer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CommonGitPreparer implements PreparerBase { constructor(config: Config, logger: Logger_2); + // Warning: (ae-forgotten-export) The symbol "PreparerResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) prepare( entity: Entity, @@ -37,24 +44,64 @@ export class CommonGitPreparer implements PreparerBase { ): Promise; } +// Warning: (ae-missing-release-tag) "DirectoryPreparer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DirectoryPreparer implements PreparerBase { - constructor(config: Config, logger: Logger_2, reader: UrlReader); + constructor(config: Config, _logger: Logger_2, reader: UrlReader); // (undocumented) - prepare(entity: Entity): Promise; + prepare( + entity: Entity, + options?: { + logger?: Logger_2; + etag?: string; + }, + ): Promise; } +// Warning: (ae-missing-release-tag) "GeneratorBase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GeneratorBase = { run(opts: GeneratorRunOptions): Promise; }; +// Warning: (ae-missing-release-tag) "GeneratorBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type GeneratorBuilder = { register(protocol: SupportedGeneratorKey, generator: GeneratorBase): void; get(entity: Entity): GeneratorBase; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-with-invalid-optional-name) The @param should not include a JSDoc-style optional name; it must not be enclosed in '[ ]' brackets. +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// Warning: (ae-missing-release-tag) "GeneratorRunOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type GeneratorRunOptions = { + inputDir: string; + outputDir: string; + parsedLocationAnnotation?: ParsedLocationAnnotation; + etag?: string; + logger: Logger_2; + logStream?: Writable; +}; + +// Warning: (ae-missing-release-tag) "Generators" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class Generators implements GeneratorBuilder { // (undocumented) @@ -74,18 +121,24 @@ export class Generators implements GeneratorBuilder { register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase): void; } +// Warning: (ae-missing-release-tag) "getAzureIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getAzureIntegrationConfig: ( config: Config, host: string, ) => AzureIntegrationConfig; +// Warning: (ae-missing-release-tag) "getDefaultBranch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getDefaultBranch: ( repositoryUrl: string, config: Config, ) => Promise; +// Warning: (ae-missing-release-tag) "getDocFilesFromRepository" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getDocFilesFromRepository: ( reader: UrlReader, @@ -98,57 +151,82 @@ export const getDocFilesFromRepository: ( | undefined, ) => Promise; +// Warning: (ae-missing-release-tag) "getGitHost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getGitHost(url: string): string; +// Warning: (ae-missing-release-tag) "getGitHubIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getGitHubIntegrationConfig: ( config: Config, host: string, ) => GitHubIntegrationConfig; +// Warning: (ae-missing-release-tag) "getGitLabIntegrationConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getGitLabIntegrationConfig: ( config: Config, host: string, ) => GitLabIntegrationConfig; +// Warning: (ae-missing-release-tag) "getGitRepositoryTempFolder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getGitRepositoryTempFolder: ( repositoryUrl: string, config: Config, ) => Promise; +// Warning: (ae-missing-release-tag) "getGitRepoType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getGitRepoType(url: string): string; +// Warning: (ae-missing-release-tag) "getLastCommitTimestamp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getLastCommitTimestamp: ( repositoryLocation: string, logger: Logger_2, ) => Promise; +// Warning: (ae-missing-release-tag) "getLocationForEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) -export const getLocationForEntity: (entity: Entity) => ParsedLocationAnnotation; +export const getLocationForEntity: ( + entity: Entity, + scmIntegration: ScmIntegrationRegistry, +) => ParsedLocationAnnotation; +// Warning: (ae-missing-release-tag) "getTokenForGitRepo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const getTokenForGitRepo: ( repositoryUrl: string, config: Config, ) => Promise; +// Warning: (ae-missing-release-tag) "ParsedLocationAnnotation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ParsedLocationAnnotation = { type: RemoteProtocol; target: string; }; +// Warning: (ae-missing-release-tag) "parseReferenceAnnotation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const parseReferenceAnnotation: ( annotationName: string, entity: Entity, ) => ParsedLocationAnnotation; +// Warning: (ae-missing-release-tag) "PreparerBase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PreparerBase = { prepare( @@ -160,14 +238,20 @@ export type PreparerBase = { ): Promise; }; +// Warning: (ae-missing-release-tag) "PreparerBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PreparerBuilder = { register(protocol: RemoteProtocol, preparer: PreparerBase): void; get(entity: Entity): PreparerBase; }; +// Warning: (ae-missing-release-tag) "Preparers" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class Preparers implements PreparerBuilder { + // Warning: (ae-forgotten-export) The symbol "factoryOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static fromConfig( config: Config, @@ -179,8 +263,12 @@ export class Preparers implements PreparerBuilder { register(protocol: RemoteProtocol, preparer: PreparerBase): void; } +// Warning: (ae-missing-release-tag) "Publisher" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class Publisher { + // Warning: (ae-forgotten-export) The symbol "factoryOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static fromConfig( config: Config, @@ -188,15 +276,31 @@ export class Publisher { ): Promise; } +// Warning: (ae-missing-release-tag) "PublisherBase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface PublisherBase { docsRouter(): express.Handler; fetchTechDocsMetadata(entityName: EntityName): Promise; + // Warning: (ae-forgotten-export) The symbol "ReadinessResponse" needs to be exported by the entry point index.d.ts getReadiness(): Promise; hasDocsBeenGenerated(entityName: Entity): Promise; + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (ae-forgotten-export) The symbol "MigrateRequest" needs to be exported by the entry point index.d.ts + migrateDocsCase?(migrateRequest: MigrateRequest): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (ae-forgotten-export) The symbol "PublishRequest" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "PublishResponse" needs to be exported by the entry point index.d.ts publish(request: PublishRequest): Promise; } +// Warning: (ae-missing-release-tag) "PublisherType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type PublisherType = | 'local' @@ -205,6 +309,8 @@ export type PublisherType = | 'azureBlobStorage' | 'openStackSwift'; +// Warning: (ae-missing-release-tag) "RemoteProtocol" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type RemoteProtocol = | 'url' @@ -214,6 +320,8 @@ export type RemoteProtocol = | 'file' | 'azure/api'; +// Warning: (ae-missing-release-tag) "TechdocsGenerator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class TechdocsGenerator implements GeneratorBase { constructor({ @@ -226,14 +334,30 @@ export class TechdocsGenerator implements GeneratorBase { config: Config; }); // (undocumented) + static fromConfig( + config: Config, + { + containerRunner, + logger, + }: { + containerRunner: ContainerRunner; + logger: Logger_2; + }, + ): Promise; + // (undocumented) run({ inputDir, outputDir, parsedLocationAnnotation, etag, + logger: childLogger, + logStream, }: GeneratorRunOptions): Promise; } +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "TechDocsMetadata" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type TechDocsMetadata = { site_name: string; @@ -241,6 +365,20 @@ export type TechDocsMetadata = { etag: string; }; +// Warning: (ae-missing-release-tag) "transformDirLocation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const transformDirLocation: ( + entity: Entity, + dirAnnotation: ParsedLocationAnnotation, + scmIntegrations: ScmIntegrationRegistry, +) => { + type: 'dir' | 'url'; + target: string; +}; + +// Warning: (ae-missing-release-tag) "UrlPreparer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class UrlPreparer implements PreparerBase { constructor(reader: UrlReader, logger: Logger_2); @@ -253,5 +391,13 @@ export class UrlPreparer implements PreparerBase { ): Promise; } +// Warnings were encountered during analysis: +// +// src/stages/generate/types.d.ts:44:5 - (ae-forgotten-export) The symbol "SupportedGeneratorKey" needs to be exported by the entry point index.d.ts +// src/stages/prepare/types.d.ts:18:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/stages/prepare/types.d.ts:19:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters +// src/stages/prepare/types.d.ts:21:33 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/stages/prepare/types.d.ts:21:16 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index de97e9815d..bb81c875fd 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.6.7", + "version": "0.7.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -36,9 +36,9 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@azure/identity": "^1.2.2", - "@azure/storage-blob": "^12.4.0", - "@backstage/backend-common": "^0.8.5", + "@azure/identity": "^1.5.0", + "@azure/storage-blob": "^12.5.0", + "@backstage/backend-common": "^0.8.7", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", @@ -60,7 +60,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.5", "@types/fs-extra": "^9.0.5", "@types/git-url-parse": "^9.0.0", "@types/js-yaml": "^4.0.0", diff --git a/packages/techdocs-common/src/helpers.test.ts b/packages/techdocs-common/src/helpers.test.ts index b94cbba572..69f09d92ed 100644 --- a/packages/techdocs-common/src/helpers.test.ts +++ b/packages/techdocs-common/src/helpers.test.ts @@ -19,14 +19,26 @@ import { SearchResponse, UrlReader, } from '@backstage/backend-common'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, getEntitySourceLocation } from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import os from 'os'; +import path from 'path'; import { Readable } from 'stream'; import { getDocFilesFromRepository, getLocationForEntity, parseReferenceAnnotation, + transformDirLocation, } from './helpers'; +jest.mock('@backstage/catalog-model', () => ({ + ...jest.requireActual('@backstage/catalog-model'), + getEntitySourceLocation: jest.fn(), +})); + +const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; + const entityBase: Entity = { metadata: { namespace: 'default', @@ -81,6 +93,10 @@ const mockEntityWithBadAnnotation: Entity = { }, }; +const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({})); + +afterEach(() => jest.resetAllMocks()); + describe('parseReferenceAnnotation', () => { it('should parse annotation', () => { const parsedLocationAnnotation = parseReferenceAnnotation( @@ -109,10 +125,158 @@ describe('parseReferenceAnnotation', () => { }); }); +describe('transformDirLocation', () => { + it.each` + techdocsRef | target + ${'dir:.'} | ${'https://my-url/folder/'} + ${'dir:./sub-folder'} | ${'https://my-url/folder/sub-folder'} + `( + 'should transform "$techdocsRef" for url type locations', + ({ techdocsRef, target }) => { + (getEntitySourceLocation as jest.Mock).mockReturnValue({ + type: 'url', + target: 'https://my-url/folder/', + }); + + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'backstage.io/techdocs-ref': techdocsRef, + }, + }, + }; + + const result = transformDirLocation( + entity, + parseReferenceAnnotation('backstage.io/techdocs-ref', entity), + scmIntegrations, + ); + + expect(result).toEqual({ type: 'url', target }); + }, + ); + + it.each` + techdocsRef | target + ${'dir:.'} | ${path.join(rootDir, 'working-copy')} + ${'dir:./sub-folder'} | ${path.join(rootDir, 'working-copy', 'sub-folder')} + `( + 'should transform "$techdocsRef" for file type locations', + ({ techdocsRef, target }) => { + (getEntitySourceLocation as jest.Mock).mockReturnValue({ + type: 'file', + target: path.join(rootDir, 'working-copy', 'catalog-info.yaml'), + }); + + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'backstage.io/techdocs-ref': techdocsRef, + }, + }, + }; + + const result = transformDirLocation( + entity, + parseReferenceAnnotation('backstage.io/techdocs-ref', entity), + scmIntegrations, + ); + + expect(result).toEqual({ type: 'dir', target }); + }, + ); + + it('should reject unsafe file location', () => { + (getEntitySourceLocation as jest.Mock).mockReturnValue({ + type: 'file', + target: '/tmp/catalog-info.yaml', + }); + + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'backstage.io/techdocs-ref': 'dir:..', + }, + }, + }; + + expect(() => + transformDirLocation( + entity, + parseReferenceAnnotation('backstage.io/techdocs-ref', entity), + scmIntegrations, + ), + ).toThrow( + /Relative path is not allowed to refer to a directory outside its parent/, + ); + }); + + it('should reject other location types', () => { + (getEntitySourceLocation as jest.Mock).mockReturnValue({ + type: 'other', + target: '/', + }); + + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'backstage.io/techdocs-ref': 'dir:.', + }, + }, + }; + + expect(() => + transformDirLocation( + entity, + parseReferenceAnnotation('backstage.io/techdocs-ref', entity), + scmIntegrations, + ), + ).toThrow(/Unable to resolve location type other/); + }); +}); + describe('getLocationForEntity', () => { + it('should handle dir locations', () => { + (getEntitySourceLocation as jest.Mock).mockReturnValue({ + type: 'url', + target: 'https://my-url/folder/', + }); + + const entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'test', + annotations: { + 'backstage.io/techdocs-ref': 'dir:.', + }, + }, + }; + + const parsedLocationAnnotation = getLocationForEntity( + entity, + scmIntegrations, + ); + expect(parsedLocationAnnotation.type).toBe('url'); + expect(parsedLocationAnnotation.target).toBe('https://my-url/folder/'); + }); + it('should get location for entity', () => { const parsedLocationAnnotation = getLocationForEntity( mockEntityWithAnnotation, + scmIntegrations, ); expect(parsedLocationAnnotation.type).toBe('url'); expect(parsedLocationAnnotation.target).toBe( diff --git a/packages/techdocs-common/src/helpers.ts b/packages/techdocs-common/src/helpers.ts index cac8a7047f..bb6461a37c 100644 --- a/packages/techdocs-common/src/helpers.ts +++ b/packages/techdocs-common/src/helpers.ts @@ -14,10 +14,19 @@ * limitations under the License. */ -import { Git, UrlReader } from '@backstage/backend-common'; -import { InputError } from '@backstage/errors'; -import { Entity, parseLocationReference } from '@backstage/catalog-model'; +import { + Git, + resolveSafeChildPath, + UrlReader, +} from '@backstage/backend-common'; +import { + Entity, + getEntitySourceLocation, + parseLocationReference, +} from '@backstage/catalog-model'; import { Config } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import fs from 'fs-extra'; import parseGitUrl from 'git-url-parse'; import os from 'os'; @@ -50,30 +59,77 @@ export const parseReferenceAnnotation = ( }; }; +/** + * TechDocs references of type `dir` are relative the source location of the entity. + * This function transforms relative references to absolute ones, based on the + * location the entity was ingested from. If the entity was registered by a `url` + * location, it returns a `url` location with a resolved target that points to the + * targeted subfolder. If the entity was registered by a `file` location, it returns + * an absolute `dir` location. + * + * @param entity - the entity with annotations + * @param dirAnnotation - the parsed techdocs-ref annotation of type 'dir' + * @param scmIntegrations - access to the scmIntegration to do url transformations + * @throws if the entity doesn't specify a `dir` location or is ingested from an unsupported location. + * @returns the transformed location with an absolute target. + */ +export const transformDirLocation = ( + entity: Entity, + dirAnnotation: ParsedLocationAnnotation, + scmIntegrations: ScmIntegrationRegistry, +): { type: 'dir' | 'url'; target: string } => { + const location = getEntitySourceLocation(entity); + + switch (location.type) { + case 'url': { + const target = scmIntegrations.resolveUrl({ + url: dirAnnotation.target, + base: location.target, + }); + + return { + type: 'url', + target, + }; + } + + case 'file': { + // only permit targets in the same folder as the target of the `file` location! + const target = resolveSafeChildPath( + path.dirname(location.target), + dirAnnotation.target, + ); + + return { + type: 'dir', + target, + }; + } + + default: + throw new InputError(`Unable to resolve location type ${location.type}`); + } +}; + export const getLocationForEntity = ( entity: Entity, + scmIntegration: ScmIntegrationRegistry, ): ParsedLocationAnnotation => { - const { type, target } = parseReferenceAnnotation( + const annotation = parseReferenceAnnotation( 'backstage.io/techdocs-ref', entity, ); - switch (type) { + switch (annotation.type) { case 'github': case 'gitlab': case 'azure/api': case 'url': - return { type, target }; + return annotation; case 'dir': - if (path.isAbsolute(target)) { - return { type, target }; - } - return parseReferenceAnnotation( - 'backstage.io/managed-by-location', - entity, - ); + return transformDirLocation(entity, annotation, scmIntegration); default: - throw new Error(`Invalid reference annotation ${type}`); + throw new Error(`Invalid reference annotation ${annotation.type}`); } }; diff --git a/packages/techdocs-common/src/stages/generate/__fixtures__/mkdocs_valid_doc_dir.yml b/packages/techdocs-common/src/stages/generate/__fixtures__/mkdocs_valid_doc_dir.yml new file mode 100644 index 0000000000..e75b06ada7 --- /dev/null +++ b/packages/techdocs-common/src/stages/generate/__fixtures__/mkdocs_valid_doc_dir.yml @@ -0,0 +1,3 @@ +site_name: Test site name +site_description: Test site description +docs_dir: docs/ diff --git a/packages/techdocs-common/src/stages/generate/helpers.test.ts b/packages/techdocs-common/src/stages/generate/helpers.test.ts index b6b71e3e2b..a5a6d99c29 100644 --- a/packages/techdocs-common/src/stages/generate/helpers.test.ts +++ b/packages/techdocs-common/src/stages/generate/helpers.test.ts @@ -47,6 +47,9 @@ const mkdocsYmlWithExtensions = fs.readFileSync( const mkdocsYmlWithRepoUrl = fs.readFileSync( resolvePath(__filename, '../__fixtures__/mkdocs_with_repo_url.yml'), ); +const mkdocsYmlWithValidDocDir = fs.readFileSync( + resolvePath(__filename, '../__fixtures__/mkdocs_valid_doc_dir.yml'), +); const mkdocsYmlWithInvalidDocDir = fs.readFileSync( resolvePath(__filename, '../__fixtures__/mkdocs_invalid_doc_dir.yml'), ); @@ -336,6 +339,7 @@ describe('helpers', () => { mockFs({ '/mkdocs.yml': mkdocsYml, '/mkdocs_with_extensions.yml': mkdocsYmlWithExtensions, + '/mkdocs_valid_doc_dir.yml': mkdocsYmlWithValidDocDir, '/mkdocs_invalid_doc_dir.yml': mkdocsYmlWithInvalidDocDir, }); }); @@ -351,6 +355,12 @@ describe('helpers', () => { ).resolves.toBeUndefined(); }); + it('should return true on when a valid docs_dir is present', async () => { + await expect( + validateMkdocsYaml(inputDir, '/mkdocs_valid_doc_dir.yml'), + ).resolves.toBeUndefined(); + }); + it('should return false on absolute doc_dir path', async () => { await expect( validateMkdocsYaml(inputDir, '/mkdocs_invalid_doc_dir.yml'), diff --git a/packages/techdocs-common/src/stages/generate/helpers.ts b/packages/techdocs-common/src/stages/generate/helpers.ts index 086377b4ef..590e75e7b2 100644 --- a/packages/techdocs-common/src/stages/generate/helpers.ts +++ b/packages/techdocs-common/src/stages/generate/helpers.ts @@ -19,6 +19,7 @@ import { isChildPath } from '@backstage/backend-common'; import { spawn } from 'child_process'; import fs from 'fs-extra'; import yaml, { DEFAULT_SCHEMA, Type } from 'js-yaml'; +import { resolve as resolvePath } from 'path'; import { PassThrough, Writable } from 'stream'; import { Logger } from 'winston'; import { ParsedLocationAnnotation } from '../../helpers'; @@ -178,7 +179,10 @@ export const validateMkdocsYaml = async ( schema: MKDOCS_SCHEMA, }); - if (mkdocsYml.docs_dir && !isChildPath(inputDir, mkdocsYml.docs_dir)) { + if ( + mkdocsYml.docs_dir && + !isChildPath(inputDir, resolvePath(inputDir, mkdocsYml.docs_dir)) + ) { throw new Error( `docs_dir configuration value in mkdocs can't be an absolute directory or start with ../ for security reasons. Use relative paths instead which are resolved relative to your mkdocs.yml file location.`, diff --git a/packages/techdocs-common/src/stages/generate/index.ts b/packages/techdocs-common/src/stages/generate/index.ts index 382712ccbc..eb015a5c2d 100644 --- a/packages/techdocs-common/src/stages/generate/index.ts +++ b/packages/techdocs-common/src/stages/generate/index.ts @@ -15,4 +15,8 @@ */ export { TechdocsGenerator } from './techdocs'; export { Generators } from './generators'; -export type { GeneratorBuilder, GeneratorBase } from './types'; +export type { + GeneratorBuilder, + GeneratorBase, + GeneratorRunOptions, +} from './types'; diff --git a/packages/techdocs-common/src/stages/generate/techdocs.test.ts b/packages/techdocs-common/src/stages/generate/techdocs.test.ts new file mode 100644 index 0000000000..f3a91657e1 --- /dev/null +++ b/packages/techdocs-common/src/stages/generate/techdocs.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ConfigReader } from '@backstage/config'; +import { readGeneratorConfig } from './techdocs'; + +const mockLogger = { + warn: jest.fn(), +}; + +describe('readGeneratorConfig', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + const logger = mockLogger as any; + + it('defaults to runIn docker', () => { + const config = new ConfigReader({ + techdocs: { + generator: {}, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'docker', + dockerImage: undefined, + pullImage: undefined, + }); + }); + + it('should read local config', () => { + const config = new ConfigReader({ + techdocs: { + generator: { + runIn: 'local', + }, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'local', + }); + }); + + it('should read docker config', () => { + const config = new ConfigReader({ + techdocs: { + generator: { + runIn: 'docker', + }, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'docker', + }); + }); + + it('should read custom docker image', () => { + const config = new ConfigReader({ + techdocs: { + generator: { + runIn: 'docker', + dockerImage: 'my-org/techdocs', + }, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'docker', + dockerImage: 'my-org/techdocs', + }); + }); + + it('should read config disabling docker pull', () => { + const config = new ConfigReader({ + techdocs: { + generator: { + runIn: 'docker', + dockerImage: 'my-org/techdocs', + pullImage: false, + }, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'docker', + dockerImage: 'my-org/techdocs', + pullImage: false, + }); + }); + + describe('with legacy techdocs.generators.techdocs config', () => { + it('should read legacy docker option', () => { + const config = new ConfigReader({ + techdocs: { + generators: { + techdocs: 'docker', + }, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'docker', + }); + }); + + it('legacy option should log warning', () => { + const config = new ConfigReader({ + techdocs: { + generators: { + techdocs: 'local', + }, + }, + }); + + expect(readGeneratorConfig(config, logger)).toEqual({ + runIn: 'local', + }); + expect(logger.warn).toHaveBeenCalledWith( + `The 'techdocs.generators.techdocs' configuration key is deprecated and will be removed in the future. Please use 'techdocs.generator' instead. ` + + `See here https://backstage.io/docs/features/techdocs/configuration`, + ); + }); + }); +}); diff --git a/packages/techdocs-common/src/stages/generate/techdocs.ts b/packages/techdocs-common/src/stages/generate/techdocs.ts index 041dff1d2f..9d6bf38c54 100644 --- a/packages/techdocs-common/src/stages/generate/techdocs.ts +++ b/packages/techdocs-common/src/stages/generate/techdocs.ts @@ -17,7 +17,6 @@ import { ContainerRunner } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import path from 'path'; -import { PassThrough } from 'stream'; import { Logger } from 'winston'; import { addBuildTimestampMetadata, @@ -26,31 +25,29 @@ import { storeEtagMetadata, validateMkdocsYaml, } from './helpers'; -import { GeneratorBase, GeneratorRunOptions } from './types'; +import { + GeneratorBase, + GeneratorConfig, + GeneratorRunInType, + GeneratorRunOptions, +} from './types'; -type TechdocsGeneratorOptions = { - // This option enables users to configure if they want to use TechDocs container - // or generate without the container. - // This is used to avoid running into Docker in Docker environment. - runGeneratorIn: string; -}; - -const createStream = (): [string[], PassThrough] => { - const log = [] as Array; - - const stream = new PassThrough(); - stream.on('data', chunk => { - const textValue = chunk.toString().trim(); - if (textValue?.length > 1) log.push(textValue); - }); - - return [log, stream]; -}; +const defaultDockerImage = 'spotify/techdocs'; export class TechdocsGenerator implements GeneratorBase { private readonly logger: Logger; private readonly containerRunner: ContainerRunner; - private readonly options: TechdocsGeneratorOptions; + private readonly options: GeneratorConfig; + + static async fromConfig( + config: Config, + { + containerRunner, + logger, + }: { containerRunner: ContainerRunner; logger: Logger }, + ) { + return new TechdocsGenerator({ logger, containerRunner, config }); + } constructor({ logger, @@ -62,10 +59,7 @@ export class TechdocsGenerator implements GeneratorBase { config: Config; }) { this.logger = logger; - this.options = { - runGeneratorIn: - config.getOptionalString('techdocs.generators.techdocs') ?? 'docker', - }; + this.options = readGeneratorConfig(config, logger); this.containerRunner = containerRunner; } @@ -74,9 +68,9 @@ export class TechdocsGenerator implements GeneratorBase { outputDir, parsedLocationAnnotation, etag, + logger: childLogger, + logStream, }: GeneratorRunOptions): Promise { - const [log, logStream] = createStream(); - // TODO: In future mkdocs.yml can be mkdocs.yaml. So, use a config variable here to find out // the correct file name. // Do some updates to mkdocs.yml before generating docs e.g. adding repo_url @@ -84,7 +78,7 @@ export class TechdocsGenerator implements GeneratorBase { if (parsedLocationAnnotation) { await patchMkdocsYmlPreBuild( mkdocsYmlPath, - this.logger, + childLogger, parsedLocationAnnotation, ); } @@ -98,7 +92,7 @@ export class TechdocsGenerator implements GeneratorBase { }; try { - switch (this.options.runGeneratorIn) { + switch (this.options.runIn) { case 'local': await runCommand({ command: 'mkdocs', @@ -108,13 +102,13 @@ export class TechdocsGenerator implements GeneratorBase { }, logStream, }); - this.logger.info( + childLogger.info( `Successfully generated docs from ${inputDir} into ${outputDir} using local mkdocs`, ); break; case 'docker': await this.containerRunner.runContainer({ - imageName: 'spotify/techdocs', + imageName: this.options.dockerImage ?? defaultDockerImage, args: ['build', '-d', '/output'], logStream, mountDirs, @@ -122,21 +116,21 @@ export class TechdocsGenerator implements GeneratorBase { // Set the home directory inside the container as something that applications can // write to, otherwise they will just fail trying to write to / envVars: { HOME: '/tmp' }, + pullImage: this.options.pullImage, }); - this.logger.info( + childLogger.info( `Successfully generated docs from ${inputDir} into ${outputDir} using techdocs-container`, ); break; default: throw new Error( - `Invalid config value "${this.options.runGeneratorIn}" provided in 'techdocs.generators.techdocs'.`, + `Invalid config value "${this.options.runIn}" provided in 'techdocs.generators.techdocs'.`, ); } } catch (error) { this.logger.debug( `Failed to generate docs from ${inputDir} into ${outputDir}`, ); - this.logger.error(`Build failed with error: ${log}`); throw new Error( `Failed to generate docs from ${inputDir} into ${outputDir} with error ${error.message}`, ); @@ -150,7 +144,7 @@ export class TechdocsGenerator implements GeneratorBase { // Creates techdocs_metadata.json if file does not exist. await addBuildTimestampMetadata( path.join(outputDir, 'techdocs_metadata.json'), - this.logger, + childLogger, ); // Add etag of the prepared tree to techdocs_metadata.json @@ -163,3 +157,28 @@ export class TechdocsGenerator implements GeneratorBase { } } } + +export function readGeneratorConfig( + config: Config, + logger: Logger, +): GeneratorConfig { + const legacyGeneratorType = config.getOptionalString( + 'techdocs.generators.techdocs', + ) as GeneratorRunInType; + + if (legacyGeneratorType) { + logger.warn( + `The 'techdocs.generators.techdocs' configuration key is deprecated and will be removed in the future. Please use 'techdocs.generator' instead. ` + + `See here https://backstage.io/docs/features/techdocs/configuration`, + ); + } + + return { + runIn: + legacyGeneratorType ?? + config.getOptionalString('techdocs.generator.runIn') ?? + 'docker', + dockerImage: config.getOptionalString('techdocs.generator.dockerImage'), + pullImage: config.getOptionalBoolean('techdocs.generator.pullImage'), + }; +} diff --git a/packages/techdocs-common/src/stages/generate/types.ts b/packages/techdocs-common/src/stages/generate/types.ts index 5ea9721c54..3559716be0 100644 --- a/packages/techdocs-common/src/stages/generate/types.ts +++ b/packages/techdocs-common/src/stages/generate/types.ts @@ -13,10 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Entity } from '@backstage/catalog-model'; import { Writable } from 'stream'; +import { Logger } from 'winston'; import { ParsedLocationAnnotation } from '../../helpers'; +// Determines where the generator will be run +export type GeneratorRunInType = 'docker' | 'local'; + +/** + * The techdocs generator configurations options. + */ +export type GeneratorConfig = { + runIn: GeneratorRunInType; + dockerImage?: string; + pullImage?: boolean; +}; + /** * The values that the generator will receive. * @@ -24,13 +38,15 @@ import { ParsedLocationAnnotation } from '../../helpers'; * @param {string} outputDir Directory to store generated docs in. Usually - a newly created temporary directory. * @param {ParsedLocationAnnotation} parsedLocationAnnotation backstage.io/techdocs-ref annotation of an entity * @param {string} etag A unique identifier for the prepared tree e.g. commit SHA. If provided it will be stored in techdocs_metadata.json. - * @param {Writable} [logStream] A dedicated log stream + * @param {Logger} [logger] A logger that forwards the messages to the caller to be displayed outside of the backend. + * @param {Writable} [logStream] A log stream that can send raw log messages to the caller to be displayed outside of the backend.. */ export type GeneratorRunOptions = { inputDir: string; outputDir: string; parsedLocationAnnotation?: ParsedLocationAnnotation; etag?: string; + logger: Logger; logStream?: Writable; }; diff --git a/packages/techdocs-common/src/stages/prepare/dir.test.ts b/packages/techdocs-common/src/stages/prepare/dir.test.ts index 6992a35c65..019bf51fe9 100644 --- a/packages/techdocs-common/src/stages/prepare/dir.test.ts +++ b/packages/techdocs-common/src/stages/prepare/dir.test.ts @@ -15,7 +15,6 @@ */ import { getVoidLogger, UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; -import { checkoutGitRepository } from '../../helpers'; import { DirectoryPreparer } from './dir'; function normalizePath(path: string) { @@ -27,8 +26,6 @@ function normalizePath(path: string) { jest.mock('../../helpers', () => ({ ...jest.requireActual<{}>('../../helpers'), - checkoutGitRepository: jest.fn(() => '/tmp/backstage-repo/org/name/branch/'), - getLastCommitTimestamp: jest.fn(() => 12345678), })); const logger = getVoidLogger(); @@ -71,7 +68,7 @@ describe('directory preparer', () => { expect(normalizePath(preparedDir)).toEqual('/directory/our-documentation'); }); - it('should merge managed-by-location and techdocs-ref when techdocs-ref is absolute', async () => { + it('should reject when techdocs-ref is absolute', async () => { const directoryPreparer = new DirectoryPreparer( mockConfig, logger, @@ -84,11 +81,12 @@ describe('directory preparer', () => { 'backstage.io/techdocs-ref': 'dir:/our-documentation/techdocs', }); - const { preparedDir } = await directoryPreparer.prepare(mockEntity); - expect(normalizePath(preparedDir)).toEqual('/our-documentation/techdocs'); + await expect(directoryPreparer.prepare(mockEntity)).rejects.toThrow( + /Relative path is not allowed to refer to a directory outside its parent/, + ); }); - it('should merge managed-by-location and techdocs-ref when managed-by-location is a git repository', async () => { + it('should reject when managed-by-location is a git repository', async () => { const directoryPreparer = new DirectoryPreparer( mockConfig, logger, @@ -101,10 +99,8 @@ describe('directory preparer', () => { 'backstage.io/techdocs-ref': 'dir:./docs', }); - const { preparedDir } = await directoryPreparer.prepare(mockEntity); - expect(normalizePath(preparedDir)).toEqual( - '/tmp/backstage-repo/org/name/branch/docs', + await expect(directoryPreparer.prepare(mockEntity)).rejects.toThrow( + /Unable to resolve location type github/, ); - expect(checkoutGitRepository).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/techdocs-common/src/stages/prepare/dir.ts b/packages/techdocs-common/src/stages/prepare/dir.ts index 9ec4b4d37a..ade0b00162 100644 --- a/packages/techdocs-common/src/stages/prepare/dir.ts +++ b/packages/techdocs-common/src/stages/prepare/dir.ts @@ -15,104 +15,68 @@ */ import { UrlReader } from '@backstage/backend-common'; -import { InputError, NotModifiedError } from '@backstage/errors'; import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; -import parseGitUrl from 'git-url-parse'; -import path from 'path'; -import { Logger } from 'winston'; +import { InputError } from '@backstage/errors'; import { - checkoutGitRepository, - getLastCommitTimestamp, - parseReferenceAnnotation, -} from '../../helpers'; + ScmIntegrationRegistry, + ScmIntegrations, +} from '@backstage/integration'; +import { Logger } from 'winston'; +import { parseReferenceAnnotation, transformDirLocation } from '../../helpers'; import { PreparerBase, PreparerResponse } from './types'; export class DirectoryPreparer implements PreparerBase { - constructor( - private readonly config: Config, - private readonly logger: Logger, - private readonly reader: UrlReader, - ) { - this.config = config; - this.logger = logger; + private readonly scmIntegrations: ScmIntegrationRegistry; + private readonly reader: UrlReader; + + constructor(config: Config, _logger: Logger, reader: UrlReader) { this.reader = reader; + this.scmIntegrations = ScmIntegrations.fromConfig(config); } - private async resolveManagedByLocationToDir( + async prepare( entity: Entity, - options?: { etag?: string }, + options?: { logger?: Logger; etag?: string }, ): Promise { - const { type, target } = parseReferenceAnnotation( - 'backstage.io/managed-by-location', + const annotation = parseReferenceAnnotation( + 'backstage.io/techdocs-ref', entity, ); - - this.logger.debug( - `Building docs for entity with type 'dir' and managed-by-location '${type}'`, + const { type, target } = transformDirLocation( + entity, + annotation, + this.scmIntegrations, ); + switch (type) { case 'url': { + options?.logger?.debug(`Reading files from ${target}`); + // the target is an absolute url since it has already been transformed const response = await this.reader.readTree(target, { etag: options?.etag, }); const preparedDir = await response.dir(); + + options?.logger?.debug(`Tree downloaded and stored at ${preparedDir}`); + return { preparedDir, etag: response.etag, }; } - case 'github': - case 'gitlab': - case 'azure/api': { - const parsedGitLocation = parseGitUrl(target); - const repoLocation = await checkoutGitRepository( - target, - this.config, - this.logger, - ); - // Check if etag has changed for cache invalidation. - const etag = await getLastCommitTimestamp(repoLocation, this.logger); - if (options?.etag === etag.toString()) { - throw new NotModifiedError(); - } + case 'dir': { return { - preparedDir: path.dirname( - path.join(repoLocation, parsedGitLocation.filepath), - ), - etag: etag.toString(), - }; - } - case 'file': - return { - preparedDir: path.dirname(target), + // the transformation already validated that the target is in a safe location + preparedDir: target, // Instead of supporting caching on local sources, use techdocs-cli for local development and debugging. etag: '', }; + } + default: throw new InputError(`Unable to resolve location type ${type}`); } } - - async prepare(entity: Entity): Promise { - this.logger.warn( - 'You are using the legacy dir preparer in TechDocs which will be removed in near future (March 2021). ' + - 'Migrate to URL reader by updating `backstage.io/techdocs-ref` annotation in `catalog-info.yaml` ' + - 'to be prefixed with `url:`. Read the migration guide and benefits at https://github.com/backstage/backstage/issues/4409 ', - ); - - const { target } = parseReferenceAnnotation( - 'backstage.io/techdocs-ref', - entity, - ); - - // This will throw NotModified error if etag has not changed. - const response = await this.resolveManagedByLocationToDir(entity); - - return { - preparedDir: path.resolve(response.preparedDir, target), - etag: response.etag, - }; - } } diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts index 15fe5a467f..2942e33380 100644 --- a/packages/techdocs-common/src/stages/publish/awsS3.ts +++ b/packages/techdocs-common/src/stages/publish/awsS3.ts @@ -16,7 +16,7 @@ import { Entity, EntityName } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import aws, { Credentials } from 'aws-sdk'; -import { ManagedUpload } from 'aws-sdk/clients/s3'; +import { ListObjectsV2Output, ManagedUpload } from 'aws-sdk/clients/s3'; import { CredentialsOptions } from 'aws-sdk/lib/credentials'; import express from 'express'; import fs from 'fs-extra'; @@ -25,7 +25,11 @@ import createLimiter from 'p-limit'; import path from 'path'; import { Readable } from 'stream'; import { Logger } from 'winston'; -import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers'; +import { + getFileTreeRecursively, + getHeadersForFileExtension, + lowerCaseEntityTripletInStoragePath, +} from './helpers'; import { PublisherBase, PublishRequest, @@ -308,4 +312,78 @@ export class AwsS3Publish implements PublisherBase { return Promise.resolve(false); } } + + async migrateDocsCase({ + removeOriginal = false, + concurrency = 25, + }): Promise { + // Iterate through every file in the root of the publisher. + const allObjects = await this.getAllObjectsFromBucket(); + const limiter = createLimiter(concurrency); + await Promise.all( + allObjects.map(f => + limiter(async file => { + let newPath; + try { + newPath = lowerCaseEntityTripletInStoragePath(file); + } catch (e) { + this.logger.warn(e.message); + return; + } + + // If all parts are already lowercase, ignore. + if (file === newPath) { + return; + } + + try { + this.logger.debug(`Migrating ${file}`); + await this.storageClient + .copyObject({ + Bucket: this.bucketName, + CopySource: [this.bucketName, file].join('/'), + Key: newPath, + }) + .promise(); + + if (removeOriginal) { + await this.storageClient + .deleteObject({ + Bucket: this.bucketName, + Key: file, + }) + .promise(); + } + } catch (e) { + this.logger.warn(`Unable to migrate ${file}: ${e.message}`); + } + }, f), + ), + ); + } + + /** + * Returns a list of all object keys from the configured bucket. + */ + protected async getAllObjectsFromBucket(): Promise { + const objects: string[] = []; + let nextContinuation: string | undefined; + let allObjects: ListObjectsV2Output; + + // Iterate through every file in the root of the publisher. + do { + allObjects = await this.storageClient + .listObjectsV2({ + Bucket: this.bucketName, + ContinuationToken: nextContinuation, + }) + .promise(); + objects.push( + ...(allObjects.Contents || []).map(f => f.Key || '').filter(f => !!f), + ); + nextContinuation = allObjects.NextContinuationToken; + } while (nextContinuation); + + return objects; + } } diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts index 3ed75f532e..7ee67a0016 100644 --- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts +++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts @@ -25,7 +25,11 @@ import JSON5 from 'json5'; import limiterFactory from 'p-limit'; import { default as path, default as platformPath } from 'path'; import { Logger } from 'winston'; -import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers'; +import { + getFileTreeRecursively, + getHeadersForFileExtension, + lowerCaseEntityTripletInStoragePath, +} from './helpers'; import { PublisherBase, PublishRequest, @@ -289,4 +293,61 @@ export class AzureBlobStoragePublish implements PublisherBase { .getBlockBlobClient(`${entityRootDir}/index.html`) .exists(); } + + protected async renameBlob( + originalName: string, + newName: string, + removeOriginal = false, + ): Promise { + const container = this.storageClient.getContainerClient(this.containerName); + const blob = container.getBlobClient(newName); + const { url } = container.getBlobClient(originalName); + const response = await blob.beginCopyFromURL(url); + await response.pollUntilDone(); + if (removeOriginal) { + await container.deleteBlob(originalName); + } + } + + protected async renameBlobToLowerCase( + originalPath: string, + removeOriginal: boolean, + ) { + let newPath; + try { + newPath = lowerCaseEntityTripletInStoragePath(originalPath); + } catch (e) { + this.logger.warn(e.message); + return; + } + + if (originalPath === newPath) return; + try { + this.logger.debug(`Migrating ${originalPath}`); + await this.renameBlob(originalPath, newPath, removeOriginal); + } catch (e) { + this.logger.warn(`Unable to migrate ${originalPath}: ${e.message}`); + } + } + + async migrateDocsCase({ + removeOriginal = false, + concurrency = 25, + }): Promise { + const promises = []; + const limiter = limiterFactory(concurrency); + const container = this.storageClient.getContainerClient(this.containerName); + + for await (const blob of container.listBlobsFlat()) { + promises.push( + limiter( + this.renameBlobToLowerCase.bind(this), + blob.name, + removeOriginal, + ), + ); + } + + await Promise.all(promises); + } } diff --git a/packages/techdocs-common/src/stages/publish/googleStorage.ts b/packages/techdocs-common/src/stages/publish/googleStorage.ts index bca7b86856..9def24722a 100644 --- a/packages/techdocs-common/src/stages/publish/googleStorage.ts +++ b/packages/techdocs-common/src/stages/publish/googleStorage.ts @@ -24,8 +24,10 @@ import express from 'express'; import JSON5 from 'json5'; import createLimiter from 'p-limit'; import path from 'path'; +import { Readable } from 'stream'; import { Logger } from 'winston'; import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers'; +import { MigrateWriteStream } from './migrations'; import { PublisherBase, PublishRequest, @@ -235,4 +237,23 @@ export class GoogleGCSPublish implements PublisherBase { }); }); } + + migrateDocsCase({ removeOriginal = false, concurrency = 25 }): Promise { + return new Promise((resolve, reject) => { + // Iterate through every file in the root of the publisher. + const allFileMetadata: Readable = this.storageClient + .bucket(this.bucketName) + .getFilesStream(); + const migrateFiles = new MigrateWriteStream( + this.logger, + removeOriginal, + concurrency, + ); + migrateFiles.on('finish', resolve).on('error', reject); + allFileMetadata.pipe(migrateFiles).on('error', error => { + migrateFiles.destroy(); + reject(error); + }); + }); + } } diff --git a/packages/techdocs-common/src/stages/publish/helpers.test.ts b/packages/techdocs-common/src/stages/publish/helpers.test.ts index cfb9c7930d..27a56cd06d 100644 --- a/packages/techdocs-common/src/stages/publish/helpers.test.ts +++ b/packages/techdocs-common/src/stages/publish/helpers.test.ts @@ -16,7 +16,11 @@ import mockFs from 'mock-fs'; import * as os from 'os'; import * as path from 'path'; -import { getFileTreeRecursively, getHeadersForFileExtension } from './helpers'; +import { + getFileTreeRecursively, + getHeadersForFileExtension, + lowerCaseEntityTripletInStoragePath, +} from './helpers'; describe('getHeadersForFileExtension', () => { const correctMapOfExtensions = [ @@ -73,3 +77,25 @@ describe('getFileTreeRecursively', () => { expect(fileList).toContain(path.resolve(root, 'subDirA/file2')); }); }); + +describe('lowerCaseEntityTripletInStoragePath', () => { + it('returns lower-cased entity triplet path', () => { + const originalPath = 'default/Component/backstage/index.html'; + const actualPath = lowerCaseEntityTripletInStoragePath(originalPath); + expect(actualPath).toBe('default/component/backstage/index.html'); + }); + + it('does not lowercase beyond the triplet', () => { + const originalPath = 'default/Component/backstage/assets/IMAGE.png'; + const actualPath = lowerCaseEntityTripletInStoragePath(originalPath); + expect(actualPath).toBe('default/component/backstage/assets/IMAGE.png'); + }); + + it('throws error when there is no triplet', () => { + const originalPath = '/default/component/IMAGE.png'; + const error = `Encountered file unmanaged by TechDocs ${originalPath}. Skipping.`; + expect(() => + lowerCaseEntityTripletInStoragePath(originalPath), + ).toThrowError(error); + }); +}); diff --git a/packages/techdocs-common/src/stages/publish/helpers.ts b/packages/techdocs-common/src/stages/publish/helpers.ts index eac5ba05c8..ed681eb8ba 100644 --- a/packages/techdocs-common/src/stages/publish/helpers.ts +++ b/packages/techdocs-common/src/stages/publish/helpers.ts @@ -82,3 +82,32 @@ export const getFileTreeRecursively = async ( }); return fileList; }; + +/** + * Returns the version of an object's storage path where the first three parts + * of the path (the entity triplet of namespace, kind, and name) are + * lower-cased. + * + * Path must not include a starting slash. + * + * @example + * lowerCaseEntityTripletInStoragePath('default/Component/backstage') + * // return default/component/backstage + */ +export const lowerCaseEntityTripletInStoragePath = ( + originalPath: string, +): string => { + const trimmedPath = + originalPath[0] === '/' ? originalPath.substring(1) : originalPath; + const matches = trimmedPath.match(/\//g) || []; + if (matches.length <= 2) { + throw new Error( + `Encountered file unmanaged by TechDocs ${originalPath}. Skipping.`, + ); + } + const [namespace, kind, name, ...parts] = originalPath.split('/'); + const lowerNamespace = namespace.toLowerCase(); + const lowerKind = kind.toLowerCase(); + const lowerName = name.toLowerCase(); + return [lowerNamespace, lowerKind, lowerName, ...parts].join('/'); +}; diff --git a/packages/techdocs-common/src/stages/publish/local.ts b/packages/techdocs-common/src/stages/publish/local.ts index 3ad0ed65ad..3414d273dd 100644 --- a/packages/techdocs-common/src/stages/publish/local.ts +++ b/packages/techdocs-common/src/stages/publish/local.ts @@ -22,6 +22,7 @@ import { Config } from '@backstage/config'; import express from 'express'; import fs from 'fs-extra'; import os from 'os'; +import createLimiter from 'p-limit'; import path from 'path'; import { Logger } from 'winston'; import { @@ -31,7 +32,11 @@ import { ReadinessResponse, TechDocsMetadata, } from './types'; -import { getHeadersForFileExtension } from './helpers'; +import { + getFileTreeRecursively, + getHeadersForFileExtension, + lowerCaseEntityTripletInStoragePath, +} from './helpers'; // TODO: Use a more persistent storage than node_modules or /tmp directory. // Make it configurable with techdocs.publisher.local.publishDirectory @@ -164,4 +169,45 @@ export class LocalPublish implements PublisherBase { return false; } } + + /** + * This code will never run in practice. It is merely here to illustrate how + * to implement this method for other storage providers. + */ + async migrateDocsCase({ + removeOriginal = false, + concurrency = 25, + }): Promise { + // Iterate through every file in the root of the publisher. + const files = await getFileTreeRecursively(staticDocsDir); + const limit = createLimiter(concurrency); + + await Promise.all( + files.map(f => + limit(async file => { + const relativeFile = file.replace(`${staticDocsDir}${path.sep}`, ''); + const newFile = lowerCaseEntityTripletInStoragePath(relativeFile); + + // If all parts are already lowercase, ignore. + if (relativeFile === newFile) { + return; + } + + // Otherwise, copy or move the file. + await new Promise(resolve => { + const migrate = removeOriginal ? fs.move : fs.copyFile; + this.logger.debug(`Migrating ${relativeFile}`); + migrate(file, newFile, err => { + if (err) { + this.logger.warn( + `Unable to migrate ${relativeFile}: ${err.message}`, + ); + } + resolve(); + }); + }); + }, f), + ), + ); + } } diff --git a/packages/techdocs-common/src/stages/publish/migrations/GoogleMigration.ts b/packages/techdocs-common/src/stages/publish/migrations/GoogleMigration.ts new file mode 100644 index 0000000000..cc35b351ea --- /dev/null +++ b/packages/techdocs-common/src/stages/publish/migrations/GoogleMigration.ts @@ -0,0 +1,79 @@ +/* + * 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. + */ + +import { File } from '@google-cloud/storage'; +import { Writable } from 'stream'; +import { Logger } from 'winston'; +import { lowerCaseEntityTripletInStoragePath } from '../helpers'; + +/** + * Writable stream to handle object copy/move operations. This implementation + * ensures we don't read in files from GCS faster than GCS can copy/move them. + */ +export class MigrateWriteStream extends Writable { + protected logger: Logger; + protected removeOriginal: boolean; + protected maxConcurrency: number; + protected inFlight = 0; + + constructor(logger: Logger, removeOriginal: boolean, concurrency: number) { + super({ objectMode: true }); + this.logger = logger; + this.removeOriginal = removeOriginal; + this.maxConcurrency = concurrency; + } + + _write(file: File, _encoding: BufferEncoding, next: Function) { + let shouldCallNext = true; + let newFile; + try { + newFile = lowerCaseEntityTripletInStoragePath(file.name); + } catch (e) { + this.logger.warn(e.message); + next(); + return; + } + + // If all parts are already lowercase, ignore. + if (newFile === file.name) { + next(); + return; + } + + // Allow up to n-many files to be migrated at a time. + this.inFlight++; + if (this.inFlight < this.maxConcurrency) { + next(); + shouldCallNext = false; + } + + // Otherwise, copy or move the file. + const migrate = this.removeOriginal + ? file.move.bind(file) + : file.copy.bind(file); + this.logger.debug(`Migrating ${file.name}`); + migrate(newFile) + .catch(e => + this.logger.warn(`Unable to migrate ${file.name}: ${e.message}`), + ) + .finally(() => { + this.inFlight--; + if (shouldCallNext) { + next(); + } + }); + } +} diff --git a/packages/techdocs-common/src/stages/publish/migrations/index.ts b/packages/techdocs-common/src/stages/publish/migrations/index.ts new file mode 100644 index 0000000000..f82ab054a4 --- /dev/null +++ b/packages/techdocs-common/src/stages/publish/migrations/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { MigrateWriteStream } from './GoogleMigration'; diff --git a/packages/techdocs-common/src/stages/publish/types.ts b/packages/techdocs-common/src/stages/publish/types.ts index a7f6ff7a45..229c853427 100644 --- a/packages/techdocs-common/src/stages/publish/types.ts +++ b/packages/techdocs-common/src/stages/publish/types.ts @@ -55,6 +55,19 @@ export type TechDocsMetadata = { etag: string; }; +export type MigrateRequest = { + /** + * Whether or not to remove the source file. Defaults to false (acting like a + * copy instead of a move). + */ + removeOriginal?: boolean; + + /** + * Maximum number of files/objects to migrate at once. Defaults to 25. + */ + concurrency?: number; +}; + /** * Base class for a TechDocs publisher (e.g. Local, Google GCS Bucket, AWS S3, etc.) * The publisher handles publishing of the generated static files after the prepare and generate steps of TechDocs. @@ -92,4 +105,14 @@ export interface PublisherBase { * Check if the index.html is present for the Entity at the Storage location. */ hasDocsBeenGenerated(entityName: Entity): Promise; + + /** + * Migrates documentation objects with case sensitive entity triplets to + * lowercase entity triplets. This was (will be) a change introduced in + * techdocs-cli v{0.x.y} and techdocs-backend v{0.x.y}. + * + * Implementation of this method is unnecessary in publishers introduced + * after v{0.x.y} of techdocs-common. + */ + migrateDocsCase?(migrateRequest: MigrateRequest): Promise; } diff --git a/packages/test-utils-core/api-report.md b/packages/test-utils-core/api-report.md index 8eb7ee458a..7cef987174 100644 --- a/packages/test-utils-core/api-report.md +++ b/packages/test-utils-core/api-report.md @@ -6,14 +6,20 @@ import { ReactElement } from 'react'; import { RenderResult } from '@testing-library/react'; +// Warning: (ae-missing-release-tag) "AsyncLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AsyncLogCollector = () => Promise; +// Warning: (ae-missing-release-tag) "CollectedLogs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CollectedLogs = { [key in T]: string[]; }; +// Warning: (ae-missing-release-tag) "Keyboard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class Keyboard { constructor( @@ -60,18 +66,31 @@ export class Keyboard { static typeDebug(target: any, input: any): Promise; } +// Warning: (ae-missing-release-tag) "LogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LogCollector = AsyncLogCollector | SyncLogCollector; +// Warning: (ae-missing-release-tag) "LogFuncs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LogFuncs = 'log' | 'warn' | 'error'; +// Warning: (ae-missing-release-tag) "renderWithEffects" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function renderWithEffects(nodes: ReactElement): Promise; +// Warning: (ae-missing-release-tag) "SyncLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SyncLogCollector = () => void; +// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "withLogCollector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function withLogCollector( callback: AsyncLogCollector, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 68e45cd831..12627e55a0 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/test-utils +## 0.1.15 + +### Patch Changes + +- 45b5fc3a8: Updated the layout of catalog and API index pages to handle smaller screen sizes. This adds responsive wrappers to the entity tables, and switches filters to a drawer when width-constrained. If you have created a custom catalog or API index page, you will need to update the page structure to match the updated [catalog customization](https://backstage.io/docs/features/software-catalog/catalog-customization) documentation. +- Updated dependencies + - @backstage/core-app-api@0.1.5 + ## 0.1.14 ### Patch Changes diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 57020e6221..187867d24f 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -15,22 +15,29 @@ import { RouteRef } from '@backstage/core-plugin-api'; import { StorageApi } from '@backstage/core-plugin-api'; import { StorageValueChange } from '@backstage/core-plugin-api'; -// @public (undocumented) -export function mockBreakpoint( - initialBreakpoint?: Breakpoint, -): { - set(breakpoint: Breakpoint): void; - remove(): void; -}; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "mockBreakpoint" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function mockBreakpoint({ + matches, +}: { + matches?: boolean | undefined; +}): void; +// Warning: (ae-missing-release-tag) "MockErrorApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class MockErrorApi implements ErrorApi { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options?: Options); // (undocumented) error$(): Observable<{ error: Error; context?: ErrorContext; }>; + // Warning: (ae-forgotten-export) The symbol "ErrorWithContext" needs to be exported by the entry point index.d.ts + // // (undocumented) getErrors(): ErrorWithContext[]; // (undocumented) @@ -39,6 +46,8 @@ export class MockErrorApi implements ErrorApi { waitForError(pattern: RegExp, timeoutMs?: number): Promise; } +// Warning: (ae-missing-release-tag) "MockStorageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class MockStorageApi implements StorageApi { // (undocumented) @@ -55,11 +64,15 @@ export class MockStorageApi implements StorageApi { set(key: string, data: T): Promise; } +// Warning: (ae-missing-release-tag) "MockStorageBucket" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type MockStorageBucket = { [key: string]: any; }; +// Warning: (ae-missing-release-tag) "msw" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const msw: { setupDefaultHandlers: (worker: { @@ -69,12 +82,17 @@ export const msw: { }) => void; }; +// Warning: (ae-forgotten-export) The symbol "TestAppOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "renderInTestApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function renderInTestApp( Component: ComponentType | ReactNode, options?: TestAppOptions, ): Promise; +// Warning: (ae-missing-release-tag) "wrapInTestApp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function wrapInTestApp( Component: ComponentType | ReactNode, diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 797a23b17f..cfe4f2198e 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.14", + "version": "0.1.15", "private": false, "publishConfig": { "access": "public", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-app-api": "^0.1.3", + "@backstage/core-app-api": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/test-utils-core": "^0.1.1", "@backstage/theme": "^0.2.8", @@ -46,7 +46,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.2", + "@backstage/cli": "^0.7.5", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/packages/test-utils/src/testUtils/mockBreakpoint.ts b/packages/test-utils/src/testUtils/mockBreakpoint.ts index 37f0e761b6..3335284f88 100644 --- a/packages/test-utils/src/testUtils/mockBreakpoint.ts +++ b/packages/test-utils/src/testUtils/mockBreakpoint.ts @@ -14,80 +14,30 @@ * limitations under the License. */ -import { act } from '@testing-library/react'; - -type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; - -const queryToBreakpoint = { - '(min-width:1920px)': 'xl', - '(min-width:1280px)': 'lg', - '(min-width:960px)': 'md', - '(min-width:600px)': 'sm', - '(min-width:0px)': 'xs', -} as Record; - -function toBreakpoint(query: string) { - const breakpoint = queryToBreakpoint[query]; - if (!breakpoint) { - throw new Error( - `received unknown media query in breakpoint mock: '${query}'`, - ); - } - return breakpoint; -} - -type Listener = (event: { matches: boolean }) => void; - -interface QueryList { - addListener(listener: Listener): void; - removeListener(listener: Listener): void; - matches: boolean; -} - -interface Query { - query: string; - queryList: QueryList; - listeners: Set; -} - -export default function mockBreakpoint(initialBreakpoint: Breakpoint = 'xl') { - let currentBreakpoint = initialBreakpoint; - const queries = Array(); - - const previousMatchMedia: any = (window as any).matchMedia; - - (window as any).matchMedia = (query: string): QueryList => { - const listeners = new Set(); - - const queryList: QueryList = { - addListener(listener) { - listeners.add(listener); - }, - removeListener(listener) { - listeners.delete(listener); - }, - matches: toBreakpoint(query) === currentBreakpoint, - }; - - queries.push({ query, queryList, listeners }); - - return queryList; - }; - - return { - set(breakpoint: Breakpoint) { - currentBreakpoint = breakpoint; - - act(() => { - queries.forEach(({ query, queryList, listeners }) => { - const matches = toBreakpoint(query) === breakpoint; - queryList.matches = matches; - listeners.forEach(listener => listener({ matches })); - }); - }); - }, - remove() { - (window as any).matchMedia = previousMatchMedia; - }, - }; +/** + * This is a mocking method suggested in the Jest Doc's, as it is not implemented in JSDOM yet. + * It can be used to mock values when the MUI `useMediaQuery` hook if it is used in a tested component. + * + * For issues checkout the documentation: + * https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom + * + * If there are any updates from MUI React on testing `useMediaQuery` this mock should be replaced + * https://material-ui.com/components/use-media-query/#testing + * + * @param matchMediaOptions + */ +export default function mockBreakpoint({ matches = false }) { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: matches, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }); } diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index e09f5838c7..a3f6f6dd43 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -9,12 +9,19 @@ import { PaletteOptions } from '@material-ui/core/styles/createPalette'; import { Theme } from '@material-ui/core'; import { ThemeOptions } from '@material-ui/core'; +// Warning: (ae-forgotten-export) The symbol "PaletteAdditions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "BackstagePalette" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstagePalette = Palette & PaletteAdditions; +// Warning: (ae-missing-release-tag) "BackstagePaletteOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstagePaletteOptions = PaletteOptions & PaletteAdditions; +// Warning: (ae-missing-release-tag) "BackstageTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BackstageTheme extends Theme { // (undocumented) @@ -25,6 +32,8 @@ export interface BackstageTheme extends Theme { palette: BackstagePalette; } +// Warning: (ae-missing-release-tag) "BackstageThemeOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BackstageThemeOptions extends ThemeOptions { // (undocumented) @@ -35,29 +44,45 @@ export interface BackstageThemeOptions extends ThemeOptions { palette: BackstagePaletteOptions; } +// Warning: (ae-missing-release-tag) "colorVariants" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const colorVariants: Record; +// Warning: (ae-missing-release-tag) "createTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createTheme(options: SimpleThemeOptions): BackstageTheme; +// Warning: (ae-missing-release-tag) "createThemeOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createThemeOptions( options: SimpleThemeOptions, ): BackstageThemeOptions; +// Warning: (ae-missing-release-tag) "createThemeOverrides" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createThemeOverrides(theme: BackstageTheme): Overrides; +// Warning: (ae-missing-release-tag) "darkTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const darkTheme: BackstageTheme; +// Warning: (ae-missing-release-tag) "genPageTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function genPageTheme(colors: string[], shape: string): PageTheme; +// Warning: (ae-missing-release-tag) "lightTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const lightTheme: BackstageTheme; +// Warning: (ae-missing-release-tag) "PageTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PageTheme = { colors: string[]; @@ -65,17 +90,25 @@ export type PageTheme = { backgroundImage: string; }; +// Warning: (ae-missing-release-tag) "pageTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const pageTheme: Record; +// Warning: (ae-missing-release-tag) "PageThemeSelector" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PageThemeSelector = { themeId: string; }; +// Warning: (ae-missing-release-tag) "shapes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const shapes: Record; +// Warning: (ae-missing-release-tag) "SimpleThemeOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type SimpleThemeOptions = { palette: BackstagePaletteOptions; diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 9135443ac9..dfa9130b4d 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-api-docs +## 0.6.3 + +### Patch Changes + +- 45b5fc3a8: Updated the layout of catalog and API index pages to handle smaller screen sizes. This adds responsive wrappers to the entity tables, and switches filters to a drawer when width-constrained. If you have created a custom catalog or API index page, you will need to update the page structure to match the updated [catalog customization](https://backstage.io/docs/features/software-catalog/catalog-customization) documentation. +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/plugin-catalog@0.6.8 + - @backstage/plugin-catalog-react@0.3.1 + +## 0.6.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@0.6.7 + - @backstage/plugin-catalog-react@0.3.0 + ## 0.6.1 ### Patch Changes diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index 60d8986d9b..d5ed198dc1 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -59,15 +59,17 @@ const apiPage = ( - + - - - - - - + + + + + + + + diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index bb83d4c539..639485a42d 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -16,9 +16,14 @@ import { RouteRef } from '@backstage/core-plugin-api'; import { TableColumn } from '@backstage/core-components'; import { UserListFilterKind } from '@backstage/plugin-catalog-react'; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ApiDefinitionCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ApiDefinitionCard: (_: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "ApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ApiDefinitionWidget = { type: string; @@ -27,9 +32,14 @@ export type ApiDefinitionWidget = { rawLanguage?: string; }; +// Warning: (ae-forgotten-export) The symbol "ApiDocsConfig" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "apiDocsConfigRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const apiDocsConfigRef: ApiRef; +// Warning: (ae-missing-release-tag) "apiDocsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const apiDocsPlugin: BackstagePlugin< { @@ -42,12 +52,19 @@ const apiDocsPlugin: BackstagePlugin< export { apiDocsPlugin }; export { apiDocsPlugin as plugin }; +// Warning: (ae-missing-release-tag) "ApiExplorerPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ApiExplorerPage: ({ initiallySelectedFilter, columns, -}: ApiExplorerPageProps) => JSX.Element; +}: { + initiallySelectedFilter?: UserListFilterKind | undefined; + columns?: TableColumn[] | undefined; +}) => JSX.Element; +// Warning: (ae-missing-release-tag) "ApiTypeTitle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ApiTypeTitle: ({ apiEntity, @@ -55,23 +72,38 @@ export const ApiTypeTitle: ({ apiEntity: ApiEntity; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AsyncApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const AsyncApiDefinitionWidget: ({ definition }: Props_5) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ConsumedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ConsumedApisCard: ({ variant }: Props_2) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ConsumingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ConsumingComponentsCard: ({ variant }: Props_6) => JSX.Element; +// Warning: (ae-missing-release-tag) "defaultDefinitionWidgets" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function defaultDefinitionWidgets(): ApiDefinitionWidget[]; +// Warning: (ae-missing-release-tag) "EntityApiDefinitionCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityApiDefinitionCard: (_: { apiEntity?: ApiEntity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityConsumedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityConsumedApisCard: ({ variant, @@ -80,6 +112,8 @@ export const EntityConsumedApisCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityConsumingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityConsumingComponentsCard: ({ variant, @@ -88,6 +122,8 @@ export const EntityConsumingComponentsCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityHasApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityHasApisCard: ({ variant, @@ -95,6 +131,8 @@ export const EntityHasApisCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityProvidedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityProvidedApisCard: ({ variant, @@ -103,6 +141,8 @@ export const EntityProvidedApisCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityProvidingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityProvidingComponentsCard: ({ variant, @@ -111,21 +151,36 @@ export const EntityProvidingComponentsCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "HasApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const HasApisCard: ({ variant }: Props_3) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "OpenApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PlainApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const PlainApiDefinitionWidget: ({ definition, language, }: Props_9) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ProvidedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ProvidedApisCard: ({ variant }: Props_4) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ProvidingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ProvidingComponentsCard: ({ variant }: Props_7) => JSX.Element; diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 3e667d6528..f02945c14c 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.6.1", + "version": "0.6.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "dependencies": { "@asyncapi/react-component": "^0.23.0", "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog": "^0.6.6", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog": "^0.6.8", + "@backstage/plugin-catalog-react": "^0.3.1", "@backstage/theme": "^0.2.8", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", @@ -51,10 +51,10 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx index 9a8b725ffc..361ce7b55b 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/ApiExplorerPage.tsx @@ -14,6 +14,21 @@ * limitations under the License. */ +import { + Content, + ContentHeader, + PageWithHeader, + SupportButton, + TableColumn, +} from '@backstage/core-components'; +import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { + CatalogTable, + CatalogTableRow, + FilteredEntityLayout, + EntityListContainer, + FilterContainer, +} from '@backstage/plugin-catalog'; import { EntityKindPicker, EntityLifecyclePicker, @@ -24,29 +39,10 @@ import { UserListFilterKind, UserListPicker, } from '@backstage/plugin-catalog-react'; -import { CatalogTable, CatalogTableRow } from '@backstage/plugin-catalog'; -import { Button, makeStyles } from '@material-ui/core'; +import { Button } from '@material-ui/core'; import React from 'react'; import { Link as RouterLink } from 'react-router-dom'; import { createComponentRouteRef } from '../../routes'; -import { ApiExplorerLayout } from './ApiExplorerLayout'; - -import { - Content, - ContentHeader, - SupportButton, - TableColumn, -} from '@backstage/core-components'; -import { useRouteRef } from '@backstage/core-plugin-api'; - -const useStyles = makeStyles(theme => ({ - contentWrapper: { - display: 'grid', - gridTemplateAreas: "'filters' 'table'", - gridTemplateColumns: '250px 1fr', - gridColumnGap: theme.spacing(2), - }, -})); const defaultColumns: TableColumn[] = [ CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), @@ -58,7 +54,7 @@ const defaultColumns: TableColumn[] = [ CatalogTable.columns.createTagsColumn(), ]; -export type ApiExplorerPageProps = { +type ApiExplorerPageProps = { initiallySelectedFilter?: UserListFilterKind; columns?: TableColumn[]; }; @@ -67,11 +63,19 @@ export const ApiExplorerPage = ({ initiallySelectedFilter = 'all', columns, }: ApiExplorerPageProps) => { - const styles = useStyles(); const createComponentLink = useRouteRef(createComponentRouteRef); + const configApi = useApi(configApiRef); + const generatedSubtitle = `${ + configApi.getOptionalString('organization.name') ?? 'Backstage' + } API Explorer`; return ( - + {createComponentLink && ( @@ -86,20 +90,22 @@ export const ApiExplorerPage = ({ )} All your APIs -

- -
+ + +
- -
-
+ + + + + + - + ); }; diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 144d641d2c..34addac443 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-backend +## 0.3.15 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + - @backstage/config-loader@0.6.5 + ## 0.3.14 ### Patch Changes diff --git a/plugins/app-backend/api-report.md b/plugins/app-backend/api-report.md index 7215366ebb..8c674b34e2 100644 --- a/plugins/app-backend/api-report.md +++ b/plugins/app-backend/api-report.md @@ -7,9 +7,13 @@ import { Config } from '@backstage/config'; import express from 'express'; import { Logger as Logger_2 } from 'winston'; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { appPackageName: string; diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index cd5b62dfb4..5101913a26 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.3.14", + "version": "0.3.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.3", - "@backstage/config-loader": "^0.6.4", + "@backstage/backend-common": "^0.8.6", + "@backstage/config-loader": "^0.6.5", "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -40,7 +40,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.1", + "@backstage/cli": "^0.7.4", "@types/supertest": "^2.0.8", "msw": "^0.29.0", "supertest": "^6.1.3" diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index dd96bee3cc..218e20cd0e 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-auth-backend +## 0.3.18 + +### Patch Changes + +- 2567c066d: TokenIssuer is now exported so it may be used by auth providers that are not bundled with Backstage +- Updated dependencies + - @backstage/catalog-client@0.3.17 + - @backstage/backend-common@0.8.7 + - @backstage/test-utils@0.1.15 + +## 0.3.17 + +### Patch Changes + +- 40b3c60e2: Configuration updates for the `OpenID Connect` auth provider to allow `prompt` configuration and some sensible defaults. +- f55f9df10: Add Sign In and Handler resolver for Microsoft provider +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + ## 0.3.16 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 50341c0392..1ada822262 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -14,11 +14,15 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { Profile } from 'passport'; import { UserEntity } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "AuthProviderFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AuthProviderFactory = ( options: AuthProviderFactoryOptions, ) => AuthProviderRouteHandlers; +// Warning: (ae-missing-release-tag) "AuthProviderFactoryOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AuthProviderFactoryOptions = { providerId: string; @@ -31,14 +35,38 @@ export type AuthProviderFactoryOptions = { identityResolver?: ExperimentalIdentityResolver; }; +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// Warning: (ae-missing-release-tag) "AuthProviderRouteHandlers" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface AuthProviderRouteHandlers { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' frameHandler(req: express.Request, res: express.Response): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' logout?(req: express.Request, res: express.Response): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' refresh?(req: express.Request, res: express.Response): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' start(req: express.Request, res: express.Response): Promise; } +// Warning: (ae-missing-release-tag) "AuthResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AuthResponse = { providerInfo: ProviderInfo; @@ -46,6 +74,8 @@ export type AuthResponse = { backstageIdentity?: BackstageIdentity; }; +// Warning: (ae-missing-release-tag) "BackstageIdentity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BackstageIdentity = { id: string; @@ -54,16 +84,22 @@ export type BackstageIdentity = { entity?: Entity; }; +// Warning: (ae-missing-release-tag) "createGoogleProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createGoogleProvider: ( options?: GoogleProviderOptions | undefined, ) => AuthProviderFactory; +// Warning: (ae-missing-release-tag) "createMicrosoftProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createMicrosoftProvider: ( options?: MicrosoftProviderOptions | undefined, ) => AuthProviderFactory; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter({ logger, @@ -73,20 +109,31 @@ export function createRouter({ providerFactories, }: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "factories" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const defaultAuthProviderFactories: { [providerId: string]: AuthProviderFactory; }; +// Warning: (ae-missing-release-tag) "encodeState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const encodeState: (state: OAuthState) => string; +// Warning: (ae-missing-release-tag) "ensuresXRequestedWith" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ensuresXRequestedWith: (req: express.Request) => boolean; +// Warning: (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "googleEmailSignInResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const googleEmailSignInResolver: SignInResolver; +// Warning: (ae-missing-release-tag) "GoogleProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GoogleProviderOptions = { authHandler?: AuthHandler; @@ -95,6 +142,8 @@ export type GoogleProviderOptions = { }; }; +// Warning: (ae-missing-release-tag) "IdentityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class IdentityClient { constructor(options: { discovery: PluginEndpointDiscovery; issuer: string }); @@ -107,9 +156,13 @@ export class IdentityClient { }>; } +// Warning: (ae-missing-release-tag) "microsoftEmailSignInResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const microsoftEmailSignInResolver: SignInResolver; +// Warning: (ae-missing-release-tag) "MicrosoftProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type MicrosoftProviderOptions = { authHandler?: AuthHandler; @@ -118,11 +171,15 @@ export type MicrosoftProviderOptions = { }; }; +// Warning: (ae-missing-release-tag) "OAuthAdapter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class OAuthAdapter implements AuthProviderRouteHandlers { constructor(handlers: OAuthHandlers, options: Options); // (undocumented) frameHandler(req: express.Request, res: express.Response): Promise; + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts + // // (undocumented) static fromConfig( config: AuthProviderConfig, @@ -140,6 +197,8 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { start(req: express.Request, res: express.Response): Promise; } +// Warning: (ae-missing-release-tag) "OAuthEnvironmentHandler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { constructor(handlers: Map); @@ -158,8 +217,12 @@ export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { start(req: express.Request, res: express.Response): Promise; } +// Warning: (ae-missing-release-tag) "OAuthHandlers" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface OAuthHandlers { + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' handler( req: express.Request, ): Promise<{ @@ -167,10 +230,20 @@ export interface OAuthHandlers { refreshToken?: string; }>; logout?(): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' refresh?(req: OAuthRefreshRequest): Promise>; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (ae-forgotten-export) The symbol "RedirectInfo" needs to be exported by the entry point index.d.ts start(req: OAuthStartRequest): Promise; } +// Warning: (ae-missing-release-tag) "OAuthProviderInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuthProviderInfo = { accessToken: string; @@ -180,6 +253,8 @@ export type OAuthProviderInfo = { refreshToken?: string; }; +// Warning: (ae-missing-release-tag) "OAuthProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type OAuthProviderOptions = { clientId: string; @@ -187,15 +262,21 @@ export type OAuthProviderOptions = { callbackUrl: string; }; +// Warning: (ae-missing-release-tag) "OAuthRefreshRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuthRefreshRequest = express.Request<{}> & { scope: string; refreshToken: string; }; +// Warning: (ae-missing-release-tag) "OAuthResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuthResponse = AuthResponse; +// Warning: (ae-missing-release-tag) "OAuthResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuthResult = { fullProfile: Profile; @@ -208,18 +289,24 @@ export type OAuthResult = { refreshToken?: string; }; +// Warning: (ae-missing-release-tag) "OAuthStartRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuthStartRequest = express.Request<{}> & { scope: string; state: OAuthState; }; +// Warning: (ae-missing-release-tag) "OAuthState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OAuthState = { nonce: string; env: string; }; +// Warning: (ae-missing-release-tag) "postMessageResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const postMessageResponse: ( res: express.Response, @@ -227,6 +314,8 @@ export const postMessageResponse: ( response: WebMessageResponse, ) => void; +// Warning: (ae-missing-release-tag) "ProfileInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type ProfileInfo = { email?: string; @@ -234,9 +323,13 @@ export type ProfileInfo = { picture?: string; }; +// Warning: (ae-missing-release-tag) "readState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const readState: (stateString: string) => OAuthState; +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) @@ -247,13 +340,29 @@ export interface RouterOptions { discovery: PluginEndpointDiscovery; // (undocumented) logger: Logger_2; + // Warning: (ae-forgotten-export) The symbol "ProviderFactories" needs to be exported by the entry point index.d.ts + // // (undocumented) providerFactories?: ProviderFactories; } +// Warning: (ae-missing-release-tag) "TokenIssuer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type TokenIssuer = { + issueToken(params: TokenParams): Promise; + listPublicKeys(): Promise<{ + keys: AnyJWK[]; + }>; +}; + +// Warning: (ae-missing-release-tag) "verifyNonce" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const verifyNonce: (req: express.Request, providerId: string) => void; +// Warning: (ae-missing-release-tag) "WebMessageResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type WebMessageResponse = | { @@ -265,5 +374,14 @@ export type WebMessageResponse = error: Error; }; +// Warnings were encountered during analysis: +// +// src/identity/types.d.ts:25:5 - (ae-forgotten-export) The symbol "TokenParams" needs to be exported by the entry point index.d.ts +// src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts +// src/providers/google/provider.d.ts:36:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:105:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:111:5 - (ae-forgotten-export) The symbol "ExperimentalIdentityResolver" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:128:8 - (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 0b108e11cd..07097a880f 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.3.16", + "version": "0.3.18", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", - "@backstage/catalog-client": "^0.3.16", + "@backstage/backend-common": "^0.8.7", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/test-utils": "^0.1.15", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", "compression": "^1.7.4", @@ -68,7 +68,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.5", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/auth-backend/src/index.ts b/plugins/auth-backend/src/index.ts index d3ea83efd6..6b15b1fc81 100644 --- a/plugins/auth-backend/src/index.ts +++ b/plugins/auth-backend/src/index.ts @@ -16,6 +16,7 @@ export * from './service/router'; export { IdentityClient } from './identity'; +export type { TokenIssuer } from './identity'; export * from './providers'; // flow package provides 2 functions diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 7a13d04a93..306adc70f5 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -56,26 +56,33 @@ type AuthResult = { export type Options = OAuthProviderOptions & { metadataUrl: string; scope?: string; + prompt?: string; tokenSignedResponseAlg?: string; }; export class OidcAuthProvider implements OAuthHandlers { private readonly implementation: Promise; private readonly scope?: string; + private readonly prompt?: string; constructor(options: Options) { this.implementation = this.setupStrategy(options); this.scope = options.scope; + this.prompt = options.prompt; } async start(req: OAuthStartRequest): Promise { const { strategy } = await this.implementation; - return await executeRedirectStrategy(req, strategy, { + const options: Record = { accessType: 'offline', - prompt: 'none', - scope: req.scope || this.scope || '', + scope: req.scope || this.scope || 'openid profile email', state: encodeState(req.state), - }); + }; + const prompt = this.prompt || 'none'; + if (prompt !== 'auto') { + options.prompt = prompt; + } + return await executeRedirectStrategy(req, strategy, options); } async handler( @@ -190,10 +197,11 @@ export const createOidcProvider = ( const clientSecret = envConfig.getString('clientSecret'); const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`; const metadataUrl = envConfig.getString('metadataUrl'); - const tokenSignedResponseAlg = envConfig.getString( + const tokenSignedResponseAlg = envConfig.getOptionalString( 'tokenSignedResponseAlg', ); const scope = envConfig.getOptionalString('scope'); + const prompt = envConfig.getOptionalString('prompt'); const provider = new OidcAuthProvider({ clientId, @@ -202,6 +210,7 @@ export const createOidcProvider = ( tokenSignedResponseAlg, metadataUrl, scope, + prompt, }); return OAuthAdapter.fromConfig(globalConfig, provider, { diff --git a/plugins/badges-backend/api-report.md b/plugins/badges-backend/api-report.md index 19f5a956e4..5723a3f0b8 100644 --- a/plugins/badges-backend/api-report.md +++ b/plugins/badges-backend/api-report.md @@ -9,6 +9,8 @@ import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; +// Warning: (ae-missing-release-tag) "Badge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Badge { color?: string; @@ -21,6 +23,8 @@ export interface Badge { style?: BadgeStyle; } +// Warning: (ae-missing-release-tag) "BADGE_STYLES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const BADGE_STYLES: readonly [ 'plastic', @@ -30,6 +34,8 @@ export const BADGE_STYLES: readonly [ 'social', ]; +// Warning: (ae-missing-release-tag) "BadgeBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BadgeBuilder = { getBadges(): Promise; @@ -37,6 +43,8 @@ export type BadgeBuilder = { createBadgeSvg(options: BadgeOptions): Promise; }; +// Warning: (ae-missing-release-tag) "BadgeContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BadgeContext { // (undocumented) @@ -47,29 +55,39 @@ export interface BadgeContext { entity?: Entity; } +// Warning: (ae-missing-release-tag) "BadgeFactories" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BadgeFactories { // (undocumented) [id: string]: BadgeFactory; } +// Warning: (ae-missing-release-tag) "BadgeFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BadgeFactory { // (undocumented) createBadge(context: BadgeContext): Badge; } +// Warning: (ae-missing-release-tag) "BadgeInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BadgeInfo = { id: string; }; +// Warning: (ae-missing-release-tag) "BadgeOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BadgeOptions = { badgeInfo: BadgeInfo; context: BadgeContext; }; +// Warning: (ae-missing-release-tag) "BadgeSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BadgeSpec = { id: string; @@ -78,15 +96,23 @@ export type BadgeSpec = { markdown: string; }; +// Warning: (ae-missing-release-tag) "BadgeStyle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BadgeStyle = typeof BADGE_STYLES[number]; +// Warning: (ae-missing-release-tag) "createDefaultBadgeFactories" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createDefaultBadgeFactories: () => BadgeFactories; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "DefaultBadgeBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DefaultBadgeBuilder implements BadgeBuilder { constructor(factories: BadgeFactories); @@ -98,6 +124,8 @@ export class DefaultBadgeBuilder implements BadgeBuilder { getBadges(): Promise; } +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index e855201853..c6f4b82596 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-badges +## 0.2.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/badges/api-report.md b/plugins/badges/api-report.md index 7e70f65b0e..9005e34c43 100644 --- a/plugins/badges/api-report.md +++ b/plugins/badges/api-report.md @@ -7,9 +7,13 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "badgesPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const badgesPlugin: BackstagePlugin<{}, {}>; +// Warning: (ae-missing-release-tag) "EntityBadgesDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityBadgesDialog: ({ open, diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 67e2ef90e3..3d38bc6d92 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-badges", - "version": "0.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index ab9d842f1f..388f7ecd27 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-bitrise +## 0.1.7 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.1.6 ### Patch Changes diff --git a/plugins/bitrise/api-report.md b/plugins/bitrise/api-report.md index 66adb0ec68..4d39353113 100644 --- a/plugins/bitrise/api-report.md +++ b/plugins/bitrise/api-report.md @@ -8,12 +8,18 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "bitrisePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const bitrisePlugin: BackstagePlugin<{}, {}>; +// Warning: (ae-missing-release-tag) "EntityBitriseContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityBitriseContent: () => JSX.Element; +// Warning: (ae-missing-release-tag) "isBitriseAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const isBitriseAvailable: (entity: Entity) => boolean; diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index a73fa64797..00eb35f055 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bitrise", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -38,10 +38,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index bd090ff847..8974e896ff 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.2.1 + +### Patch Changes + +- afe3e4b54: Expose missing types used by the custom transformers +- Updated dependencies + - @backstage/plugin-catalog-backend@0.13.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-ldap/api-report.md b/plugins/catalog-backend-module-ldap/api-report.md index f18ace927c..5e713eee09 100644 --- a/plugins/catalog-backend-module-ldap/api-report.md +++ b/plugins/catalog-backend-module-ldap/api-report.md @@ -15,6 +15,8 @@ import { SearchEntry } from 'ldapjs'; import { SearchOptions } from 'ldapjs'; import { UserEntity } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "defaultGroupTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function defaultGroupTransformer( vendor: LdapVendor, @@ -22,6 +24,8 @@ export function defaultGroupTransformer( entry: SearchEntry, ): Promise; +// Warning: (ae-missing-release-tag) "defaultUserTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function defaultUserTransformer( vendor: LdapVendor, @@ -29,6 +33,8 @@ export function defaultUserTransformer( entry: SearchEntry, ): Promise; +// Warning: (ae-missing-release-tag) "GroupConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type GroupConfig = { dn: string; @@ -49,6 +55,12 @@ export type GroupConfig = { }; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-undefined-tag) The TSDoc tag "@return" is not defined in this configuration +// Warning: (ae-missing-release-tag) "GroupTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type GroupTransformer = ( vendor: LdapVendor, @@ -56,18 +68,28 @@ export type GroupTransformer = ( group: SearchEntry, ) => Promise; +// Warning: (ae-missing-release-tag) "LDAP_DN_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const LDAP_DN_ANNOTATION = 'backstage.io/ldap-dn'; +// Warning: (ae-missing-release-tag) "LDAP_RDN_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const LDAP_RDN_ANNOTATION = 'backstage.io/ldap-rdn'; +// Warning: (ae-missing-release-tag) "LDAP_UUID_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const LDAP_UUID_ANNOTATION = 'backstage.io/ldap-uuid'; +// Warning: (ae-missing-release-tag) "LdapClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class LdapClient { constructor(client: Client); + // Warning: (ae-forgotten-export) The symbol "BindConfig" needs to be exported by the entry point index.d.ts + // // (undocumented) static create( logger: Logger_2, @@ -76,9 +98,13 @@ export class LdapClient { ): Promise; getRootDSE(): Promise; getVendor(): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen search(dn: string, options: SearchOptions): Promise; } +// Warning: (ae-missing-release-tag) "LdapOrgReaderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class LdapOrgReaderProcessor implements CatalogProcessor { constructor(options: { @@ -104,6 +130,8 @@ export class LdapOrgReaderProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "LdapProviderConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type LdapProviderConfig = { target: string; @@ -112,6 +140,8 @@ export type LdapProviderConfig = { groups: GroupConfig; }; +// Warning: (ae-missing-release-tag) "LdapVendor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type LdapVendor = { dnAttributeName: string; @@ -119,6 +149,12 @@ export type LdapVendor = { decodeStringAttribute: (entry: SearchEntry, name: string) => string[]; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "mapStringAttr" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function mapStringAttr( entry: SearchEntry, @@ -127,9 +163,18 @@ export function mapStringAttr( setter: (value: string) => void, ): void; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readLdapConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readLdapConfig(config: Config): LdapProviderConfig[]; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "readLdapOrg" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function readLdapOrg( client: LdapClient, @@ -145,6 +190,8 @@ export function readLdapOrg( groups: GroupEntity[]; }>; +// Warning: (ae-missing-release-tag) "UserConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type UserConfig = { dn: string; @@ -163,6 +210,12 @@ export type UserConfig = { }; }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-undefined-tag) The TSDoc tag "@return" is not defined in this configuration +// Warning: (ae-missing-release-tag) "UserTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type UserTransformer = ( vendor: LdapVendor, @@ -170,5 +223,10 @@ export type UserTransformer = ( user: SearchEntry, ) => Promise; +// Warnings were encountered during analysis: +// +// src/ldap/vendors.d.ts:17:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ldap/vendors.d.ts:18:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 27969ca463..e6c8b4e801 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,14 +30,14 @@ "dependencies": { "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/plugin-catalog-backend": "^0.12.0", - "@types/ldapjs": "^1.0.10", + "@backstage/plugin-catalog-backend": "^0.13.0", + "@types/ldapjs": "^2.2.0", "ldapjs": "^2.2.0", "lodash": "^4.17.15", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.4", "@backstage/test-utils": "^0.1.13", "@types/lodash": "^4.14.151", "msw": "^0.29.0" diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 1f23da520f..a719fd0f46 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@0.13.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/api-report.md b/plugins/catalog-backend-module-msgraph/api-report.md index 1fae2731ad..91f8180e94 100644 --- a/plugins/catalog-backend-module-msgraph/api-report.md +++ b/plugins/catalog-backend-module-msgraph/api-report.md @@ -13,45 +13,63 @@ import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import * as msal from '@azure/msal-node'; import { UserEntity } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "defaultGroupTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function defaultGroupTransformer( group: MicrosoftGraph.Group, groupPhoto?: string, ): Promise; +// Warning: (ae-missing-release-tag) "defaultOrganizationTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function defaultOrganizationTransformer( organization: MicrosoftGraph.Organization, ): Promise; +// Warning: (ae-missing-release-tag) "defaultUserTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function defaultUserTransformer( user: MicrosoftGraph.User, userPhoto?: string, ): Promise; +// Warning: (ae-missing-release-tag) "GroupTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GroupTransformer = ( group: MicrosoftGraph.Group, groupPhoto?: string, ) => Promise; +// Warning: (ae-missing-release-tag) "MICROSOFT_GRAPH_GROUP_ID_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const MICROSOFT_GRAPH_GROUP_ID_ANNOTATION = 'graph.microsoft.com/group-id'; +// Warning: (ae-missing-release-tag) "MICROSOFT_GRAPH_TENANT_ID_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION = 'graph.microsoft.com/tenant-id'; +// Warning: (ae-missing-release-tag) "MICROSOFT_GRAPH_USER_ID_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const MICROSOFT_GRAPH_USER_ID_ANNOTATION = 'graph.microsoft.com/user-id'; +// Warning: (ae-missing-release-tag) "MicrosoftGraphClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class MicrosoftGraphClient { constructor(baseUrl: string, pca: msal.ConfidentialClientApplication); // (undocumented) static create(config: MicrosoftGraphProviderConfig): MicrosoftGraphClient; + // Warning: (ae-forgotten-export) The symbol "GroupMember" needs to be exported by the entry point index.d.ts + // // (undocumented) getGroupMembers(groupId: string): AsyncIterable; // (undocumented) @@ -78,12 +96,16 @@ export class MicrosoftGraphClient { getUsers(query?: ODataQuery): AsyncIterable; // (undocumented) requestApi(path: string, query?: ODataQuery): Promise; + // Warning: (ae-forgotten-export) The symbol "ODataQuery" needs to be exported by the entry point index.d.ts + // // (undocumented) requestCollection(path: string, query?: ODataQuery): AsyncIterable; // (undocumented) requestRaw(url: string): Promise; } +// Warning: (ae-missing-release-tag) "MicrosoftGraphOrgReaderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor { constructor(options: { @@ -107,6 +129,8 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "MicrosoftGraphProviderConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type MicrosoftGraphProviderConfig = { target: string; @@ -118,19 +142,27 @@ export type MicrosoftGraphProviderConfig = { groupFilter?: string; }; +// Warning: (ae-missing-release-tag) "normalizeEntityName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function normalizeEntityName(name: string): string; +// Warning: (ae-missing-release-tag) "OrganizationTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type OrganizationTransformer = ( organization: MicrosoftGraph.Organization, ) => Promise; +// Warning: (ae-missing-release-tag) "readMicrosoftGraphConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function readMicrosoftGraphConfig( config: Config, ): MicrosoftGraphProviderConfig[]; +// Warning: (ae-missing-release-tag) "readMicrosoftGraphOrg" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function readMicrosoftGraphOrg( client: MicrosoftGraphClient, @@ -146,11 +178,17 @@ export function readMicrosoftGraphOrg( groups: GroupEntity[]; }>; +// Warning: (ae-missing-release-tag) "UserTransformer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type UserTransformer = ( user: MicrosoftGraph.User, userPhoto?: string, ) => Promise; +// Warnings were encountered during analysis: +// +// src/microsoftGraph/config.d.ts:28:8 - (tsdoc-undefined-tag) The TSDoc tag "@visibility" is not defined in this configuration + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 3d24b9efd6..e5b167695f 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ "@azure/msal-node": "^1.1.0", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/plugin-catalog-backend": "^0.12.0", + "@backstage/plugin-catalog-backend": "^0.13.0", "@microsoft/microsoft-graph-types": "^1.25.0", "cross-fetch": "^3.0.6", "lodash": "^4.17.15", @@ -40,8 +40,8 @@ "qs": "^6.9.4" }, "devDependencies": { - "@backstage/backend-common": "^0.8.5", - "@backstage/cli": "^0.7.3", + "@backstage/backend-common": "^0.8.6", + "@backstage/cli": "^0.7.4", "@backstage/test-utils": "^0.1.14", "@types/lodash": "^4.14.151", "msw": "^0.29.0" diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index bc8de4878d..daa6c5a0e8 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-catalog-backend +## 0.13.0 + +### Minor Changes + +- 8bfc0571c: Add a default catalog value for BitBucketDiscoveryProcessor. This allows to have a target like so: https://bitbucket.mycompany.com/projects/backstage/repos/service-* + which will be expanded to https://bitbucket.mycompany.com/projects/backstage/repos/service-a/catalog-info.yaml given that repository 'service-a' exists. + + ## Migration + + If you are using a custom [Bitbucket parser](https://backstage.io/docs/integrations/bitbucket/discovery#custom-repository-processing) and your `bitbucket-discovery` target (e.g. in your app-config.yaml) omits the catalog path in any of the following ways: + + - https://bitbucket.mycompany.com/projects/backstage/repos/service-* + - https://bitbucket.mycompany.com/projects/backstage/repos/* + - https://bitbucket.mycompany.com/projects/backstage/repos/*/ + + then you will be affected by this change. + The 'target' input to your parser before this commit would be '/', and after this commit it will be '/catalog-info.yaml', and as such needs to be handled to maintain the same functionality. + +### Patch Changes + +- 8b048934b: The codeowners processor extracts the username of the primary owner and uses this as the owner field. + Given the kind isn't specified this is assumed to be a group and so the link to the owner in the about card + doesn't work. This change specifies the kind where the entity is a user. e.g: + + `@iain-b` -> `user:iain-b` + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + - @backstage/plugin-search-backend-node@0.4.0 + ## 0.12.0 ### Minor Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index ae7559f5d1..0bf7831c6d 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -30,25 +30,34 @@ import { ScmIntegrations } from '@backstage/integration'; import { UrlReader } from '@backstage/backend-common'; import { Validators } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "AddLocationResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AddLocationResult = { location: Location_2; entities: Entity[]; }; +// Warning: (ae-missing-release-tag) "AnalyzeLocationRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AnalyzeLocationRequest = { location: LocationSpec; }; +// Warning: (ae-missing-release-tag) "AnalyzeLocationResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AnalyzeLocationResponse = { existingEntityFiles: AnalyzeLocationExistingEntity[]; generateEntities: AnalyzeLocationGenerateEntity[]; }; +// Warning: (ae-missing-release-tag) "AnnotateLocationEntityProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class AnnotateLocationEntityProcessor implements CatalogProcessor { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options_2); // (undocumented) preProcessEntity( @@ -59,6 +68,8 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "AnnotateScmSlugEntityProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { constructor(opts: { scmIntegrationRegistry: ScmIntegrationRegistry }); @@ -68,6 +79,8 @@ export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { preProcessEntity(entity: Entity, location: LocationSpec): Promise; } +// Warning: (ae-missing-release-tag) "AwsOrganizationCloudAccountProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { constructor(options: { @@ -98,6 +111,8 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { normalizeName(name: string): string; // (undocumented) organizations: Organizations; + // Warning: (ae-forgotten-export) The symbol "AwsOrganizationProviderConfig" needs to be exported by the entry point index.d.ts + // // (undocumented) provider: AwsOrganizationProviderConfig; // (undocumented) @@ -108,6 +123,8 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "BitbucketDiscoveryProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class BitbucketDiscoveryProcessor implements CatalogProcessor { constructor(options: { @@ -131,6 +148,8 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "BitbucketRepositoryParser" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BitbucketRepositoryParser = (options: { integration: BitbucketIntegration; @@ -138,6 +157,8 @@ export type BitbucketRepositoryParser = (options: { logger: Logger_2; }) => AsyncIterable; +// Warning: (ae-missing-release-tag) "BuiltinKindsEntityProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class BuiltinKindsEntityProcessor implements CatalogProcessor { // (undocumented) @@ -150,10 +171,14 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { validateEntityKind(entity: Entity): Promise; } +// Warning: (ae-missing-release-tag) "CatalogBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class CatalogBuilder { constructor(env: CatalogEnvironment); + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen addEntityPolicy(...policies: EntityPolicy[]): CatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen addProcessor(...processors: CatalogProcessor[]): CatalogBuilder; build(): Promise<{ entitiesCatalog: EntitiesCatalog; @@ -161,18 +186,28 @@ export class CatalogBuilder { higherOrderOperation: HigherOrderOperation; locationAnalyzer: LocationAnalyzer; }>; + // Warning: (ae-forgotten-export) The symbol "CatalogEnvironment" needs to be exported by the entry point index.d.ts + // // (undocumented) static create(env: CatalogEnvironment): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen replaceEntityPolicies(policies: EntityPolicy[]): CatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen replaceProcessors(processors: CatalogProcessor[]): CatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen setEntityDataParser(parser: CatalogProcessorParser): CatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen setFieldFormatValidators(validators: Partial): CatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen setPlaceholderResolver( key: string, resolver: PlaceholderResolver, ): CatalogBuilder; } +// Warning: (ae-missing-release-tag) "CatalogEntityDocument" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CatalogEntityDocument extends IndexableDocument { // (undocumented) @@ -187,12 +222,16 @@ export interface CatalogEntityDocument extends IndexableDocument { owner: string; } +// Warning: (ae-missing-release-tag) "CatalogProcessingOrchestrator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CatalogProcessingOrchestrator { // (undocumented) process(request: EntityProcessingRequest): Promise; } +// Warning: (ae-missing-release-tag) "CatalogProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessor = { readLocation?( @@ -220,9 +259,13 @@ export type CatalogProcessor = { ): Promise; }; +// Warning: (ae-missing-release-tag) "CatalogProcessorEmit" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessorEmit = (generated: CatalogProcessorResult) => void; +// Warning: (ae-missing-release-tag) "CatalogProcessorEntityResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessorEntityResult = { type: 'entity'; @@ -230,6 +273,8 @@ export type CatalogProcessorEntityResult = { location: LocationSpec; }; +// Warning: (ae-missing-release-tag) "CatalogProcessorErrorResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessorErrorResult = { type: 'error'; @@ -237,6 +282,8 @@ export type CatalogProcessorErrorResult = { location: LocationSpec; }; +// Warning: (ae-missing-release-tag) "CatalogProcessorLocationResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessorLocationResult = { type: 'location'; @@ -244,12 +291,16 @@ export type CatalogProcessorLocationResult = { optional: boolean; }; +// Warning: (ae-missing-release-tag) "CatalogProcessorParser" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type CatalogProcessorParser = (options: { data: Buffer; location: LocationSpec; }) => AsyncIterable; +// Warning: (ae-missing-release-tag) "CatalogProcessorRelationResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessorRelationResult = { type: 'relation'; @@ -257,6 +308,8 @@ export type CatalogProcessorRelationResult = { entityRef?: string; }; +// Warning: (ae-missing-release-tag) "CatalogProcessorResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogProcessorResult = | CatalogProcessorLocationResult @@ -264,6 +317,8 @@ export type CatalogProcessorResult = | CatalogProcessorRelationResult | CatalogProcessorErrorResult; +// Warning: (ae-missing-release-tag) "CodeOwnersProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CodeOwnersProcessor implements CatalogProcessor { constructor(options: { @@ -283,6 +338,8 @@ export class CodeOwnersProcessor implements CatalogProcessor { preProcessEntity(entity: Entity, location: LocationSpec): Promise; } +// Warning: (ae-missing-release-tag) "CommonDatabase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class CommonDatabase implements Database { constructor(database: Knex, logger: Logger_2); @@ -291,11 +348,15 @@ export class CommonDatabase implements Database { txOpaque: Transaction, request: DbEntityRequest[], ): Promise; + // Warning: (ae-forgotten-export) The symbol "DbLocationsRow" needs to be exported by the entry point index.d.ts + // // (undocumented) addLocation( txOpaque: Transaction, location: Location_2, ): Promise; + // Warning: (ae-forgotten-export) The symbol "DatabaseLocationUpdateLogStatus" needs to be exported by the entry point index.d.ts + // // (undocumented) addLocationUpdateLogEvent( locationId: string, @@ -303,6 +364,9 @@ export class CommonDatabase implements Database { entityName?: string | string[], message?: string, ): Promise; + // Warning: (ae-forgotten-export) The symbol "DbEntitiesRequest" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "DbEntitiesResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) entities( txOpaque: Transaction, @@ -318,8 +382,12 @@ export class CommonDatabase implements Database { txOpaque: Transaction, uid: string, ): Promise; + // Warning: (ae-forgotten-export) The symbol "DbLocationsRowWithStatus" needs to be exported by the entry point index.d.ts + // // (undocumented) location(id: string): Promise; + // Warning: (ae-forgotten-export) The symbol "DatabaseLocationUpdateLogEvent" needs to be exported by the entry point index.d.ts + // // (undocumented) locationHistory(id: string): Promise; // (undocumented) @@ -345,14 +413,21 @@ export class CommonDatabase implements Database { ): Promise; } +// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createNextRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createNextRouter( options: RouterOptions_2, ): Promise; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "Database" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Database = { transaction(fn: (tx: Transaction) => Promise): Promise; @@ -397,9 +472,14 @@ export type Database = { ): Promise; }; +// Warning: (ae-missing-release-tag) "DatabaseEntitiesCatalog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DatabaseEntitiesCatalog implements EntitiesCatalog { constructor(database: Database, logger: Logger_2); + // Warning: (ae-forgotten-export) The symbol "EntityUpsertRequest" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "EntityUpsertResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) batchAddOrUpdateEntities( requests: EntityUpsertRequest[], @@ -409,12 +489,17 @@ export class DatabaseEntitiesCatalog implements EntitiesCatalog { outputEntities?: boolean; }, ): Promise; + // Warning: (ae-forgotten-export) The symbol "EntitiesRequest" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "EntitiesResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) entities(request?: EntitiesRequest): Promise; // (undocumented) removeEntityByUid(uid: string): Promise; } +// Warning: (ae-missing-release-tag) "DatabaseLocationsCatalog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DatabaseLocationsCatalog implements LocationsCatalog { constructor(database: Database); @@ -424,6 +509,8 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { location(id: string): Promise; // (undocumented) locationHistory(id: string): Promise; + // Warning: (ae-forgotten-export) The symbol "LocationResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) locations(): Promise; // (undocumented) @@ -441,8 +528,12 @@ export class DatabaseLocationsCatalog implements LocationsCatalog { removeLocation(id: string): Promise; } +// Warning: (ae-missing-release-tag) "DatabaseManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DatabaseManager { + // Warning: (ae-forgotten-export) The symbol "CreateDatabaseOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) static createDatabase( knex: Knex, @@ -458,6 +549,8 @@ export class DatabaseManager { static createTestDatabaseConnection(): Promise; } +// Warning: (ae-missing-release-tag) "DbEntityRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type DbEntityRequest = { locationId?: string; @@ -465,12 +558,16 @@ export type DbEntityRequest = { relations: EntityRelationSpec[]; }; +// Warning: (ae-missing-release-tag) "DbEntityResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type DbEntityResponse = { locationId?: string; entity: Entity; }; +// Warning: (ae-missing-release-tag) "DefaultCatalogCollator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DefaultCatalogCollator implements DocumentCollator { constructor({ @@ -495,6 +592,8 @@ export class DefaultCatalogCollator implements DocumentCollator { readonly type: string; } +// Warning: (ae-missing-release-tag) "DefaultCatalogProcessingOrchestrator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class DefaultCatalogProcessingOrchestrator implements CatalogProcessingOrchestrator { @@ -509,9 +608,14 @@ export class DefaultCatalogProcessingOrchestrator process(request: EntityProcessingRequest): Promise; } +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "durationText" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function durationText(startTimestamp: [number, number]): string; +// Warning: (ae-missing-release-tag) "EntitiesCatalog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type EntitiesCatalog = { entities(request?: EntitiesRequest): Promise; @@ -526,18 +630,24 @@ export type EntitiesCatalog = { ): Promise; }; +// Warning: (ae-missing-release-tag) "EntitiesSearchFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntitiesSearchFilter = { key: string; matchValueIn?: string[]; }; +// Warning: (ae-missing-release-tag) "entity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function entity( atLocation: LocationSpec, newEntity: Entity, ): CatalogProcessorResult; +// Warning: (ae-missing-release-tag) "EntityFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityFilter = { anyOf: { @@ -545,6 +655,8 @@ export type EntityFilter = { }[]; }; +// Warning: (ae-missing-release-tag) "EntityPagination" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type EntityPagination = { limit?: number; @@ -552,12 +664,16 @@ export type EntityPagination = { after?: string; }; +// Warning: (ae-missing-release-tag) "EntityProcessingRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type EntityProcessingRequest = { entity: Entity; state: Map; }; +// Warning: (ae-missing-release-tag) "EntityProcessingResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type EntityProcessingResult = | { @@ -573,6 +689,8 @@ export type EntityProcessingResult = errors: Error[]; }; +// Warning: (ae-missing-release-tag) "FileReaderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class FileReaderProcessor implements CatalogProcessor { // (undocumented) @@ -583,12 +701,16 @@ export class FileReaderProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "generalError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function generalError( atLocation: LocationSpec, message: string, ): CatalogProcessorResult; +// Warning: (ae-missing-release-tag) "GithubDiscoveryProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class GithubDiscoveryProcessor implements CatalogProcessor { constructor(options: { integrations: ScmIntegrations; logger: Logger_2 }); @@ -629,6 +751,8 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "GithubOrgReaderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class GithubOrgReaderProcessor implements CatalogProcessor { constructor(options: { integrations: ScmIntegrations; logger: Logger_2 }); @@ -647,6 +771,8 @@ export class GithubOrgReaderProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "HigherOrderOperation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type HigherOrderOperation = { addLocation( @@ -658,6 +784,8 @@ export type HigherOrderOperation = { refreshAllLocations(): Promise; }; +// Warning: (ae-missing-release-tag) "HigherOrderOperations" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class HigherOrderOperations implements HigherOrderOperation { constructor( @@ -666,6 +794,7 @@ export class HigherOrderOperations implements HigherOrderOperation { locationReader: LocationReader, logger: Logger_2, ); + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen addLocation( spec: LocationSpec, options?: { @@ -675,18 +804,24 @@ export class HigherOrderOperations implements HigherOrderOperation { refreshAllLocations(): Promise; } +// Warning: (ae-missing-release-tag) "inputError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function inputError( atLocation: LocationSpec, message: string, ): CatalogProcessorResult; +// Warning: (ae-missing-release-tag) "location" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function location_2( newLocation: LocationSpec, optional: boolean, ): CatalogProcessorResult; +// Warning: (ae-missing-release-tag) "LocationAnalyzer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LocationAnalyzer = { analyzeLocation( @@ -694,8 +829,11 @@ export type LocationAnalyzer = { ): Promise; }; +// Warning: (ae-missing-release-tag) "LocationEntityProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class LocationEntityProcessor implements CatalogProcessor { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options_3); // (undocumented) postProcessEntity( @@ -705,18 +843,25 @@ export class LocationEntityProcessor implements CatalogProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "LocationReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LocationReader = { read(location: LocationSpec): Promise; }; +// Warning: (ae-missing-release-tag) "LocationReaders" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class LocationReaders implements LocationReader { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options); // (undocumented) read(location: LocationSpec): Promise; } +// Warning: (ae-missing-release-tag) "LocationsCatalog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LocationsCatalog = { addLocation(location: Location_2): Promise; @@ -735,11 +880,18 @@ export type LocationsCatalog = { ): Promise; }; +// Warning: (ae-missing-release-tag) "NextCatalogBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class NextCatalogBuilder { + // Warning: (ae-forgotten-export) The symbol "CatalogEnvironment" needs to be exported by the entry point index.d.ts constructor(env: CatalogEnvironment_2); + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen addEntityPolicy(...policies: EntityPolicy[]): NextCatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (ae-forgotten-export) The symbol "EntityProvider" needs to be exported by the entry point index.d.ts addEntityProvider(...providers: EntityProvider[]): NextCatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen addProcessor(...processors: CatalogProcessor[]): NextCatalogBuilder; build(): Promise<{ entitiesCatalog: EntitiesCatalog; @@ -748,10 +900,16 @@ export class NextCatalogBuilder { processingEngine: CatalogProcessingEngine; locationService: LocationService; }>; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen replaceEntityPolicies(policies: EntityPolicy[]): NextCatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen replaceProcessors(processors: CatalogProcessor[]): NextCatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen setEntityDataParser(parser: CatalogProcessorParser): NextCatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen setFieldFormatValidators(validators: Partial): NextCatalogBuilder; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen setPlaceholderResolver( key: string, resolver: PlaceholderResolver, @@ -759,30 +917,42 @@ export class NextCatalogBuilder { setRefreshIntervalSeconds(seconds: number): NextCatalogBuilder; } +// Warning: (ae-missing-release-tag) "notFoundError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function notFoundError( atLocation: LocationSpec, message: string, ): CatalogProcessorResult; +// Warning: (ae-missing-release-tag) "parseEntityYaml" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function parseEntityYaml( data: Buffer, location: LocationSpec, ): Iterable; +// Warning: (ae-missing-release-tag) "PlaceholderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class PlaceholderProcessor implements CatalogProcessor { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options_4); // (undocumented) preProcessEntity(entity: Entity, location: LocationSpec): Promise; } +// Warning: (ae-forgotten-export) The symbol "ResolverParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PlaceholderResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PlaceholderResolver = ( params: ResolverParams, ) => Promise; +// Warning: (ae-missing-release-tag) "ReadLocationEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ReadLocationEntity = { location: LocationSpec; @@ -790,18 +960,24 @@ export type ReadLocationEntity = { relations: EntityRelationSpec[]; }; +// Warning: (ae-missing-release-tag) "ReadLocationError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ReadLocationError = { location: LocationSpec; error: Error; }; +// Warning: (ae-missing-release-tag) "ReadLocationResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ReadLocationResult = { entities: ReadLocationEntity[]; errors: ReadLocationError[]; }; +// Warning: (ae-missing-release-tag) "RecursivePartial" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type RecursivePartial = { [P in keyof T]?: T[P] extends (infer U)[] @@ -811,6 +987,8 @@ export type RecursivePartial = { : T[P]; }; +// Warning: (ae-missing-release-tag) "relation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function relation(spec: EntityRelationSpec): CatalogProcessorResult; @@ -826,6 +1004,8 @@ declare namespace results { } export { results }; +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) @@ -844,9 +1024,15 @@ export interface RouterOptions { logger: Logger_2; } +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-missing-release-tag) "runPeriodically" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function runPeriodically(fn: () => any, delayMs: number): () => void; +// Warning: (ae-missing-release-tag) "StaticLocationProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class StaticLocationProcessor implements StaticLocationProcessor { constructor(staticLocations: LocationSpec[]); @@ -860,13 +1046,18 @@ export class StaticLocationProcessor implements StaticLocationProcessor { ): Promise; } +// Warning: (ae-missing-release-tag) "Transaction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Transaction = { rollback(): Promise; }; +// Warning: (ae-missing-release-tag) "UrlReaderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class UrlReaderProcessor implements CatalogProcessor { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options_5); // (undocumented) readLocation( @@ -877,5 +1068,48 @@ export class UrlReaderProcessor implements CatalogProcessor { ): Promise; } +// Warnings were encountered during analysis: +// +// src/catalog/types.d.ts:30:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/catalog/types.d.ts:36:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/catalog/types.d.ts:42:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/catalog/types.d.ts:43:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters +// src/catalog/types.d.ts:44:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters +// src/catalog/types.d.ts:45:8 - (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters +// src/catalog/types.d.ts:75:5 - (ae-forgotten-export) The symbol "LocationUpdateLogEvent" needs to be exported by the entry point index.d.ts +// src/database/types.d.ts:121:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:127:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:128:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:142:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:143:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:144:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:146:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:159:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:160:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/database/types.d.ts:161:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/GithubMultiOrgReaderProcessor.d.ts:23:9 - (ae-forgotten-export) The symbol "GithubMultiOrgConfig" needs to be exported by the entry point index.d.ts +// src/ingestion/processors/types.d.ts:7:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:8:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:9:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:10:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:23:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:24:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:25:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:26:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:36:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:47:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:48:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:49:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:56:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:57:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/processors/types.d.ts:58:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/types.d.ts:17:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/types.d.ts:41:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/ingestion/types.d.ts:49:5 - (ae-forgotten-export) The symbol "AnalyzeLocationExistingEntity" needs to be exported by the entry point index.d.ts +// src/ingestion/types.d.ts:50:5 - (ae-forgotten-export) The symbol "AnalyzeLocationGenerateEntity" needs to be exported by the entry point index.d.ts +// src/next/NextCatalogBuilder.d.ts:140:9 - (ae-forgotten-export) The symbol "CatalogProcessingEngine" needs to be exported by the entry point index.d.ts +// src/next/NextCatalogBuilder.d.ts:141:9 - (ae-forgotten-export) The symbol "LocationService" needs to be exported by the entry point index.d.ts +// src/next/processing/types.d.ts:11:5 - (ae-forgotten-export) The symbol "DeferredEntity" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index a3d0ff14da..10d89c41f2 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.12.0", + "version": "0.13.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,13 +29,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", + "@backstage/backend-common": "^0.8.6", "@backstage/catalog-client": "^0.3.16", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", - "@backstage/plugin-search-backend-node": "^0.3.0", + "@backstage/plugin-search-backend-node": "^0.4.0", "@backstage/search-common": "^0.1.2", "@octokit/graphql": "^4.5.8", "@types/express": "^4.17.6", @@ -62,7 +62,7 @@ }, "devDependencies": { "@backstage/backend-test-utils": "^0.1.4", - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.4", "@backstage/test-utils": "^0.1.14", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts index d33ea7ab26..e2b295e7ff 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts @@ -225,6 +225,34 @@ describe('BitbucketDiscoveryProcessor', () => { optional: true, }); }); + + it.each` + target + ${'https://bitbucket.mycompany.com/projects/backstage/repos/*'} + ${'https://bitbucket.mycompany.com/projects/backstage/repos/*/'} + ${'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-*/'} + `("target '$target' adds default path to catalog", async ({ target }) => { + setupStubs([{ key: 'backstage', repos: ['techdocs-cli'] }]); + + const location: LocationSpec = { + type: 'bitbucket-discovery', + target: target, + }; + + const emitter = jest.fn(); + await processor.readLocation(location, false, emitter); + + expect(emitter).toHaveBeenCalledTimes(1); + expect(emitter).toHaveBeenCalledWith({ + type: 'location', + location: { + type: 'url', + target: + 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-cli/browse/catalog-info.yaml', + }, + optional: true, + }); + }); }); describe('Custom repository parser', () => { diff --git a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts index 877cd08a3e..9d53b7e5b5 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts @@ -84,13 +84,15 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { this.logger.info(`Reading Bitbucket repositories from ${location.target}`); const { catalogPath } = parseUrl(location.target); + const expandedCatalogPath = + catalogPath === '/' ? '/catalog-info.yaml' : catalogPath; const result = await readBitbucketOrg(client, location.target); for (const repository of result.matches) { for await (const entity of this.parser({ integration: integration, - target: `${repository.links.self[0].href}${catalogPath}`, + target: `${repository.links.self[0].href}${expandedCatalogPath}`, logger: this.logger, })) { emit(entity); diff --git a/plugins/catalog-graphql/api-report.md b/plugins/catalog-graphql/api-report.md index 67beee09da..ac64559f13 100644 --- a/plugins/catalog-graphql/api-report.md +++ b/plugins/catalog-graphql/api-report.md @@ -7,9 +7,13 @@ import { Config } from '@backstage/config'; import { GraphQLModule } from '@graphql-modules/core'; import { Logger as Logger_2 } from 'winston'; +// Warning: (ae-missing-release-tag) "createModule" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createModule(options: ModuleOptions): Promise; +// Warning: (ae-missing-release-tag) "ModuleOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ModuleOptions { // (undocumented) diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index fb9ae20305..ad1a05da5e 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-import +## 0.5.14 + +### Patch Changes + +- 903f3323c: Fix heading that wrongly implied catalog-import supports entity discovery for multiple integrations. +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/catalog-client@0.3.17 + - @backstage/plugin-catalog-react@0.3.1 + +## 0.5.13 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.5.12 ### Patch Changes diff --git a/plugins/catalog-import/api-report.md b/plugins/catalog-import/api-report.md index 808d8f1909..ac4360f463 100644 --- a/plugins/catalog-import/api-report.md +++ b/plugins/catalog-import/api-report.md @@ -27,6 +27,8 @@ import { UseControllerOptions } from 'react-hook-form'; import { UseFormMethods } from 'react-hook-form'; import { UseFormOptions } from 'react-hook-form'; +// Warning: (ae-missing-release-tag) "AnalyzeResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AnalyzeResult = | { @@ -43,6 +45,9 @@ export type AnalyzeResult = generatedEntities: PartialEntity[]; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AutocompleteTextField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const AutocompleteTextField: ({ name, @@ -58,6 +63,8 @@ export const AutocompleteTextField: ({ textFieldProps, }: Props_4) => JSX.Element; +// Warning: (ae-missing-release-tag) "CatalogImportApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CatalogImportApi { // (undocumented) @@ -74,9 +81,13 @@ export interface CatalogImportApi { }>; } +// Warning: (ae-missing-release-tag) "catalogImportApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const catalogImportApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "CatalogImportClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CatalogImportClient implements CatalogImportApi { constructor(options: { @@ -105,9 +116,14 @@ export class CatalogImportClient implements CatalogImportApi { }>; } +// Warning: (ae-forgotten-export) The symbol "StepperProviderOpts" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "CatalogImportPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CatalogImportPage: (opts: StepperProviderOpts) => JSX.Element; +// Warning: (ae-missing-release-tag) "catalogImportPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const catalogImportPlugin: BackstagePlugin< { @@ -118,12 +134,21 @@ const catalogImportPlugin: BackstagePlugin< export { catalogImportPlugin }; export { catalogImportPlugin as plugin }; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-forgotten-export) The symbol "ImportFlows" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "StepperProvider" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "defaultGenerateStepper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function defaultGenerateStepper( flow: ImportFlows, defaults: StepperProvider, ): StepperProvider; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityListComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityListComponent: ({ locations, @@ -134,6 +159,9 @@ export const EntityListComponent: ({ withLinks, }: Props_2) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ImportStepper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ImportStepper: ({ initialUrl, @@ -142,6 +170,12 @@ export const ImportStepper: ({ opts, }: Props) => JSX.Element; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PreparePullRequestForm" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const PreparePullRequestForm: < TFieldValues extends Record @@ -151,6 +185,9 @@ export const PreparePullRequestForm: < render, }: Props_5) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PreviewCatalogInfoComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const PreviewCatalogInfoComponent: ({ repositoryUrl, @@ -158,6 +195,9 @@ export const PreviewCatalogInfoComponent: ({ classes, }: Props_6) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PreviewPullRequestComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const PreviewPullRequestComponent: ({ title, @@ -165,9 +205,17 @@ export const PreviewPullRequestComponent: ({ classes, }: Props_7) => JSX.Element; +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (opts: StepperProviderOpts) => JSX.Element; +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "StepInitAnalyzeUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const StepInitAnalyzeUrl: ({ onAnalysis, @@ -175,6 +223,9 @@ export const StepInitAnalyzeUrl: ({ disablePullRequest, }: Props_3) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "StepPrepareCreatePullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const StepPrepareCreatePullRequest: ({ analyzeResult, @@ -185,5 +236,9 @@ export const StepPrepareCreatePullRequest: ({ defaultBody, }: Props_8) => JSX.Element; +// Warnings were encountered during analysis: +// +// src/api/CatalogImportApi.d.ts:14:5 - (ae-forgotten-export) The symbol "PartialEntity" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 3b3f441e8a..d6f371bc65 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.5.12", + "version": "0.5.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,13 +30,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.1", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -54,10 +54,10 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/catalog-import/src/components/ImportComponentPage.tsx b/plugins/catalog-import/src/components/ImportComponentPage.tsx index 6bf5af24ef..f4ca144165 100644 --- a/plugins/catalog-import/src/components/ImportComponentPage.tsx +++ b/plugins/catalog-import/src/components/ImportComponentPage.tsx @@ -14,12 +14,12 @@ * limitations under the License. */ -import { Grid, Typography } from '@material-ui/core'; +import { Chip, Grid, Typography } from '@material-ui/core'; import React from 'react'; import { ImportStepper } from './ImportStepper'; import { StepperProviderOpts } from './ImportStepper/defaults'; -import { ConfigApi, configApiRef, useApi } from '@backstage/core-plugin-api'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { Content, ContentHeader, @@ -29,30 +29,12 @@ import { SupportButton, } from '@backstage/core-components'; -function repositories(configApi: ConfigApi): string[] { - const integrations = configApi.getConfig('integrations'); - const repos = []; - if (integrations.has('github')) { - repos.push('GitHub'); - } - if (integrations.has('bitbucket')) { - repos.push('Bitbucket'); - } - if (integrations.has('gitlab')) { - repos.push('GitLab'); - } - if (integrations.has('azure')) { - repos.push('Azure'); - } - return repos; -} - export const ImportComponentPage = (opts: StepperProviderOpts) => { const configApi = useApi(configApiRef); const appTitle = configApi.getOptional('app.title') || 'Backstage'; - const repos = repositories(configApi); - const repositoryString = repos.join(', ').replace(/, (\w*)$/, ' or $1'); + const integrations = configApi.getConfig('integrations'); + const hasGithubIntegration = integrations.has('github'); return ( @@ -76,7 +58,8 @@ export const ImportComponentPage = (opts: StepperProviderOpts) => { }} > - Enter the URL to your SCM repository to add it to {appTitle}. + Enter the URL to your source code repository to add it to{' '} + {appTitle}. Link to an existing entity file @@ -91,10 +74,11 @@ export const ImportComponentPage = (opts: StepperProviderOpts) => { The wizard analyzes the file, previews the entities, and adds them to the {appTitle} catalog. - {repos.length > 0 && ( + {hasGithubIntegration && ( <> - Link to a {repositoryString} repository + Link to a repository{' '} + ; +// Warning: (ae-missing-release-tag) "catalogRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const catalogRouteRef: RouteRef; +// Warning: (ae-missing-release-tag) "createDomainColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createDomainColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createEntityRefColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createEntityRefColumn({ defaultKind, @@ -40,6 +48,8 @@ function createEntityRefColumn({ defaultKind?: string; }): TableColumn; +// Warning: (ae-missing-release-tag) "createEntityRelationColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createEntityRelationColumn({ title, @@ -55,21 +65,33 @@ function createEntityRelationColumn({ }; }): TableColumn; +// Warning: (ae-missing-release-tag) "createMetadataDescriptionColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createMetadataDescriptionColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createOwnerColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createOwnerColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createSpecLifecycleColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createSpecLifecycleColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createSpecTypeColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createSpecTypeColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createSystemColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createSystemColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "DefaultEntityFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type DefaultEntityFilters = { kind?: EntityKindFilter; @@ -81,9 +103,14 @@ export type DefaultEntityFilters = { text?: EntityTextFilter; }; +// Warning: (ae-forgotten-export) The symbol "EntityLoadingStatus" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityContext: Context; +// Warning: (ae-missing-release-tag) "EntityFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type EntityFilter = { getCatalogFilters?: () => Record; @@ -91,6 +118,8 @@ export type EntityFilter = { toQueryValue?: () => string | string[]; }; +// Warning: (ae-missing-release-tag) "EntityKindFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class EntityKindFilter implements EntityFilter { constructor(value: string); @@ -102,12 +131,17 @@ export class EntityKindFilter implements EntityFilter { readonly value: string; } +// Warning: (ae-forgotten-export) The symbol "EntityKindFilterProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityKindPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityKindPicker: ({ initialFilter, hidden, }: EntityKindFilterProps) => JSX.Element | null; +// Warning: (ae-missing-release-tag) "EntityLifecycleFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class EntityLifecycleFilter implements EntityFilter { constructor(values: string[]); @@ -119,19 +153,28 @@ export class EntityLifecycleFilter implements EntityFilter { readonly values: string[]; } +// Warning: (ae-missing-release-tag) "EntityLifecyclePicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLifecyclePicker: () => JSX.Element | null; +// Warning: (ae-forgotten-export) The symbol "EntityListContextProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityListContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityListContext: React_2.Context< EntityListContextProps | undefined >; +// Warning: (ae-missing-release-tag) "EntityListProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityListProvider: ({ children, }: PropsWithChildren<{}>) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityOwnerFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class EntityOwnerFilter implements EntityFilter { constructor(values: string[]); @@ -143,15 +186,23 @@ export class EntityOwnerFilter implements EntityFilter { readonly values: string[]; } +// Warning: (ae-missing-release-tag) "EntityOwnerPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityOwnerPicker: () => JSX.Element | null; +// Warning: (ae-forgotten-export) The symbol "EntityProviderProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityProvider: ({ entity, children, }: EntityProviderProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "EntityRefLinkProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityRefLink" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityRefLink: React_2.ForwardRefExoticComponent< Pick< @@ -440,6 +491,9 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< React_2.RefAttributes >; +// Warning: (ae-forgotten-export) The symbol "EntityRefLinksProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityRefLinks" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityRefLinks: ({ entityRefs, @@ -447,6 +501,8 @@ export const EntityRefLinks: ({ ...linkProps }: EntityRefLinksProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "entityRoute" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const entityRoute: RouteRef<{ name: string; @@ -454,6 +510,8 @@ export const entityRoute: RouteRef<{ namespace: string; }>; +// Warning: (ae-missing-release-tag) "entityRouteParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function entityRouteParams( entity: Entity, @@ -463,6 +521,8 @@ export function entityRouteParams( readonly name: string; }; +// Warning: (ae-missing-release-tag) "entityRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const entityRouteRef: RouteRef<{ name: string; @@ -470,15 +530,23 @@ export const entityRouteRef: RouteRef<{ namespace: string; }>; +// Warning: (ae-missing-release-tag) "EntitySearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySearchBar: () => JSX.Element; +// Warning: (ae-missing-release-tag) "EntitySourceLocation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type EntitySourceLocation = { locationTargetUrl: string; integrationType?: string; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "EntityTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function EntityTable({ entities, @@ -490,7 +558,9 @@ export function EntityTable({ // @public (undocumented) export namespace EntityTable { - var // (undocumented) + var // Warning: (ae-forgotten-export) The symbol "columnFactories" needs to be exported by the entry point index.d.ts + // + // (undocumented) columns: typeof columnFactories; var // (undocumented) systemEntityColumns: TableColumn[]; @@ -498,6 +568,8 @@ export namespace EntityTable { componentEntityColumns: TableColumn[]; } +// Warning: (ae-missing-release-tag) "EntityTagFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class EntityTagFilter implements EntityFilter { constructor(values: string[]); @@ -509,9 +581,13 @@ export class EntityTagFilter implements EntityFilter { readonly values: string[]; } +// Warning: (ae-missing-release-tag) "EntityTagPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityTagPicker: () => JSX.Element | null; +// Warning: (ae-missing-release-tag) "EntityTextFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class EntityTextFilter implements EntityFilter { constructor(value: string); @@ -521,6 +597,8 @@ export class EntityTextFilter implements EntityFilter { readonly value: string; } +// Warning: (ae-missing-release-tag) "EntityTypeFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class EntityTypeFilter implements EntityFilter { constructor(value: string | string[]); @@ -534,9 +612,13 @@ export class EntityTypeFilter implements EntityFilter { readonly value: string | string[]; } +// Warning: (ae-missing-release-tag) "EntityTypePicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityTypePicker: () => JSX.Element | null; +// Warning: (ae-missing-release-tag) "formatEntityRefTitle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function formatEntityRefTitle( entityRef: Entity | EntityName, @@ -545,12 +627,18 @@ export function formatEntityRefTitle( }, ): string; +// Warning: (ae-missing-release-tag) "getEntityMetadataEditUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getEntityMetadataEditUrl(entity: Entity): string | undefined; +// Warning: (ae-missing-release-tag) "getEntityMetadataViewUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getEntityMetadataViewUrl(entity: Entity): string | undefined; +// Warning: (ae-missing-release-tag) "getEntityRelations" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function getEntityRelations( entity: Entity | undefined, @@ -560,15 +648,21 @@ export function getEntityRelations( }, ): EntityName[]; +// Warning: (ae-missing-release-tag) "getEntitySourceLocation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getEntitySourceLocation( entity: Entity, scmIntegrationsApi: ScmIntegrationRegistry, ): EntitySourceLocation | undefined; +// Warning: (ae-missing-release-tag) "isOwnerOf" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function isOwnerOf(owner: Entity, owned: Entity): boolean; +// Warning: (ae-missing-release-tag) "MockEntityListContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const MockEntityListContextProvider: ({ children, @@ -577,26 +671,37 @@ export const MockEntityListContextProvider: ({ value: Partial; }>) => JSX.Element; +// Warning: (ae-missing-release-tag) "reduceCatalogFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function reduceCatalogFilters( filters: EntityFilter[], ): Record; +// Warning: (ae-missing-release-tag) "reduceEntityFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function reduceEntityFilters( filters: EntityFilter[], ): (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "rootRoute" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const rootRoute: RouteRef; +// Warning: (ae-missing-release-tag) "useEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function useEntity(): { entity: T; loading: boolean; error: Error | undefined; + refresh: VoidFunction | undefined; }; +// Warning: (ae-missing-release-tag) "useEntityCompoundName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const useEntityCompoundName: () => { kind: string; @@ -604,20 +709,31 @@ export const useEntityCompoundName: () => { name: string; }; +// Warning: (ae-missing-release-tag) "useEntityFromUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const useEntityFromUrl: () => EntityLoadingStatus; +// Warning: (ae-missing-release-tag) "useEntityListProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useEntityListProvider< EntityFilters extends DefaultEntityFilters = DefaultEntityFilters >(): EntityListContextProps; +// Warning: (ae-forgotten-export) The symbol "EntityTypeReturn" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "useEntityTypeFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function useEntityTypeFilter(): EntityTypeReturn; +// Warning: (ae-missing-release-tag) "useOwnUser" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function useOwnUser(): AsyncState; +// Warning: (ae-missing-release-tag) "useRelatedEntities" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function useRelatedEntities( entity: Entity, @@ -634,6 +750,8 @@ export function useRelatedEntities( error: Error | undefined; }; +// Warning: (ae-missing-release-tag) "UserListFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class UserListFilter implements EntityFilter { constructor( @@ -653,15 +771,22 @@ export class UserListFilter implements EntityFilter { readonly value: UserListFilterKind; } +// Warning: (ae-missing-release-tag) "UserListFilterKind" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type UserListFilterKind = 'owned' | 'starred' | 'all'; +// Warning: (ae-forgotten-export) The symbol "UserListPickerProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "UserListPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserListPicker: ({ initialFilter, availableFilters, }: UserListPickerProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "useStarredEntities" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const useStarredEntities: () => { starredEntities: Set; @@ -669,5 +794,16 @@ export const useStarredEntities: () => { isStarredEntity: (entity: Entity) => boolean; }; +// Warnings were encountered during analysis: +// +// src/types.d.ts:6:49 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/types.d.ts:6:10 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/types.d.ts:7:75 - (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// src/types.d.ts:7:10 - (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// src/types.d.ts:15:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/types.d.ts:16:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/types.d.ts:22:68 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag +// src/types.d.ts:22:88 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index c65e06df29..d0cb5ac947 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "0.2.6", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,10 +28,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", - "@backstage/core-app-api": "^0.1.4", - "@backstage/core-components": "^0.1.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/integration": "^0.5.8", "@material-ui/core": "^4.11.0", @@ -46,9 +46,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/catalog-react/src/hooks/useEntity.ts b/plugins/catalog-react/src/hooks/useEntity.ts index 465cffcf9b..6fe25fb115 100644 --- a/plugins/catalog-react/src/hooks/useEntity.ts +++ b/plugins/catalog-react/src/hooks/useEntity.ts @@ -17,7 +17,7 @@ import { Entity } from '@backstage/catalog-model'; import { errorApiRef, useApi } from '@backstage/core-plugin-api'; import { createContext, useContext, useEffect } from 'react'; import { useNavigate } from 'react-router'; -import { useAsync } from 'react-use'; +import { useAsyncRetry } from 'react-use'; import { catalogApiRef } from '../api'; import { useEntityCompoundName } from './useEntityCompoundName'; @@ -25,12 +25,14 @@ type EntityLoadingStatus = { entity?: Entity; loading: boolean; error?: Error; + refresh?: VoidFunction; }; export const EntityContext = createContext({ entity: undefined, loading: true, error: undefined, + refresh: () => {}, }); export const useEntityFromUrl = (): EntityLoadingStatus => { @@ -39,7 +41,7 @@ export const useEntityFromUrl = (): EntityLoadingStatus => { const errorApi = useApi(errorApiRef); const catalogApi = useApi(catalogApiRef); - const { value: entity, error, loading } = useAsync( + const { value: entity, error, loading, retry: refresh } = useAsyncRetry( () => catalogApi.getEntityByName({ kind, namespace, name }), [catalogApi, kind, namespace, name], ); @@ -51,13 +53,13 @@ export const useEntityFromUrl = (): EntityLoadingStatus => { } }, [errorApi, navigate, error, loading, entity, name]); - return { entity, loading, error }; + return { entity, loading, error, refresh }; }; /** * Grab the current entity from the context and its current loading state. */ export function useEntity() { - const { entity, loading, error } = useContext(EntityContext); - return { entity: entity as T, loading, error }; + const { entity, loading, error, refresh } = useContext(EntityContext); + return { entity: entity as T, loading, error, refresh }; } diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index 8ca3efd243..2506b55d6d 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -25,7 +25,7 @@ import React, { useState, } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useAsyncFn, useDebounce } from 'react-use'; +import { useAsyncFn, useDebounce, useMountedState } from 'react-use'; import { catalogApiRef } from '../api'; import { EntityKindFilter, @@ -102,6 +102,7 @@ type OutputState = { export const EntityListProvider = ({ children, }: PropsWithChildren<{}>) => { + const isMounted = useMountedState(); const catalogApi = useApi(catalogApiRef); const [searchParams, setSearchParams] = useSearchParams(); const allQueryParams = qs.parse(searchParams.toString()); @@ -164,12 +165,14 @@ export const EntityListProvider = ({ }); } - setSearchParams( - qs.stringify({ ...allQueryParams, filters: queryParams }), - { - replace: true, - }, - ); + if (isMounted()) { + setSearchParams( + qs.stringify({ ...allQueryParams, filters: queryParams }), + { + replace: true, + }, + ); + } }, [catalogApi, requestedFilters, outputState], { loading: true }, diff --git a/plugins/catalog-react/src/testUtils/providers.tsx b/plugins/catalog-react/src/testUtils/providers.tsx index 3639715da2..1f9e4f40d6 100644 --- a/plugins/catalog-react/src/testUtils/providers.tsx +++ b/plugins/catalog-react/src/testUtils/providers.tsx @@ -52,8 +52,8 @@ export const MockEntityListContextProvider = ({ const defaultContext: EntityListContextProps = { entities: [], backendEntities: [], - updateFilters: updateFilters, - filters: filters, + updateFilters, + filters, loading: false, queryParameters: {}, }; diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 0faa012cf3..295c1fe15c 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-catalog +## 0.6.8 + +### Patch Changes + +- 221d7d060: added retry callback to useEntity hook +- 45b5fc3a8: Updated the layout of catalog and API index pages to handle smaller screen sizes. This adds responsive wrappers to the entity tables, and switches filters to a drawer when width-constrained. If you have created a custom catalog or API index page, you will need to update the page structure to match the updated [catalog customization](https://backstage.io/docs/features/software-catalog/catalog-customization) documentation. +- 71c936eb6: Export `CatalogClientWrapper` class +- 03bf17e9b: Improve the responsiveness of the EntityPage UI. With this the Header component should scale with the screen size & wrapping should not cause overflowing/blocking of links. Additionally enforce the Pages using the Grid Layout to use it across all screen sizes & to wrap as intended. + + To benefit from the improved responsive layout, the `EntityPage` in existing Backstage applications should be updated to set the `xs` column size on each grid item in the page, as this does not default. For example: + + ```diff + - + + + ``` + +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/catalog-client@0.3.17 + - @backstage/plugin-catalog-react@0.3.1 + +## 0.6.7 + +### Patch Changes + +- 75a532fbe: Add unstable prop for disabling unregister entity menu +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.6.6 ### Patch Changes diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 86b2322246..a416e70038 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -5,12 +5,21 @@ ```ts /// +import { AddLocationRequest } from '@backstage/catalog-client'; +import { AddLocationResponse } from '@backstage/catalog-client'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { CatalogApi } from '@backstage/catalog-client'; +import { CatalogClient } from '@backstage/catalog-client'; +import { CatalogEntitiesRequest } from '@backstage/catalog-client'; +import { CatalogListResponse } from '@backstage/catalog-client'; +import { CatalogRequestOptions } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; +import { Location as Location_2 } from '@backstage/catalog-model'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; @@ -20,33 +29,93 @@ import { TableProps } from '@backstage/core-components'; import { TabProps } from '@material-ui/core'; import { UserListFilterKind } from '@backstage/plugin-catalog-react'; +// Warning: (ae-forgotten-export) The symbol "AboutCardProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AboutCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function AboutCard({ variant }: AboutCardProps): JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AboutContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) -export const AboutContent: ({ entity }: Props_2) => JSX.Element; +export const AboutContent: ({ entity }: Props) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AboutField" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const AboutField: ({ label, value, gridSizes, children, -}: Props_3) => JSX.Element; +}: Props_2) => JSX.Element; +// Warning: (ae-missing-release-tag) "CatalogClientWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class CatalogClientWrapper implements CatalogApi { + constructor(options: { client: CatalogClient; identityApi: IdentityApi }); + // (undocumented) + addLocation( + request: AddLocationRequest, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getEntities( + request?: CatalogEntitiesRequest, + options?: CatalogRequestOptions, + ): Promise>; + // (undocumented) + getEntityByName( + compoundName: EntityName, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getLocationByEntity( + entity: Entity, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getLocationById( + id: string, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getOriginLocationByEntity( + entity: Entity, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + removeEntityByUid( + uid: string, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + removeLocationById( + id: string, + options?: CatalogRequestOptions, + ): Promise; +} + +// Warning: (ae-missing-release-tag) "CatalogEntityPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CatalogEntityPage: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "CatalogPageProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "CatalogIndexPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CatalogIndexPage: ({ - initiallySelectedFilter, columns, actions, + initiallySelectedFilter, }: CatalogPageProps) => JSX.Element; -// @public (undocumented) -export const CatalogLayout: ({ children }: Props) => JSX.Element; - +// Warning: (ae-missing-release-tag) "catalogPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const catalogPlugin: BackstagePlugin< { @@ -64,15 +133,21 @@ const catalogPlugin: BackstagePlugin< export { catalogPlugin }; export { catalogPlugin as plugin }; +// Warning: (ae-missing-release-tag) "CatalogResultListItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CatalogResultListItem: ({ result }: any) => JSX.Element; +// Warning: (ae-missing-release-tag) "CatalogTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CatalogTable: { ({ columns, actions }: CatalogTableProps): JSX.Element; columns: typeof columnFactories; }; +// Warning: (ae-missing-release-tag) "EntityRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CatalogTableRow = { entity: Entity; @@ -85,35 +160,56 @@ export type CatalogTableRow = { }; }; +// Warning: (ae-missing-release-tag) "CreateComponentButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CreateComponentButton: () => JSX.Element | null; +// Warning: (ae-missing-release-tag) "createMetadataDescriptionColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createMetadataDescriptionColumn(): TableColumn; +// Warning: (ae-forgotten-export) The symbol "NameColumnProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createNameColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createNameColumn( props?: NameColumnProps, ): TableColumn; +// Warning: (ae-missing-release-tag) "createOwnerColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createOwnerColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createSpecLifecycleColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createSpecLifecycleColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createSpecTypeColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createSpecTypeColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createSystemColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createSystemColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createTagsColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createTagsColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "EntityAboutCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityAboutCard: AboutCard; +// Warning: (ae-missing-release-tag) "EntityDependencyOfComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityDependencyOfComponentsCard: ({ variant, @@ -123,6 +219,8 @@ export const EntityDependencyOfComponentsCard: ({ title?: string | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityDependsOnComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityDependsOnComponentsCard: ({ variant, @@ -132,6 +230,8 @@ export const EntityDependsOnComponentsCard: ({ title?: string | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityDependsOnResourcesCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityDependsOnResourcesCard: ({ variant, @@ -139,6 +239,8 @@ export const EntityDependsOnResourcesCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityHasComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityHasComponentsCard: ({ variant, @@ -146,6 +248,8 @@ export const EntityHasComponentsCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityHasResourcesCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityHasResourcesCard: ({ variant, @@ -153,6 +257,8 @@ export const EntityHasResourcesCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityHasSubcomponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityHasSubcomponentsCard: ({ variant, @@ -160,6 +266,8 @@ export const EntityHasSubcomponentsCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityHasSystemsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityHasSystemsCard: ({ variant, @@ -167,6 +275,8 @@ export const EntityHasSystemsCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const EntityLayout: { ({ @@ -177,6 +287,8 @@ export const EntityLayout: { Route: (props: SubRoute) => null; }; +// Warning: (ae-missing-release-tag) "EntityLinksCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLinksCard: ({ cols, @@ -187,9 +299,20 @@ export const EntityLinksCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityListContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const EntityListContainer: ({ + children, +}: PropsWithChildren<{}>) => JSX.Element; + +// Warning: (ae-missing-release-tag) "EntityOrphanWarning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const EntityOrphanWarning: () => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityPageLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityPageLayout: { ({ @@ -204,6 +327,8 @@ export const EntityPageLayout: { }) => null; }; +// Warning: (ae-missing-release-tag) "EntitySwitch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySwitch: { ({ children }: PropsWithChildren<{}>): JSX.Element | null; @@ -213,21 +338,48 @@ export const EntitySwitch: { }) => null; }; +// Warning: (ae-forgotten-export) The symbol "SystemDiagramCard" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntitySystemDiagramCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySystemDiagramCard: SystemDiagramCard; +// Warning: (ae-missing-release-tag) "FilterContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const FilterContainer: ({ + children, +}: PropsWithChildren<{}>) => JSX.Element; + +// Warning: (ae-missing-release-tag) "FilteredEntityLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const FilteredEntityLayout: ({ + children, +}: PropsWithChildren<{}>) => JSX.Element; + +// Warning: (ae-missing-release-tag) "isComponentType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function isComponentType(type: string): (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "isKind" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function isKind(kind: string): (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "isNamespace" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function isNamespace(namespace: string): (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "isOrphan" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const isOrphan: (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: ({ EntityPage, @@ -235,5 +387,14 @@ export const Router: ({ EntityPage?: React_2.ComponentType<{}> | undefined; }) => JSX.Element; +// Warnings were encountered during analysis: +// +// src/components/CatalogTable/CatalogTable.d.ts:10:5 - (ae-forgotten-export) The symbol "CatalogTableProps" needs to be exported by the entry point index.d.ts +// src/components/CatalogTable/CatalogTable.d.ts:11:5 - (ae-forgotten-export) The symbol "columnFactories" needs to be exported by the entry point index.d.ts +// src/components/EntityLayout/EntityLayout.d.ts:43:5 - (ae-forgotten-export) The symbol "EntityLayoutProps" needs to be exported by the entry point index.d.ts +// src/components/EntityLayout/EntityLayout.d.ts:44:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts +// src/components/EntityPageLayout/EntityPageLayout.d.ts:17:5 - (ae-forgotten-export) The symbol "EntityPageLayoutProps" needs to be exported by the entry point index.d.ts +// src/plugin.d.ts:17:5 - (ae-forgotten-export) The symbol "ColumnBreakpoints" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 2c5065755d..821e029616 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.6.6", + "version": "0.6.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,14 +30,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.1", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -54,10 +54,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/catalog/src/CatalogClientWrapper.ts b/plugins/catalog/src/CatalogClientWrapper.ts index a10b968216..a46edb56b7 100644 --- a/plugins/catalog/src/CatalogClientWrapper.ts +++ b/plugins/catalog/src/CatalogClientWrapper.ts @@ -19,16 +19,13 @@ import { AddLocationRequest, AddLocationResponse, CatalogApi, + CatalogClient, CatalogEntitiesRequest, CatalogListResponse, - CatalogClient, + CatalogRequestOptions, } from '@backstage/catalog-client'; import { IdentityApi } from '@backstage/core-plugin-api'; -type CatalogRequestOptions = { - token?: string; -}; - /** * CatalogClient wrapper that injects identity token for all requests */ diff --git a/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx b/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx deleted file mode 100644 index 2471ac9df2..0000000000 --- a/plugins/catalog/src/components/CatalogPage/CatalogLayout.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 React from 'react'; - -import { configApiRef, useApi } from '@backstage/core-plugin-api'; -import { Header, Page } from '@backstage/core-components'; - -type Props = { - children?: React.ReactNode; -}; - -export const CatalogLayout = ({ children }: Props) => { - const orgName = - useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage'; - - return ( - -
- {children} - - ); -}; - -export default CatalogLayout; diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx index 584eea0850..e09aec55e7 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx @@ -26,6 +26,7 @@ import { MockStorageApi, renderWithEffects, wrapInTestApp, + mockBreakpoint, } from '@backstage/test-utils'; import { fireEvent, screen } from '@testing-library/react'; import React from 'react'; @@ -244,4 +245,13 @@ describe('CatalogPage', () => { screen.findByText(/Starred \(1\)/), ).resolves.toBeInTheDocument(); }); + + it('should wrap filter in drawer on smaller screens', async () => { + mockBreakpoint({ matches: true }); + const { getByRole } = await renderWrapped(); + const button = getByRole('button', { name: 'Filters' }); + expect(getByRole('presentation', { hidden: true })).toBeInTheDocument(); + fireEvent.click(button); + expect(getByRole('presentation')).toBeVisible(); + }); }); diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 089e61f0fb..963e04e5e2 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -14,8 +14,15 @@ * limitations under the License. */ -import React from 'react'; -import { Grid } from '@material-ui/core'; +import { + Content, + ContentHeader, + PageWithHeader, + SupportButton, + TableColumn, + TableProps, +} from '@backstage/core-components'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { EntityKindPicker, EntityLifecyclePicker, @@ -26,18 +33,15 @@ import { UserListFilterKind, UserListPicker, } from '@backstage/plugin-catalog-react'; +import React from 'react'; import { CatalogTable } from '../CatalogTable'; - import { EntityRow } from '../CatalogTable/types'; -import CatalogLayout from './CatalogLayout'; import { CreateComponentButton } from '../CreateComponentButton'; import { - Content, - ContentHeader, - SupportButton, - TableColumn, - TableProps, -} from '@backstage/core-components'; + FilteredEntityLayout, + EntityListContainer, + FilterContainer, +} from '../FilteredEntityLayout'; export type CatalogPageProps = { initiallySelectedFilter?: UserListFilterKind; @@ -46,39 +50,36 @@ export type CatalogPageProps = { }; export const CatalogPage = ({ - initiallySelectedFilter = 'owned', columns, actions, -}: CatalogPageProps) => ( - - - - - All your software catalog entities - - + initiallySelectedFilter = 'owned', +}: CatalogPageProps) => { + const orgName = + useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage'; + + return ( + + + + + All your software catalog entities + - - - - - - - - - - - - - - - - - + + + + + + + - - - -); + + + ); +}; diff --git a/plugins/catalog/src/components/CatalogPage/index.ts b/plugins/catalog/src/components/CatalogPage/index.ts index 66c3c2f4b3..dc7ed68229 100644 --- a/plugins/catalog/src/components/CatalogPage/index.ts +++ b/plugins/catalog/src/components/CatalogPage/index.ts @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { CatalogLayout } from './CatalogLayout'; export { CatalogPage } from './CatalogPage'; diff --git a/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx b/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx index 482a0c0a3c..ae2fc4b718 100644 --- a/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx +++ b/plugins/catalog/src/components/CreateComponentButton/CreateComponentButton.tsx @@ -23,9 +23,7 @@ import { useRouteRef } from '@backstage/core-plugin-api'; export const CreateComponentButton = () => { const createComponentLink = useRouteRef(createComponentRouteRef); - if (!createComponentLink) return null; - - return ( + return createComponentLink ? ( - ); + ) : null; }; diff --git a/plugins/catalog/src/components/CreateComponentButton/index.ts b/plugins/catalog/src/components/CreateComponentButton/index.ts index e525bef782..edf59d18a6 100644 --- a/plugins/catalog/src/components/CreateComponentButton/index.ts +++ b/plugins/catalog/src/components/CreateComponentButton/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + export { CreateComponentButton } from './CreateComponentButton'; diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx index 9cb0b7c961..a5b85082cc 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx @@ -20,9 +20,17 @@ import { RELATION_OWNED_BY, } from '@backstage/catalog-model'; import { - useElementFilter, + Content, + Header, + HeaderLabel, + Page, + Progress, + RoutedTabs, +} from '@backstage/core-components'; +import { attachComponentData, IconComponent, + useElementFilter, } from '@backstage/core-plugin-api'; import { EntityContext, @@ -37,14 +45,6 @@ import { useNavigate } from 'react-router'; import { EntityContextMenu } from '../EntityContextMenu/EntityContextMenu'; import { FavouriteEntity } from '../FavouriteEntity/FavouriteEntity'; import { UnregisterEntityDialog } from '../UnregisterEntityDialog/UnregisterEntityDialog'; -import { - Content, - Header, - HeaderLabel, - Page, - Progress, - RoutedTabs, -} from '@backstage/core-components'; type SubRoute = { path: string; @@ -68,12 +68,21 @@ const EntityLayoutTitle = ({ }: { title: string; entity: Entity | undefined; -}) => ( - - {title} - {entity && } - -); +}) => { + return ( + + + {title} + + {entity && } + + ); +}; const headerProps = ( paramKind: string | undefined, diff --git a/plugins/catalog/src/components/EntityLoaderProvider/EntityLoaderProvider.tsx b/plugins/catalog/src/components/EntityLoaderProvider/EntityLoaderProvider.tsx index 7f9f728161..8d2feae7da 100644 --- a/plugins/catalog/src/components/EntityLoaderProvider/EntityLoaderProvider.tsx +++ b/plugins/catalog/src/components/EntityLoaderProvider/EntityLoaderProvider.tsx @@ -20,10 +20,10 @@ import { import React, { ReactNode } from 'react'; export const EntityLoaderProvider = ({ children }: { children: ReactNode }) => { - const { entity, loading, error } = useEntityFromUrl(); + const { entity, loading, error, refresh } = useEntityFromUrl(); return ( - + {children} ); diff --git a/plugins/catalog/src/components/FilteredEntityLayout/EntityListContainer.tsx b/plugins/catalog/src/components/FilteredEntityLayout/EntityListContainer.tsx new file mode 100644 index 0000000000..a39ef76b87 --- /dev/null +++ b/plugins/catalog/src/components/FilteredEntityLayout/EntityListContainer.tsx @@ -0,0 +1,24 @@ +/* + * 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. + */ + +import { Grid } from '@material-ui/core'; +import React, { PropsWithChildren } from 'react'; + +export const EntityListContainer = ({ children }: PropsWithChildren<{}>) => ( + + {children} + +); diff --git a/plugins/catalog/src/components/FilteredEntityLayout/FilterContainer.tsx b/plugins/catalog/src/components/FilteredEntityLayout/FilterContainer.tsx new file mode 100644 index 0000000000..683dbf8b40 --- /dev/null +++ b/plugins/catalog/src/components/FilteredEntityLayout/FilterContainer.tsx @@ -0,0 +1,71 @@ +/* + * 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. + */ + +import { BackstageTheme } from '@backstage/theme'; +import { + Box, + Button, + Drawer, + Grid, + Typography, + useMediaQuery, + useTheme, +} from '@material-ui/core'; +import FilterListIcon from '@material-ui/icons/FilterList'; +import React, { useState, PropsWithChildren } from 'react'; + +export const FilterContainer = ({ children }: PropsWithChildren<{}>) => { + const isMidSizeScreen = useMediaQuery(theme => + theme.breakpoints.down('md'), + ); + const theme = useTheme(); + const [filterDrawerOpen, setFilterDrawerOpen] = useState(false); + + return isMidSizeScreen ? ( + <> + + setFilterDrawerOpen(false)} + anchor="left" + disableAutoFocus + keepMounted + variant="temporary" + > + + + Filters + + {children} + + + + ) : ( + + {children} + + ); +}; diff --git a/plugins/catalog/src/components/FilteredEntityLayout/FilteredEntityLayout.tsx b/plugins/catalog/src/components/FilteredEntityLayout/FilteredEntityLayout.tsx new file mode 100644 index 0000000000..628ad9fee6 --- /dev/null +++ b/plugins/catalog/src/components/FilteredEntityLayout/FilteredEntityLayout.tsx @@ -0,0 +1,24 @@ +/* + * 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. + */ + +import { Grid } from '@material-ui/core'; +import React, { PropsWithChildren } from 'react'; + +export const FilteredEntityLayout = ({ children }: PropsWithChildren<{}>) => ( + + {children} + +); diff --git a/plugins/catalog/src/components/FilteredEntityLayout/index.ts b/plugins/catalog/src/components/FilteredEntityLayout/index.ts new file mode 100644 index 0000000000..ded61a4edc --- /dev/null +++ b/plugins/catalog/src/components/FilteredEntityLayout/index.ts @@ -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. + */ + +export { FilteredEntityLayout } from './FilteredEntityLayout'; +export { FilterContainer } from './FilterContainer'; +export { EntityListContainer } from './EntityListContainer'; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index f2f28679a5..215a18f661 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -14,16 +14,18 @@ * limitations under the License. */ +export { CatalogClientWrapper } from './CatalogClientWrapper'; export * from './components/AboutCard'; -export { CatalogLayout } from './components/CatalogPage'; -export { CatalogResultListItem } from './components/CatalogResultListItem'; +export * from './components/CatalogResultListItem'; export { CatalogTable } from './components/CatalogTable'; export type { EntityRow as CatalogTableRow } from './components/CatalogTable'; -export { CreateComponentButton } from './components/CreateComponentButton'; -export { EntityLayout } from './components/EntityLayout'; +export * from './components/CatalogTable/columns'; +export * from './components/CreateComponentButton'; +export * from './components/EntityLayout'; export * from './components/EntityOrphanWarning'; -export { EntityPageLayout } from './components/EntityPageLayout'; +export * from './components/EntityPageLayout'; export * from './components/EntitySwitch'; +export * from './components/FilteredEntityLayout'; export { Router } from './components/Router'; export { CatalogEntityPage, @@ -31,8 +33,8 @@ export { catalogPlugin, catalogPlugin as plugin, EntityAboutCard, - EntityDependsOnComponentsCard, EntityDependencyOfComponentsCard, + EntityDependsOnComponentsCard, EntityDependsOnResourcesCard, EntityHasComponentsCard, EntityHasResourcesCard, @@ -41,4 +43,3 @@ export { EntityLinksCard, EntitySystemDiagramCard, } from './plugin'; -export * from './components/CatalogTable/columns'; diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index 7dbaea83e4..015bd04ca8 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-circleci +## 0.2.19 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/circleci/api-report.md b/plugins/circleci/api-report.md index 7bb1b7766a..76426d8385 100644 --- a/plugins/circleci/api-report.md +++ b/plugins/circleci/api-report.md @@ -24,11 +24,16 @@ export { BuildSummary }; export { BuildWithSteps }; +// Warning: (ae-missing-release-tag) "CIRCLECI_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CIRCLECI_ANNOTATION = 'circleci.com/project-slug'; +// Warning: (ae-missing-release-tag) "CircleCIApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CircleCIApi { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options); // (undocumented) getBuild( @@ -55,20 +60,30 @@ export class CircleCIApi { ): Promise; } +// Warning: (ae-missing-release-tag) "circleCIApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const circleCIApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "circleCIBuildRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const circleCIBuildRouteRef: RouteRef; +// Warning: (ae-missing-release-tag) "circleCIPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const circleCIPlugin: BackstagePlugin<{}, {}>; export { circleCIPlugin }; export { circleCIPlugin as plugin }; +// Warning: (ae-missing-release-tag) "circleCIRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const circleCIRouteRef: RouteRef; +// Warning: (ae-missing-release-tag) "EntityCircleCIContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityCircleCIContent: (_props: { entity?: Entity | undefined; @@ -76,11 +91,16 @@ export const EntityCircleCIContent: (_props: { export { GitType }; +// Warning: (ae-missing-release-tag) "isCircleCIAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isCircleCIAvailable: (entity: Entity) => boolean; export { isCircleCIAvailable }; export { isCircleCIAvailable as isPluginApplicableToEntity }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props) => JSX.Element; diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index f03debe9ac..52aae08b57 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-circleci", - "version": "0.2.18", + "version": "0.2.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -50,10 +50,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index 4e837d532b..d0c5a647d8 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-cloudbuild +## 0.2.19 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.18 ### Patch Changes diff --git a/plugins/cloudbuild/api-report.md b/plugins/cloudbuild/api-report.md index 8c2d7876b2..8c0ee26539 100644 --- a/plugins/cloudbuild/api-report.md +++ b/plugins/cloudbuild/api-report.md @@ -11,6 +11,8 @@ import { Entity } from '@backstage/catalog-model'; import { OAuthApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "ActionsGetWorkflowResponseData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ActionsGetWorkflowResponseData = { id: string; @@ -35,12 +37,16 @@ export type ActionsGetWorkflowResponseData = { timing: Timing2; }; +// Warning: (ae-missing-release-tag) "ActionsListWorkflowRunsForRepoResponseData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ActionsListWorkflowRunsForRepoResponseData { // (undocumented) builds: ActionsGetWorkflowResponseData[]; } +// Warning: (ae-missing-release-tag) "BUILD" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BUILD { // (undocumented) @@ -49,9 +55,13 @@ export interface BUILD { startTime: string; } +// Warning: (ae-missing-release-tag) "CLOUDBUILD_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CLOUDBUILD_ANNOTATION = 'google.com/cloudbuild-project-slug'; +// Warning: (ae-missing-release-tag) "CloudbuildApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CloudbuildApi = { listWorkflowRuns: (request: { @@ -80,9 +90,13 @@ export type CloudbuildApi = { }) => Promise; }; +// Warning: (ae-missing-release-tag) "cloudbuildApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const cloudbuildApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "CloudbuildClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class CloudbuildClient implements CloudbuildApi { constructor(googleAuthApi: OAuthApi); @@ -120,6 +134,8 @@ export class CloudbuildClient implements CloudbuildApi { }): Promise; } +// Warning: (ae-missing-release-tag) "cloudbuildPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const cloudbuildPlugin: BackstagePlugin< { @@ -130,11 +146,15 @@ const cloudbuildPlugin: BackstagePlugin< export { cloudbuildPlugin }; export { cloudbuildPlugin as plugin }; +// Warning: (ae-missing-release-tag) "EntityCloudbuildContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityCloudbuildContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLatestCloudbuildRunCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLatestCloudbuildRunCard: ({ branch, @@ -143,6 +163,8 @@ export const EntityLatestCloudbuildRunCard: ({ branch: string; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLatestCloudbuildsForBranchCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLatestCloudbuildsForBranchCard: ({ branch, @@ -151,6 +173,8 @@ export const EntityLatestCloudbuildsForBranchCard: ({ branch: string; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "FETCHSOURCE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface FETCHSOURCE { // (undocumented) @@ -159,11 +183,15 @@ export interface FETCHSOURCE { startTime: string; } +// Warning: (ae-missing-release-tag) "isCloudbuildAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isCloudbuildAvailable: (entity: Entity) => boolean; export { isCloudbuildAvailable }; export { isCloudbuildAvailable as isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "LatestWorkflowRunCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LatestWorkflowRunCard: ({ branch, @@ -172,6 +200,8 @@ export const LatestWorkflowRunCard: ({ branch: string; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "LatestWorkflowsForBranchCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LatestWorkflowsForBranchCard: ({ branch, @@ -180,6 +210,8 @@ export const LatestWorkflowsForBranchCard: ({ branch: string; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "Options" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Options { // (undocumented) @@ -192,6 +224,8 @@ export interface Options { substitutionOption: string; } +// Warning: (ae-missing-release-tag) "PullTiming" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface PullTiming { // (undocumented) @@ -200,6 +234,8 @@ export interface PullTiming { startTime: string; } +// Warning: (ae-missing-release-tag) "ResolvedStorageSource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ResolvedStorageSource { // (undocumented) @@ -210,6 +246,8 @@ export interface ResolvedStorageSource { object: string; } +// Warning: (ae-missing-release-tag) "Results" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Results { // (undocumented) @@ -218,15 +256,22 @@ export interface Results { buildStepOutputs: string[]; } +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "Source" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Source { // (undocumented) storageSource: StorageSource; } +// Warning: (ae-missing-release-tag) "SourceProvenance" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SourceProvenance { // (undocumented) @@ -235,6 +280,8 @@ export interface SourceProvenance { resolvedStorageSource: {}; } +// Warning: (ae-missing-release-tag) "Step" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Step { // (undocumented) @@ -259,6 +306,8 @@ export interface Step { waitFor: string[]; } +// Warning: (ae-missing-release-tag) "StorageSource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface StorageSource { // (undocumented) @@ -267,6 +316,8 @@ export interface StorageSource { object: string; } +// Warning: (ae-missing-release-tag) "Substitutions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Substitutions { // (undocumented) @@ -281,6 +332,8 @@ export interface Substitutions { SHORT_SHA: string; } +// Warning: (ae-missing-release-tag) "Timing" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Timing { // (undocumented) @@ -289,6 +342,8 @@ export interface Timing { startTime: string; } +// Warning: (ae-missing-release-tag) "Timing2" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Timing2 { // (undocumented) @@ -297,6 +352,8 @@ export interface Timing2 { FETCHSOURCE: FETCHSOURCE; } +// Warning: (ae-missing-release-tag) "Volume" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Volume { // (undocumented) diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 4fa32a8200..ee9f8a4e0c 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cloudbuild", - "version": "0.2.18", + "version": "0.2.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/code-coverage-backend/api-report.md b/plugins/code-coverage-backend/api-report.md index fb7e844f13..8d92686f2c 100644 --- a/plugins/code-coverage-backend/api-report.md +++ b/plugins/code-coverage-backend/api-report.md @@ -10,18 +10,26 @@ import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; +// Warning: (ae-missing-release-tag) "CodeCoverageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CodeCoverageApi { // (undocumented) name: string; } +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "makeRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const makeRouter: (options: RouterOptions) => Promise; +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index 4064ba3f1a..a0b44e590f 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-code-coverage +## 0.1.7 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.1.6 ### Patch Changes diff --git a/plugins/code-coverage/api-report.md b/plugins/code-coverage/api-report.md index cd69066d4f..19c1a8b1d5 100644 --- a/plugins/code-coverage/api-report.md +++ b/plugins/code-coverage/api-report.md @@ -9,6 +9,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "codeCoveragePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const codeCoveragePlugin: BackstagePlugin< { @@ -17,14 +19,20 @@ export const codeCoveragePlugin: BackstagePlugin< {} >; +// Warning: (ae-missing-release-tag) "EntityCodeCoverageContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityCodeCoverageContent: () => JSX.Element; +// Warning: (ae-missing-release-tag) "isCodeCoverageAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isCodeCoverageAvailable: (entity: Entity) => boolean; export { isCodeCoverageAvailable }; export { isCodeCoverageAvailable as isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: () => JSX.Element; diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 2a4f7f4bf6..bd632972df 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-code-coverage", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,10 +22,10 @@ "dependencies": { "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.4", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,10 +40,10 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/config-schema/api-report.md b/plugins/config-schema/api-report.md index e01cc67888..6b0ef9a964 100644 --- a/plugins/config-schema/api-report.md +++ b/plugins/config-schema/api-report.md @@ -11,18 +11,28 @@ import { Observable } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; import { Schema } from 'jsonschema'; +// Warning: (ae-missing-release-tag) "ConfigSchemaApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ConfigSchemaApi { + // Warning: (ae-forgotten-export) The symbol "ConfigSchemaResult" needs to be exported by the entry point index.d.ts + // // (undocumented) schema$(): Observable; } +// Warning: (ae-missing-release-tag) "configSchemaApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const configSchemaApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "ConfigSchemaPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ConfigSchemaPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "configSchemaPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const configSchemaPlugin: BackstagePlugin< { @@ -31,6 +41,8 @@ export const configSchemaPlugin: BackstagePlugin< {} >; +// Warning: (ae-missing-release-tag) "StaticSchemaLoader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class StaticSchemaLoader implements ConfigSchemaApi { constructor({ url }?: { url?: string }); diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index ae88de45b4..7ef279f123 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@backstage/config": "^0.1.4", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", "@backstage/theme": "^0.2.8", @@ -35,10 +35,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/cost-insights/api-report.md b/plugins/cost-insights/api-report.md index 2c32f09c53..541c2af9e0 100644 --- a/plugins/cost-insights/api-report.md +++ b/plugins/cost-insights/api-report.md @@ -22,6 +22,8 @@ import { SetStateAction } from 'react'; import { TooltipProps } from 'recharts'; import { TypographyProps } from '@material-ui/core'; +// Warning: (ae-missing-release-tag) "Alert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export type Alert = { title: string | JSX.Element; @@ -38,6 +40,8 @@ export type Alert = { onDismissed?(options: AlertOptions): Promise; }; +// Warning: (ae-missing-release-tag) "AlertCost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AlertCost { // (undocumented) @@ -46,6 +50,8 @@ export interface AlertCost { id: string; } +// Warning: (ae-missing-release-tag) "AlertDismissFormData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AlertDismissFormData { // (undocumented) @@ -56,6 +62,8 @@ export interface AlertDismissFormData { reason: AlertDismissReason; } +// Warning: (ae-missing-release-tag) "AlertDismissOption" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AlertDismissOption { // (undocumented) @@ -64,9 +72,13 @@ export interface AlertDismissOption { reason: string; } +// Warning: (ae-missing-release-tag) "AlertDismissOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const AlertDismissOptions: AlertDismissOption[]; +// Warning: (ae-missing-release-tag) "AlertDismissReason" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum AlertDismissReason { // (undocumented) @@ -83,6 +95,8 @@ export enum AlertDismissReason { Seasonal = 'seasonal', } +// Warning: (ae-missing-release-tag) "AlertForm" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AlertForm< A extends Alert = any, @@ -91,6 +105,8 @@ export type AlertForm< AlertFormProps & RefAttributes >; +// Warning: (ae-missing-release-tag) "AlertFormProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AlertFormProps = { alert: A; @@ -98,6 +114,8 @@ export type AlertFormProps = { disableSubmit: (isDisabled: boolean) => void; }; +// Warning: (ae-missing-release-tag) "AlertOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AlertOptions { // (undocumented) @@ -106,21 +124,29 @@ export interface AlertOptions { group: string; } +// Warning: (ae-missing-release-tag) "AlertSnoozeFormData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface AlertSnoozeFormData { // (undocumented) intervals: string; } +// Warning: (ae-missing-release-tag) "AlertSnoozeOption" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AlertSnoozeOption = { label: string; duration: Duration; }; +// Warning: (ae-missing-release-tag) "AlertSnoozeOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const AlertSnoozeOptions: AlertSnoozeOption[]; +// Warning: (ae-missing-release-tag) "AlertStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum AlertStatus { // (undocumented) @@ -131,6 +157,8 @@ export enum AlertStatus { Snoozed = 'snoozed', } +// Warning: (ae-missing-release-tag) "BarChart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const BarChart: ({ resources, @@ -142,9 +170,13 @@ export const BarChart: ({ onMouseMove, }: BarChartProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "BarChartData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface BarChartData extends BarChartOptions {} +// Warning: (ae-missing-release-tag) "BarChartLegend" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const BarChartLegend: ({ costStart, @@ -153,6 +185,8 @@ export const BarChartLegend: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "BarChartLegendOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BarChartLegendOptions = { previousName: string; @@ -162,6 +196,8 @@ export type BarChartLegendOptions = { hideMarker?: boolean; }; +// Warning: (ae-missing-release-tag) "BarChartLegendProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BarChartLegendProps = { costStart: number; @@ -169,6 +205,8 @@ export type BarChartLegendProps = { options?: Partial; }; +// Warning: (ae-missing-release-tag) "BarChartOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface BarChartOptions { // (undocumented) @@ -181,6 +219,8 @@ export interface BarChartOptions { previousName: string; } +// Warning: (ae-missing-release-tag) "BarChartProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BarChartProps = { resources: ResourceData[]; @@ -192,6 +232,8 @@ export type BarChartProps = { onMouseMove?: RechartsFunction; }; +// Warning: (ae-missing-release-tag) "BarChartTooltip" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const BarChartTooltip: ({ title, @@ -202,16 +244,22 @@ export const BarChartTooltip: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "BarChartTooltipItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const BarChartTooltipItem: ({ item, }: BarChartTooltipItemProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "BarChartTooltipItemProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BarChartTooltipItemProps = { item: TooltipItem; }; +// Warning: (ae-missing-release-tag) "BarChartTooltipProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BarChartTooltipProps = { title: string; @@ -221,6 +269,8 @@ export type BarChartTooltipProps = { actions?: ReactNode; }; +// Warning: (ae-missing-release-tag) "ChangeStatistic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ChangeStatistic { // (undocumented) @@ -229,6 +279,8 @@ export interface ChangeStatistic { ratio?: number; } +// Warning: (ae-missing-release-tag) "ChangeThreshold" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum ChangeThreshold { // (undocumented) @@ -237,6 +289,8 @@ export enum ChangeThreshold { upper = 0.05, } +// Warning: (ae-missing-release-tag) "ChartData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ChartData = { date: number; @@ -245,6 +299,8 @@ export type ChartData = { [key: string]: number; }; +// Warning: (ae-missing-release-tag) "Cost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Cost { // (undocumented) @@ -259,9 +315,13 @@ export interface Cost { trendline?: Trendline; } +// Warning: (ae-missing-release-tag) "CostGrowth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CostGrowth: ({ change, duration }: CostGrowthProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "CostGrowthIndicator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CostGrowthIndicator: ({ change, @@ -270,18 +330,24 @@ export const CostGrowthIndicator: ({ ...props }: CostGrowthIndicatorProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "CostGrowthIndicatorProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CostGrowthIndicatorProps = TypographyProps & { change: ChangeStatistic; formatter?: (change: ChangeStatistic) => Maybe; }; +// Warning: (ae-missing-release-tag) "CostGrowthProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CostGrowthProps = { change: ChangeStatistic; duration: Duration; }; +// Warning: (ae-missing-release-tag) "CostInsightsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CostInsightsApi = { getLastCompleteBillingDate(): Promise; @@ -294,23 +360,36 @@ export type CostInsightsApi = { getAlerts(group: string): Promise; }; +// Warning: (ae-missing-release-tag) "costInsightsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const costInsightsApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "CostInsightsLabelDataflowInstructionsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CostInsightsLabelDataflowInstructionsPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "CostInsightsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CostInsightsPage: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "CostInsightsPaletteAdditions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "CostInsightsPalette" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CostInsightsPalette = BackstagePalette & CostInsightsPaletteAdditions; +// Warning: (ae-missing-release-tag) "CostInsightsPaletteOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CostInsightsPaletteOptions = PaletteOptions & CostInsightsPaletteAdditions; +// Warning: (ae-missing-release-tag) "costInsightsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const costInsightsPlugin: BackstagePlugin< { @@ -323,21 +402,29 @@ const costInsightsPlugin: BackstagePlugin< export { costInsightsPlugin }; export { costInsightsPlugin as plugin }; +// Warning: (ae-missing-release-tag) "CostInsightsProjectGrowthInstructionsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const CostInsightsProjectGrowthInstructionsPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "CostInsightsTheme" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CostInsightsTheme extends BackstageTheme { // (undocumented) palette: CostInsightsPalette; } +// Warning: (ae-missing-release-tag) "CostInsightsThemeOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CostInsightsThemeOptions extends PaletteOptions { // (undocumented) palette: CostInsightsPaletteOptions; } +// Warning: (ae-missing-release-tag) "Currency" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Currency { // (undocumented) @@ -352,6 +439,8 @@ export interface Currency { unit: string; } +// Warning: (ae-missing-release-tag) "CurrencyType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum CurrencyType { // (undocumented) @@ -364,6 +453,8 @@ export enum CurrencyType { USD = 'USD', } +// Warning: (ae-missing-release-tag) "DataKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum DataKey { // (undocumented) @@ -374,15 +465,21 @@ export enum DataKey { Previous = 'previous', } +// Warning: (ae-missing-release-tag) "DateAggregation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type DateAggregation = { date: string; amount: number; }; +// Warning: (ae-missing-release-tag) "DEFAULT_DATE_FORMAT" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DEFAULT_DATE_FORMAT = 'yyyy-LL-dd'; +// Warning: (ae-missing-release-tag) "Duration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export enum Duration { // (undocumented) @@ -395,9 +492,13 @@ export enum Duration { P90D = 'P90D', } +// Warning: (ae-missing-release-tag) "EngineerThreshold" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EngineerThreshold = 0.5; +// Warning: (ae-missing-release-tag) "Entity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Entity { // (undocumented) @@ -410,6 +511,8 @@ export interface Entity { id: Maybe; } +// Warning: (ae-missing-release-tag) "ExampleCostInsightsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ExampleCostInsightsClient implements CostInsightsApi { // (undocumented) @@ -430,11 +533,15 @@ export class ExampleCostInsightsClient implements CostInsightsApi { getUserGroups(userId: string): Promise; } +// Warning: (ae-missing-release-tag) "Group" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Group = { id: string; }; +// Warning: (ae-missing-release-tag) "GrowthType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum GrowthType { // (undocumented) @@ -445,12 +552,16 @@ export enum GrowthType { Savings = 1, } +// Warning: (ae-missing-release-tag) "Icon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Icon = { kind: string; component: JSX.Element; }; +// Warning: (ae-missing-release-tag) "IconType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum IconType { // (undocumented) @@ -467,6 +578,8 @@ export enum IconType { Storage = 'storage', } +// Warning: (ae-missing-release-tag) "LegendItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LegendItem: ({ title, @@ -475,6 +588,8 @@ export const LegendItem: ({ children, }: PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "LegendItemProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LegendItemProps = { title: string; @@ -482,12 +597,18 @@ export type LegendItemProps = { markerColor?: string; }; +// Warning: (ae-missing-release-tag) "Loading" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Loading = Record; +// Warning: (ae-missing-release-tag) "Maybe" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Maybe = T | null; +// Warning: (ae-missing-release-tag) "Metric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Metric = { kind: string; @@ -495,6 +616,8 @@ export type Metric = { default: boolean; }; +// Warning: (ae-missing-release-tag) "MetricData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface MetricData { // (undocumented) @@ -507,18 +630,26 @@ export interface MetricData { id: string; } +// Warning: (ae-forgotten-export) The symbol "MockConfigProviderProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "MockConfigProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const MockConfigProvider: ({ children, ...context }: MockConfigProviderProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "MockCurrencyProviderProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "MockCurrencyProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const MockCurrencyProvider: ({ children, ...context }: MockCurrencyProviderProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "PageFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface PageFilters { // (undocumented) @@ -531,6 +662,8 @@ export interface PageFilters { project: Maybe; } +// Warning: (ae-missing-release-tag) "Product" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Product { // (undocumented) @@ -539,9 +672,13 @@ export interface Product { name: string; } +// Warning: (ae-missing-release-tag) "ProductFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ProductFilters = Array; +// Warning: (ae-missing-release-tag) "ProductInsightsOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ProductInsightsOptions = { product: string; @@ -550,6 +687,8 @@ export type ProductInsightsOptions = { project: Maybe; }; +// Warning: (ae-missing-release-tag) "ProductPeriod" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ProductPeriod { // (undocumented) @@ -558,6 +697,8 @@ export interface ProductPeriod { productType: string; } +// Warning: (ae-missing-release-tag) "Project" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Project { // (undocumented) @@ -566,6 +707,8 @@ export interface Project { name?: string; } +// Warning: (ae-missing-release-tag) "ProjectGrowthAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class ProjectGrowthAlert implements Alert { constructor(data: ProjectGrowthData); @@ -581,6 +724,8 @@ export class ProjectGrowthAlert implements Alert { get url(): string; } +// Warning: (ae-missing-release-tag) "ProjectGrowthData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ProjectGrowthData { // (undocumented) @@ -597,6 +742,8 @@ export interface ProjectGrowthData { project: string; } +// Warning: (ae-missing-release-tag) "ResourceData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ResourceData { // (undocumented) @@ -607,6 +754,8 @@ export interface ResourceData { previous: number; } +// Warning: (ae-missing-release-tag) "TooltipItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TooltipItem = { fill: string; @@ -614,12 +763,16 @@ export type TooltipItem = { value: string; }; +// Warning: (ae-missing-release-tag) "Trendline" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Trendline = { slope: number; intercept: number; }; +// Warning: (ae-missing-release-tag) "UnlabeledDataflowAlert" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class UnlabeledDataflowAlert implements Alert { constructor(data: UnlabeledDataflowData); @@ -637,6 +790,8 @@ export class UnlabeledDataflowAlert implements Alert { get url(): string; } +// Warning: (ae-missing-release-tag) "UnlabeledDataflowAlertProject" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface UnlabeledDataflowAlertProject { // (undocumented) @@ -647,6 +802,8 @@ export interface UnlabeledDataflowAlertProject { unlabeledCost: number; } +// Warning: (ae-missing-release-tag) "UnlabeledDataflowData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface UnlabeledDataflowData { // (undocumented) @@ -661,5 +818,17 @@ export interface UnlabeledDataflowData { unlabeledCost: number; } +// Warnings were encountered during analysis: +// +// src/api/CostInsightsApi.d.ts:34:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:42:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:56:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:57:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:73:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:74:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:83:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:84:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen +// src/api/CostInsightsApi.d.ts:100:8 - (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 13b9c9d85a..2f6fac1479 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -54,10 +54,10 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/explore-react/api-report.md b/plugins/explore-react/api-report.md index 530c6eedaf..06d7565559 100644 --- a/plugins/explore-react/api-report.md +++ b/plugins/explore-react/api-report.md @@ -5,6 +5,8 @@ ```ts import { ApiRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "ExploreTool" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ExploreTool = { title: string; @@ -15,12 +17,16 @@ export type ExploreTool = { lifecycle?: string; }; +// Warning: (ae-missing-release-tag) "ExploreToolsConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ExploreToolsConfig { // (undocumented) getTools: () => Promise; } +// Warning: (ae-missing-release-tag) "exploreToolsConfigRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const exploreToolsConfigRef: ApiRef; diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index 7049978f85..2cb7e849d3 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-explore +## 0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.3.9 ### Patch Changes diff --git a/plugins/explore/api-report.md b/plugins/explore/api-report.md index 4084c6a203..6ed2be6cde 100644 --- a/plugins/explore/api-report.md +++ b/plugins/explore/api-report.md @@ -12,6 +12,8 @@ import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; import { TabProps } from '@material-ui/core'; +// Warning: (ae-missing-release-tag) "catalogEntityRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const catalogEntityRouteRef: ExternalRouteRef< { name: string; @@ -19,20 +21,28 @@ kind: string; namespace: string; }, false>; +// Warning: (ae-missing-release-tag) "DomainExplorerContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DomainExplorerContent: ({ title, }: { title?: string | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "ExploreLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export const ExploreLayout: { ({ title, subtitle, children, }: ExploreLayoutProps): JSX.Element; Route: (props: SubRoute) => null; }; +// Warning: (ae-missing-release-tag) "ExplorePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ExplorePage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "explorePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const explorePlugin: BackstagePlugin< { explore: RouteRef; @@ -46,19 +56,30 @@ namespace: string; export { explorePlugin } export { explorePlugin as plugin } +// Warning: (ae-missing-release-tag) "exploreRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const exploreRouteRef: RouteRef; +// Warning: (ae-missing-release-tag) "GroupsExplorerContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GroupsExplorerContent: ({ title, }: { title?: string | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "ToolExplorerContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ToolExplorerContent: ({ title }: { title?: string | undefined; }) => JSX.Element; +// Warnings were encountered during analysis: +// +// src/components/ExploreLayout/ExploreLayout.d.ts:29:5 - (ae-forgotten-export) The symbol "ExploreLayoutProps" needs to be exported by the entry point index.d.ts +// src/components/ExploreLayout/ExploreLayout.d.ts:30:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 9a4d3b2d88..2059632c08 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore", - "version": "0.3.9", + "version": "0.3.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/plugin-explore-react": "^0.0.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index e0f30ed179..8d7faa87f9 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-fossa +## 0.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.10 ### Patch Changes diff --git a/plugins/fossa/api-report.md b/plugins/fossa/api-report.md index 2a18f74f1b..e1b2cb0cf2 100644 --- a/plugins/fossa/api-report.md +++ b/plugins/fossa/api-report.md @@ -9,6 +9,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntityFossaCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityFossaCard: ({ variant, @@ -16,9 +18,13 @@ export const EntityFossaCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "FossaPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const FossaPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "fossaPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const fossaPlugin: BackstagePlugin< { diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 8f6d32eb70..cc4a27a5c0 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-fossa", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gcp-projects/api-report.md b/plugins/gcp-projects/api-report.md index b2347b42ad..9464a5af6a 100644 --- a/plugins/gcp-projects/api-report.md +++ b/plugins/gcp-projects/api-report.md @@ -10,6 +10,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { OAuthApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "GcpApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GcpApi = { listProjects(): Promise; @@ -20,9 +22,13 @@ export type GcpApi = { }): Promise; }; +// Warning: (ae-missing-release-tag) "gcpApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const gcpApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "GcpClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GcpClient implements GcpApi { constructor(googleAuthApi: OAuthApi); @@ -39,9 +45,13 @@ export class GcpClient implements GcpApi { listProjects(): Promise; } +// Warning: (ae-missing-release-tag) "GcpProjectsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GcpProjectsPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "gcpProjectsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const gcpProjectsPlugin: BackstagePlugin< { @@ -52,6 +62,8 @@ const gcpProjectsPlugin: BackstagePlugin< export { gcpProjectsPlugin }; export { gcpProjectsPlugin as plugin }; +// Warning: (ae-missing-release-tag) "Operation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Operation = { name: string; @@ -61,6 +73,8 @@ export type Operation = { response: string; }; +// Warning: (ae-missing-release-tag) "Project" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Project = { name: string; @@ -70,11 +84,15 @@ export type Project = { createTime?: string; }; +// Warning: (ae-missing-release-tag) "ProjectDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ProjectDetails = { details: string; }; +// Warning: (ae-missing-release-tag) "Status" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Status = { code: number; diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 539e9151f5..1d04d7f1d5 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index 19538e822a..6f2eaae984 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-git-release-manager +## 0.2.0 + +### Minor Changes + +- a2d8922c9: Enable users to add custom features + + Add more metadata to success callbacks + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.6 + ## 0.1.3 ### Patch Changes diff --git a/plugins/git-release-manager/api-report.md b/plugins/git-release-manager/api-report.md index 3039bb033f..018a0f4be8 100644 --- a/plugins/git-release-manager/api-report.md +++ b/plugins/git-release-manager/api-report.md @@ -7,14 +7,23 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-forgotten-export) The symbol "GitReleaseApi" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "gitReleaseManagerApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const gitReleaseManagerApiRef: ApiRef; +// Warning: (ae-forgotten-export) The symbol "GitReleaseManager" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "GitReleaseManagerPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GitReleaseManagerPage: GitReleaseManager; +// Warning: (ae-missing-release-tag) "gitReleaseManagerPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const gitReleaseManagerPlugin: BackstagePlugin< { diff --git a/plugins/git-release-manager/dev/index.tsx b/plugins/git-release-manager/dev/index.tsx index 2a0170a61e..1876995bf1 100644 --- a/plugins/git-release-manager/dev/index.tsx +++ b/plugins/git-release-manager/dev/index.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { createDevApp } from '@backstage/dev-utils'; -import { Box, Typography } from '@material-ui/core'; +import { Box, Button, Typography } from '@material-ui/core'; import { gitReleaseManagerPlugin, GitReleaseManagerPage } from '../src/plugin'; import { InfoCardPlus } from '../src/components/InfoCardPlus'; @@ -44,10 +44,16 @@ createDevApp() Dev notes + Configure plugin statically by passing props to the `GitHubReleaseManagerPage` component + + + Note that the static configuration points towards private + repositories and will thus not work for everyone. + Dev notes - Each feature can be omitted - Success callbacks can also be added + + Each feature can be individually omitted as well as have success + callback attached to them + { + onSuccess: args => { // eslint-disable-next-line no-console console.log( - 'Custom success callback for Create RC', - comparisonUrl, - createdTag, - gitReleaseName, - gitReleaseUrl, - previousTag, + 'Custom success callback for Create RC with the following args', ); + console.log(JSON.stringify(args, null, 2)); // eslint-disable-line no-console }, }, promoteRc: { @@ -108,4 +106,50 @@ createDevApp() ), }) + .addPage({ + title: 'Custom', + path: '/custom', + element: ( + + + Dev notes + + The custom feature's return value can either be a React Element or + an array of React Elements. + + + + { + return ( + + I'm a custom feature + + + + ); + }, + }, + }} + /> + + ), + }) .render(); diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 19328c4ebd..5598fcd6ef 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-git-release-manager", - "version": "0.1.3", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/integration": "^0.5.6", "@backstage/theme": "^0.2.8", @@ -28,22 +28,23 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "@octokit/rest": "^18.5.3", + "@types/react": "^16.9", "luxon": "^1.26.0", "qs": "^6.10.1", - "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", "react-use": "^17.2.4", + "react": "^16.13.1", "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", - "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", + "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", diff --git a/plugins/git-release-manager/src/GitReleaseManager.tsx b/plugins/git-release-manager/src/GitReleaseManager.tsx index 44669f9bc7..bca19b9b13 100644 --- a/plugins/git-release-manager/src/GitReleaseManager.tsx +++ b/plugins/git-release-manager/src/GitReleaseManager.tsx @@ -18,12 +18,14 @@ import React from 'react'; import { useAsync } from 'react-use'; import { Alert } from '@material-ui/lab'; import { Box } from '@material-ui/core'; +import { useApi } from '@backstage/core-plugin-api'; +import { ContentHeader, Progress } from '@backstage/core-components'; import { ComponentConfig, - ComponentConfigCreateRc, - ComponentConfigPatch, - ComponentConfigPromoteRc, + CreateRcOnSuccessArgs, + PatchOnSuccessArgs, + PromoteRcOnSuccessArgs, } from './types/types'; import { Features } from './features/Features'; import { gitReleaseManagerApiRef } from './api/serviceApiRef'; @@ -33,18 +35,33 @@ import { ProjectContext, Project } from './contexts/ProjectContext'; import { RepoDetailsForm } from './features/RepoDetailsForm/RepoDetailsForm'; import { useQueryHandler } from './hooks/useQueryHandler'; import { UserContext } from './contexts/UserContext'; - -import { useApi } from '@backstage/core-plugin-api'; -import { ContentHeader, Progress } from '@backstage/core-components'; +import { + GetBranchResult, + GetLatestReleaseResult, + GetRepositoryResult, +} from './api/GitReleaseClient'; interface GitReleaseManagerProps { project?: Omit; features?: { info?: Pick, 'omit'>; stats?: Pick, 'omit'>; - createRc?: ComponentConfigCreateRc; - promoteRc?: ComponentConfigPromoteRc; - patch?: ComponentConfigPatch; + createRc?: ComponentConfig; + promoteRc?: ComponentConfig; + patch?: ComponentConfig; + custom?: { + factory: ({ + latestRelease, + project, + releaseBranch, + repository, + }: { + latestRelease: GetLatestReleaseResult['latestRelease'] | null; + project: Project; + releaseBranch: GetBranchResult['branch'] | null; + repository: GetRepositoryResult['repository']; + }) => React.ReactElement | React.ReactElement[]; + }; }; } diff --git a/plugins/git-release-manager/src/features/CreateReleaseCandidate/CreateReleaseCandidate.tsx b/plugins/git-release-manager/src/features/CreateReleaseCandidate/CreateReleaseCandidate.tsx index e99fd9267e..7f24241e66 100644 --- a/plugins/git-release-manager/src/features/CreateReleaseCandidate/CreateReleaseCandidate.tsx +++ b/plugins/git-release-manager/src/features/CreateReleaseCandidate/CreateReleaseCandidate.tsx @@ -31,7 +31,7 @@ import { GetLatestReleaseResult, GetRepositoryResult, } from '../../api/GitReleaseClient'; -import { ComponentConfigCreateRc } from '../../types/types'; +import { ComponentConfig, CreateRcOnSuccessArgs } from '../../types/types'; import { Differ } from '../../components/Differ'; import { getReleaseCandidateGitInfo } from '../../helpers/getReleaseCandidateGitInfo'; import { InfoCardPlus } from '../../components/InfoCardPlus'; @@ -45,7 +45,7 @@ interface CreateReleaseCandidateProps { defaultBranch: GetRepositoryResult['repository']['defaultBranch']; latestRelease: GetLatestReleaseResult['latestRelease']; releaseBranch: GetBranchResult['branch'] | null; - onSuccess?: ComponentConfigCreateRc['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } const InfoCardPlusWrapper = ({ children }: { children: React.ReactNode }) => { diff --git a/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts b/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts index 0d6d4ebb4f..9a89d59c54 100644 --- a/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts +++ b/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.ts @@ -21,7 +21,11 @@ import { GetRepositoryResult, } from '../../../api/GitReleaseClient'; -import { CardHook, ComponentConfigCreateRc } from '../../../types/types'; +import { + CardHook, + ComponentConfig, + CreateRcOnSuccessArgs, +} from '../../../types/types'; import { getReleaseCandidateGitInfo } from '../../../helpers/getReleaseCandidateGitInfo'; import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef'; import { GitReleaseManagerError } from '../../../errors/GitReleaseManagerError'; @@ -31,12 +35,12 @@ import { useResponseSteps } from '../../../hooks/useResponseSteps'; import { useUserContext } from '../../../contexts/UserContext'; import { useApi } from '@backstage/core-plugin-api'; -interface UseCreateReleaseCandidate { +export interface UseCreateReleaseCandidate { defaultBranch: GetRepositoryResult['repository']['defaultBranch']; latestRelease: GetLatestReleaseResult['latestRelease']; releaseCandidateGitInfo: ReturnType; project: Project; - onSuccess?: ComponentConfigCreateRc['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } export function useCreateReleaseCandidate({ @@ -266,6 +270,12 @@ export function useCreateReleaseCandidate({ try { await onSuccess({ + input: { + defaultBranch, + latestRelease, + releaseCandidateGitInfo, + project, + }, comparisonUrl: getComparisonRes.value.htmlUrl, createdTag: createReleaseRes.value.tagName, gitReleaseName: createReleaseRes.value.name, diff --git a/plugins/git-release-manager/src/features/Features.tsx b/plugins/git-release-manager/src/features/Features.tsx index 4014f26f73..b8386c41cc 100644 --- a/plugins/git-release-manager/src/features/Features.tsx +++ b/plugins/git-release-manager/src/features/Features.tsx @@ -142,6 +142,14 @@ export function Features({ onSuccess={features?.patch?.onSuccess} /> )} + + {features?.custom?.factory && + features.custom.factory({ + latestRelease: gitBatchInfo.value.latestRelease, + project, + releaseBranch: gitBatchInfo.value.releaseBranch, + repository: gitBatchInfo.value.repository, + })} ); diff --git a/plugins/git-release-manager/src/features/Patch/Patch.tsx b/plugins/git-release-manager/src/features/Patch/Patch.tsx index 53fc901da7..c902ab0dc5 100644 --- a/plugins/git-release-manager/src/features/Patch/Patch.tsx +++ b/plugins/git-release-manager/src/features/Patch/Patch.tsx @@ -22,7 +22,7 @@ import { GetBranchResult, GetLatestReleaseResult, } from '../../api/GitReleaseClient'; -import { ComponentConfigPatch } from '../../types/types'; +import { ComponentConfig, PatchOnSuccessArgs } from '../../types/types'; import { getBumpedTag } from '../../helpers/getBumpedTag'; import { InfoCardPlus } from '../../components/InfoCardPlus'; import { NoLatestRelease } from '../../components/NoLatestRelease'; @@ -32,7 +32,7 @@ import { useProjectContext } from '../../contexts/ProjectContext'; interface PatchProps { latestRelease: GetLatestReleaseResult['latestRelease']; releaseBranch: GetBranchResult['branch'] | null; - onSuccess?: ComponentConfigPatch['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } export const Patch = ({ diff --git a/plugins/git-release-manager/src/features/Patch/PatchBody.tsx b/plugins/git-release-manager/src/features/Patch/PatchBody.tsx index b4e705fa58..01f5bc9fed 100644 --- a/plugins/git-release-manager/src/features/Patch/PatchBody.tsx +++ b/plugins/git-release-manager/src/features/Patch/PatchBody.tsx @@ -38,7 +38,7 @@ import { GetLatestReleaseResult, } from '../../api/GitReleaseClient'; import { CalverTagParts } from '../../helpers/tagParts/getCalverTagParts'; -import { ComponentConfigPatch } from '../../types/types'; +import { ComponentConfig, PatchOnSuccessArgs } from '../../types/types'; import { Differ } from '../../components/Differ'; import { getPatchCommitSuffix } from './helpers/getPatchCommitSuffix'; import { gitReleaseManagerApiRef } from '../../api/serviceApiRef'; @@ -56,7 +56,7 @@ interface PatchBodyProps { bumpedTag: string; latestRelease: NonNullable; releaseBranch: GetBranchResult['branch']; - onSuccess?: ComponentConfigPatch['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; tagParts: NonNullable; } diff --git a/plugins/git-release-manager/src/features/Patch/hooks/usePatch.ts b/plugins/git-release-manager/src/features/Patch/hooks/usePatch.ts index 5d3f9829ad..a2cbb7aefb 100644 --- a/plugins/git-release-manager/src/features/Patch/hooks/usePatch.ts +++ b/plugins/git-release-manager/src/features/Patch/hooks/usePatch.ts @@ -22,7 +22,11 @@ import { } from '../../../api/GitReleaseClient'; import { CalverTagParts } from '../../../helpers/tagParts/getCalverTagParts'; -import { ComponentConfigPatch, CardHook } from '../../../types/types'; +import { + CardHook, + ComponentConfig, + PatchOnSuccessArgs, +} from '../../../types/types'; import { getPatchCommitSuffix } from '../helpers/getPatchCommitSuffix'; import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef'; import { Project } from '../../../contexts/ProjectContext'; @@ -32,12 +36,12 @@ import { useResponseSteps } from '../../../hooks/useResponseSteps'; import { useUserContext } from '../../../contexts/UserContext'; import { useApi } from '@backstage/core-plugin-api'; -interface Patch { +export interface UsePatch { bumpedTag: string; latestRelease: NonNullable; project: Project; tagParts: NonNullable; - onSuccess?: ComponentConfigPatch['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } // Inspiration: https://stackoverflow.com/questions/53859199/how-to-cherry-pick-through-githubs-api @@ -47,7 +51,7 @@ export function usePatch({ project, tagParts, onSuccess, -}: Patch): CardHook { +}: UsePatch): CardHook { const pluginApiClient = useApi(gitReleaseManagerApiRef); const { user } = useUserContext(); const { @@ -337,13 +341,19 @@ ${selectedPatchCommit.commit.message}`, try { await onSuccess?.({ - updatedReleaseUrl: updatedReleaseRes.value.htmlUrl, - updatedReleaseName: updatedReleaseRes.value.name, - previousTag: latestRelease.tagName, - patchedTag: updatedReleaseRes.value.tagName, - patchCommitUrl: releaseBranchRes.value.selectedPatchCommit.htmlUrl, + input: { + bumpedTag, + latestRelease, + project, + tagParts, + }, patchCommitMessage: releaseBranchRes.value.selectedPatchCommit.commit.message, + patchCommitUrl: releaseBranchRes.value.selectedPatchCommit.htmlUrl, + patchedTag: updatedReleaseRes.value.tagName, + previousTag: latestRelease.tagName, + updatedReleaseName: updatedReleaseRes.value.name, + updatedReleaseUrl: updatedReleaseRes.value.htmlUrl, }); } catch (error) { asyncCatcher(error); diff --git a/plugins/git-release-manager/src/features/PromoteRc/PromoteRc.tsx b/plugins/git-release-manager/src/features/PromoteRc/PromoteRc.tsx index 8d42119748..341ecbbce4 100644 --- a/plugins/git-release-manager/src/features/PromoteRc/PromoteRc.tsx +++ b/plugins/git-release-manager/src/features/PromoteRc/PromoteRc.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { Alert, AlertTitle } from '@material-ui/lab'; import { Box, Typography } from '@material-ui/core'; -import { ComponentConfigPromoteRc } from '../../types/types'; +import { ComponentConfig, PromoteRcOnSuccessArgs } from '../../types/types'; import { GetLatestReleaseResult } from '../../api/GitReleaseClient'; import { InfoCardPlus } from '../../components/InfoCardPlus'; import { NoLatestRelease } from '../../components/NoLatestRelease'; @@ -27,7 +27,7 @@ import { TEST_IDS } from '../../test-helpers/test-ids'; interface PromoteRcProps { latestRelease: GetLatestReleaseResult['latestRelease']; - onSuccess?: ComponentConfigPromoteRc['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } export const PromoteRc = ({ latestRelease, onSuccess }: PromoteRcProps) => { diff --git a/plugins/git-release-manager/src/features/PromoteRc/PromoteRcBody.tsx b/plugins/git-release-manager/src/features/PromoteRc/PromoteRcBody.tsx index d3ce5e4a70..3ca4518bbc 100644 --- a/plugins/git-release-manager/src/features/PromoteRc/PromoteRcBody.tsx +++ b/plugins/git-release-manager/src/features/PromoteRc/PromoteRcBody.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { Button, Typography, Box } from '@material-ui/core'; -import { ComponentConfigPromoteRc } from '../../types/types'; +import { ComponentConfig, PromoteRcOnSuccessArgs } from '../../types/types'; import { Differ } from '../../components/Differ'; import { GetLatestReleaseResult } from '../../api/GitReleaseClient'; import { ResponseStepDialog } from '../../components/ResponseStepDialog/ResponseStepDialog'; @@ -26,7 +26,7 @@ import { usePromoteRc } from './hooks/usePromoteRc'; interface PromoteRcBodyProps { rcRelease: NonNullable; - onSuccess?: ComponentConfigPromoteRc['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } export const PromoteRcBody = ({ rcRelease, onSuccess }: PromoteRcBodyProps) => { diff --git a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts index 357ba1e0a6..f3a87d3c74 100644 --- a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts +++ b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.ts @@ -16,7 +16,11 @@ import { useState, useEffect } from 'react'; import { useAsync, useAsyncFn } from 'react-use'; -import { CardHook, ComponentConfigPromoteRc } from '../../../types/types'; +import { + CardHook, + ComponentConfig, + PromoteRcOnSuccessArgs, +} from '../../../types/types'; import { GetLatestReleaseResult } from '../../../api/GitReleaseClient'; import { gitReleaseManagerApiRef } from '../../../api/serviceApiRef'; @@ -27,17 +31,17 @@ import { useResponseSteps } from '../../../hooks/useResponseSteps'; import { useUserContext } from '../../../contexts/UserContext'; import { useApi } from '@backstage/core-plugin-api'; -interface PromoteRc { +export interface UsePromoteRc { rcRelease: NonNullable; releaseVersion: string; - onSuccess?: ComponentConfigPromoteRc['onSuccess']; + onSuccess?: ComponentConfig['onSuccess']; } export function usePromoteRc({ rcRelease, releaseVersion, onSuccess, -}: PromoteRc): CardHook { +}: UsePromoteRc): CardHook { const pluginApiClient = useApi(gitReleaseManagerApiRef); const { user } = useUserContext(); const { project } = useProjectContext(); @@ -170,12 +174,16 @@ export function usePromoteRc({ try { await onSuccess?.({ - gitReleaseUrl: promotedReleaseRes.value.htmlUrl, + input: { + rcRelease, + releaseVersion, + }, gitReleaseName: promotedReleaseRes.value.name, - previousTagUrl: rcRelease.htmlUrl, + gitReleaseUrl: promotedReleaseRes.value.htmlUrl, previousTag: rcRelease.tagName, - updatedTagUrl: promotedReleaseRes.value.htmlUrl, + previousTagUrl: rcRelease.htmlUrl, updatedTag: promotedReleaseRes.value.tagName, + updatedTagUrl: promotedReleaseRes.value.htmlUrl, }); } catch (error) { asyncCatcher(error); diff --git a/plugins/git-release-manager/src/types/types.ts b/plugins/git-release-manager/src/types/types.ts index fd45e55ba2..39418fa9d3 100644 --- a/plugins/git-release-manager/src/types/types.ts +++ b/plugins/git-release-manager/src/types/types.ts @@ -14,21 +14,26 @@ * limitations under the License. */ -export type ComponentConfig = { +import { UseCreateReleaseCandidate } from '../features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate'; +import { UsePatch } from '../features/Patch/hooks/usePatch'; +import { UsePromoteRc } from '../features/PromoteRc/hooks/usePromoteRc'; + +export type ComponentConfig = { omit?: boolean; - onSuccess?: (args: Args) => Promise | void; + onSuccess?: (args: OnSuccessArgs) => Promise | void; }; -interface CreateRcOnSuccessArgs { - gitReleaseUrl: string; - gitReleaseName: string | null; +export interface CreateRcOnSuccessArgs { + input: Omit; comparisonUrl: string; - previousTag?: string; createdTag: string; + gitReleaseName: string | null; + gitReleaseUrl: string; + previousTag?: string; } -export type ComponentConfigCreateRc = ComponentConfig; -interface PromoteRcOnSuccessArgs { +export interface PromoteRcOnSuccessArgs { + input: Omit; gitReleaseUrl: string; gitReleaseName: string | null; previousTagUrl: string; @@ -36,9 +41,9 @@ interface PromoteRcOnSuccessArgs { updatedTagUrl: string; updatedTag: string; } -export type ComponentConfigPromoteRc = ComponentConfig; -interface PatchOnSuccessArgs { +export interface PatchOnSuccessArgs { + input: Omit; updatedReleaseUrl: string; updatedReleaseName: string | null; previousTag: string; @@ -46,7 +51,6 @@ interface PatchOnSuccessArgs { patchCommitUrl: string; patchCommitMessage: string; } -export type ComponentConfigPatch = ComponentConfig; export interface ResponseStep { message: string | React.ReactNode; diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 96ec437188..88bc9d08df 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-github-actions +## 0.4.13 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.4.12 ### Patch Changes diff --git a/plugins/github-actions/api-report.md b/plugins/github-actions/api-report.md index e9fb9e09dc..22b5d57d45 100644 --- a/plugins/github-actions/api-report.md +++ b/plugins/github-actions/api-report.md @@ -14,6 +14,8 @@ import { OAuthApi } from '@backstage/core-plugin-api'; import { RestEndpointMethodTypes } from '@octokit/rest'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "BuildStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export enum BuildStatus { // (undocumented) @@ -26,11 +28,15 @@ export enum BuildStatus { 'success' = 0, } +// Warning: (ae-missing-release-tag) "EntityGithubActionsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityGithubActionsContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLatestGithubActionRunCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLatestGithubActionRunCard: ({ branch, @@ -41,6 +47,8 @@ export const EntityLatestGithubActionRunCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLatestGithubActionsForBranchCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLatestGithubActionsForBranchCard: ({ branch, @@ -51,6 +59,9 @@ export const EntityLatestGithubActionsForBranchCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityRecentGithubActionsRunsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityRecentGithubActionsRunsCard: ({ branch, @@ -59,9 +70,13 @@ export const EntityRecentGithubActionsRunsCard: ({ variant, }: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "GITHUB_ACTIONS_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; +// Warning: (ae-missing-release-tag) "GithubActionsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GithubActionsApi = { listWorkflowRuns: ({ @@ -150,9 +165,13 @@ export type GithubActionsApi = { >; }; +// Warning: (ae-missing-release-tag) "githubActionsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const githubActionsApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "GithubActionsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GithubActionsClient implements GithubActionsApi { constructor(options: { configApi: ConfigApi; githubAuthApi: OAuthApi }); @@ -248,6 +267,8 @@ export class GithubActionsClient implements GithubActionsApi { }): Promise; } +// Warning: (ae-missing-release-tag) "githubActionsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const githubActionsPlugin: BackstagePlugin< { @@ -258,11 +279,15 @@ const githubActionsPlugin: BackstagePlugin< export { githubActionsPlugin }; export { githubActionsPlugin as plugin }; +// Warning: (ae-missing-release-tag) "isGithubActionsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isGithubActionsAvailable: (entity: Entity) => boolean; export { isGithubActionsAvailable }; export { isGithubActionsAvailable as isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "Job" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Job = { html_url: string; @@ -275,24 +300,33 @@ export type Job = { steps: Step[]; }; +// Warning: (ae-missing-release-tag) "Jobs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Jobs = { total_count: number; jobs: Job[]; }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "LatestWorkflowRunCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LatestWorkflowRunCard: ({ branch, variant, }: Props_3) => JSX.Element; +// Warning: (ae-missing-release-tag) "LatestWorkflowsForBranchCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LatestWorkflowsForBranchCard: ({ branch, variant, }: Props_3) => JSX.Element; +// Warning: (ae-missing-release-tag) "RecentWorkflowRunsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RecentWorkflowRunsCard: ({ branch, @@ -301,9 +335,14 @@ export const RecentWorkflowRunsCard: ({ variant, }: Props) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props_2) => JSX.Element; +// Warning: (ae-missing-release-tag) "Step" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Step = { name: string; diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 11262a91bc..d85d1e53fe 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.4.12", + "version": "0.4.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/integration": "^0.5.8", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -51,10 +51,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index 4c525a63dc..524c1bd86b 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-github-deployments +## 0.1.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.1.10 ### Patch Changes diff --git a/plugins/github-deployments/api-report.md b/plugins/github-deployments/api-report.md index 3cdf9f0316..fcc5022d7c 100644 --- a/plugins/github-deployments/api-report.md +++ b/plugins/github-deployments/api-report.md @@ -9,21 +9,34 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { TableColumn } from '@backstage/core-components'; +// Warning: (ae-forgotten-export) The symbol "GithubDeployment" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createCommitColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createCommitColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createCreatorColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createCreatorColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createEnvironmentColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createEnvironmentColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createLastUpdatedColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createLastUpdatedColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "createStatusColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) function createStatusColumn(): TableColumn; +// Warning: (ae-missing-release-tag) "EntityGithubDeploymentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityGithubDeploymentsCard: ({ last, @@ -35,9 +48,15 @@ export const EntityGithubDeploymentsCard: ({ columns?: TableColumn[] | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "githubDeploymentsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const githubDeploymentsPlugin: BackstagePlugin<{}, {}>; +// Warning: (ae-forgotten-export) The symbol "GithubDeploymentsTableProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "GithubDeploymentsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "GithubDeploymentsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function GithubDeploymentsTable({ deployments, @@ -48,15 +67,21 @@ export function GithubDeploymentsTable({ // @public (undocumented) export namespace GithubDeploymentsTable { - var // (undocumented) + var // Warning: (ae-forgotten-export) The symbol "columnFactories" needs to be exported by the entry point index.d.ts + // + // (undocumented) columns: typeof columnFactories; var // (undocumented) defaultDeploymentColumns: TableColumn[]; } +// Warning: (ae-missing-release-tag) "GithubStateIndicator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const GithubStateIndicator: ({ state }: { state: string }) => JSX.Element; +// Warning: (ae-missing-release-tag) "isGithubDeploymentsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const isGithubDeploymentsAvailable: (entity: Entity) => boolean; diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 754f1d73bc..9561083063 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-deployments", - "version": "0.1.10", + "version": "0.1.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,12 +21,12 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -38,10 +38,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/gitops-profiles/api-report.md b/plugins/gitops-profiles/api-report.md index f38f660c77..aacdaff966 100644 --- a/plugins/gitops-profiles/api-report.md +++ b/plugins/gitops-profiles/api-report.md @@ -9,6 +9,8 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "ApplyProfileRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ApplyProfileRequest { // (undocumented) @@ -23,6 +25,8 @@ export interface ApplyProfileRequest { targetRepo: string; } +// Warning: (ae-missing-release-tag) "ChangeClusterStateRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ChangeClusterStateRequest { // (undocumented) @@ -37,6 +41,8 @@ export interface ChangeClusterStateRequest { targetRepo: string; } +// Warning: (ae-missing-release-tag) "CloneFromTemplateRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CloneFromTemplateRequest { // (undocumented) @@ -56,6 +62,8 @@ export interface CloneFromTemplateRequest { templateRepository: string; } +// Warning: (ae-missing-release-tag) "ClusterStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ClusterStatus { // (undocumented) @@ -70,6 +78,8 @@ export interface ClusterStatus { status: string; } +// Warning: (ae-missing-release-tag) "FetchError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class FetchError extends Error { // (undocumented) @@ -78,18 +88,24 @@ export class FetchError extends Error { get name(): string; } +// Warning: (ae-missing-release-tag) "GithubUserInfoRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface GithubUserInfoRequest { // (undocumented) accessToken: string; } +// Warning: (ae-missing-release-tag) "GithubUserInfoResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface GithubUserInfoResponse { // (undocumented) login: string; } +// Warning: (ae-missing-release-tag) "GitOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GitOpsApi = { url: string; @@ -101,18 +117,28 @@ export type GitOpsApi = { fetchUserInfo(req: GithubUserInfoRequest): Promise; }; +// Warning: (ae-missing-release-tag) "gitOpsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const gitOpsApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "GitopsProfilesClusterListPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GitopsProfilesClusterListPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "GitopsProfilesClusterPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GitopsProfilesClusterPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "GitopsProfilesCreatePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GitopsProfilesCreatePage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "gitopsProfilesPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const gitopsProfilesPlugin: BackstagePlugin< { @@ -128,6 +154,8 @@ const gitopsProfilesPlugin: BackstagePlugin< export { gitopsProfilesPlugin }; export { gitopsProfilesPlugin as plugin }; +// Warning: (ae-missing-release-tag) "GitOpsRestApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GitOpsRestApi implements GitOpsApi { constructor(url?: string); @@ -147,6 +175,8 @@ export class GitOpsRestApi implements GitOpsApi { url: string; } +// Warning: (ae-missing-release-tag) "ListClusterRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ListClusterRequest { // (undocumented) @@ -155,12 +185,16 @@ export interface ListClusterRequest { gitHubUser: string; } +// Warning: (ae-missing-release-tag) "ListClusterStatusesResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ListClusterStatusesResponse { // (undocumented) result: ClusterStatus[]; } +// Warning: (ae-missing-release-tag) "PollLogRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface PollLogRequest { // (undocumented) @@ -173,6 +207,8 @@ export interface PollLogRequest { targetRepo: string; } +// Warning: (ae-missing-release-tag) "Status" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Status { // (undocumented) @@ -183,6 +219,8 @@ export interface Status { status: string; } +// Warning: (ae-missing-release-tag) "StatusResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface StatusResponse { // (undocumented) diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index f9d029b6d6..e7e45ee2a6 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -43,10 +43,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/graphiql/api-report.md b/plugins/graphiql/api-report.md index 673f508e03..110d3b315d 100644 --- a/plugins/graphiql/api-report.md +++ b/plugins/graphiql/api-report.md @@ -12,6 +12,8 @@ import { IconComponent } from '@backstage/core-plugin-api'; import { OAuthApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EndpointConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type EndpointConfig = { id: string; @@ -23,6 +25,8 @@ export type EndpointConfig = { }; }; +// Warning: (ae-missing-release-tag) "GithubEndpointConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GithubEndpointConfig = { id: string; @@ -32,28 +36,42 @@ export type GithubEndpointConfig = { githubAuthApi: OAuthApi; }; +// Warning: (ae-missing-release-tag) "GraphiQLIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GraphiQLIcon: IconComponent; +// Warning: (ae-missing-release-tag) "GraphiQLPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GraphiQLPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "graphiqlPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const graphiqlPlugin: BackstagePlugin<{}, {}>; export { graphiqlPlugin }; export { graphiqlPlugin as plugin }; +// Warning: (ae-missing-release-tag) "graphiQLRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const graphiQLRouteRef: RouteRef; +// Warning: (ae-missing-release-tag) "GraphQLBrowseApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GraphQLBrowseApi = { getEndpoints(): Promise; }; +// Warning: (ae-missing-release-tag) "graphQlBrowseApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const graphQlBrowseApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "GraphQLEndpoint" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GraphQLEndpoint = { id: string; @@ -61,6 +79,8 @@ export type GraphQLEndpoint = { fetcher: (body: any) => Promise; }; +// Warning: (ae-missing-release-tag) "GraphQLEndpoints" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class GraphQLEndpoints implements GraphQLBrowseApi { // (undocumented) @@ -72,6 +92,8 @@ export class GraphQLEndpoints implements GraphQLBrowseApi { static github(config: GithubEndpointConfig): GraphQLEndpoint; } +// Warning: (ae-missing-release-tag) "GraphiQLPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: () => JSX.Element; diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 22eab92ba4..46f44cdcf4 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/graphql/api-report.md b/plugins/graphql/api-report.md index c50312ddf6..c09feb7bcc 100644 --- a/plugins/graphql/api-report.md +++ b/plugins/graphql/api-report.md @@ -7,9 +7,13 @@ import { Config } from '@backstage/config'; import express from 'express'; import { Logger as Logger_2 } from 'winston'; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index 9521e1dfc4..b7eef7ed31 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-ilert +## 0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.1.5 ### Patch Changes diff --git a/plugins/ilert/api-report.md b/plugins/ilert/api-report.md index 94a3657e04..3c6965b2c4 100644 --- a/plugins/ilert/api-report.md +++ b/plugins/ilert/api-report.md @@ -13,14 +13,20 @@ import { Entity } from '@backstage/catalog-model'; import { IconComponent } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntityILertCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityILertCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "GetIncidentsCountOpts" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GetIncidentsCountOpts = { states?: IncidentStatus[]; }; +// Warning: (ae-missing-release-tag) "GetIncidentsOpts" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type GetIncidentsOpts = { maxResults?: number; @@ -29,6 +35,8 @@ export type GetIncidentsOpts = { alertSources?: number[]; }; +// Warning: (ae-missing-release-tag) "ILertApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ILertApi { // (undocumented) @@ -43,6 +51,8 @@ export interface ILertApi { incident: Incident, responder: IncidentResponder, ): Promise; + // Warning: (ae-forgotten-export) The symbol "EventRequest" needs to be exported by the entry point index.d.ts + // // (undocumented) createIncident(eventRequest: EventRequest): Promise; // (undocumented) @@ -51,30 +61,48 @@ export interface ILertApi { enableAlertSource(alertSource: AlertSource): Promise; // (undocumented) fetchAlertSource(idOrIntegrationKey: number | string): Promise; + // Warning: (ae-forgotten-export) The symbol "OnCall" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchAlertSourceOnCalls(alertSource: AlertSource): Promise; + // Warning: (ae-forgotten-export) The symbol "AlertSource" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchAlertSources(): Promise; // (undocumented) fetchIncident(id: number): Promise; + // Warning: (ae-forgotten-export) The symbol "IncidentAction" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchIncidentActions(incident: Incident): Promise; + // Warning: (ae-forgotten-export) The symbol "IncidentResponder" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchIncidentResponders(incident: Incident): Promise; + // Warning: (ae-forgotten-export) The symbol "Incident" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchIncidents(opts?: GetIncidentsOpts): Promise; // (undocumented) fetchIncidentsCount(opts?: GetIncidentsCountOpts): Promise; + // Warning: (ae-forgotten-export) The symbol "Schedule" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchOnCallSchedules(): Promise; // (undocumented) fetchUptimeMonitor(id: number): Promise; + // Warning: (ae-forgotten-export) The symbol "UptimeMonitor" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchUptimeMonitors(): Promise; + // Warning: (ae-forgotten-export) The symbol "User" needs to be exported by the entry point index.d.ts + // // (undocumented) fetchUsers(): Promise; // (undocumented) getAlertSourceDetailsURL(alertSource: AlertSource | null): string; + // Warning: (ae-forgotten-export) The symbol "EscalationPolicy" needs to be exported by the entry point index.d.ts + // // (undocumented) getEscalationPolicyDetailsURL(escalationPolicy: EscalationPolicy): string; // (undocumented) @@ -107,14 +135,21 @@ export interface ILertApi { ): Promise; } +// Warning: (ae-missing-release-tag) "ilertApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ilertApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "ILertCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ILertCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "ILertClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ILertClient implements ILertApi { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(opts: Options); // (undocumented) acceptIncident(incident: Incident, userName: string): Promise; @@ -197,12 +232,18 @@ export class ILertClient implements ILertApi { ): Promise; } +// Warning: (ae-missing-release-tag) "ILertIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ILertIcon: IconComponent; +// Warning: (ae-missing-release-tag) "ILertPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ILertPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "ilertPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const ilertPlugin: BackstagePlugin< { @@ -213,22 +254,34 @@ const ilertPlugin: BackstagePlugin< export { ilertPlugin }; export { ilertPlugin as plugin }; +// Warning: (ae-missing-release-tag) "iLertRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const iLertRouteRef: RouteRef; +// Warning: (ae-missing-release-tag) "isPluginApplicableToEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isPluginApplicableToEntity: (entity: Entity) => boolean; export { isPluginApplicableToEntity as isILertAvailable }; export { isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "ILertPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: () => JSX.Element; +// Warning: (ae-missing-release-tag) "TableState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TableState = { page: number; pageSize: number; }; +// Warnings were encountered during analysis: +// +// src/api/types.d.ts:14:5 - (ae-forgotten-export) The symbol "IncidentStatus" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index e40f6ae310..01a8e7b013 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-ilert", - "version": "0.1.5", + "version": "0.1.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@date-io/luxon": "2.x", "@material-ui/core": "^4.11.0", @@ -38,10 +38,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/jenkins-backend/.eslintrc.js b/plugins/jenkins-backend/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/jenkins-backend/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md new file mode 100644 index 0000000000..8f2a4bc576 --- /dev/null +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -0,0 +1,10 @@ +# @backstage/plugin-jenkins-backend + +## 0.1.2 + +### Patch Changes + +- eee05803a: Update `@backstage/backend-common` to `^0.8.6` +- Updated dependencies + - @backstage/catalog-client@0.3.17 + - @backstage/backend-common@0.8.7 diff --git a/plugins/jenkins-backend/README.md b/plugins/jenkins-backend/README.md new file mode 100644 index 0000000000..bac56b6eaf --- /dev/null +++ b/plugins/jenkins-backend/README.md @@ -0,0 +1,209 @@ +# Jenkins Plugin (Alpha) + +Welcome to the Jenkins backend plugin! Website: [https://jenkins.io/](https://jenkins.io/) + +This is the backend half of the 2 Jenkins plugins and is responsible for: + +- finding an appropriate instance of Jenkins for an entity +- finding the appropriate job(s) on that instance for an entity +- connecting to Jenkins and gathering data to present to the frontend + +## Integrating into a backstage instance + +This plugin needs to be added to an existing backstage instance. + +Typically, this means creating a `src/plugins/jenkins.ts` file and adding a reference to it to `src/index.ts` + +### jenkins.ts + +```typescript +import { + createRouter, + DefaultJenkinsInfoProvider, +} from '@backstage/plugin-jenkins-backend'; +import { CatalogClient } from '@backstage/catalog-client'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + config, + discovery, +}: PluginEnvironment): Promise { + const catalog = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + logger, + jenkinsInfoProvider: DefaultJenkinsInfoProvider.fromConfig({ + config, + catalog, + }), + }); +} +``` + +This plugin must be provided with a JenkinsInfoProvider, this is a strategy object for finding the Jenkins instance and job for an entity. + +There is a standard one provided, but the Integrator is free to build their own. + +### DefaultJenkinsInfoProvider + +Allows configuration of either a single or multiple global Jenkins instances and annotating entities with the job name on that instance (and optionally the name of the instance). + +#### Example - Single global instance + +The following will look for jobs for this entity at `https://jenkins.example.com/job/teamA/job/artistLookup-build` + +Config + +```yaml +jenkins: + baseUrl: https://jenkins.example.com + username: backstage-bot + apiKey: 123456789abcdef0123456789abcedf012 +``` + +Catalog + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: artist-lookup + annotations: + 'jenkins.io/job-full-name': teamA/artistLookup-build +``` + +The old annotation name of `jenkins.io/github-folder` is equivalent to `jenkins.io/job-full-name` + +#### Example - Multiple global instances + +The following will look for jobs for this entity at `https://jenkins-foo.example.com/job/teamA/job/artistLookup-build` + +Config + +```yaml +jenkins: + instances: + - name: default + baseUrl: https://jenkins.example.com + username: backstage-bot + apiKey: 123456789abcdef0123456789abcedf012 + - name: departmentFoo + baseUrl: https://jenkins-foo.example.com + username: backstage-bot + apiKey: 123456789abcdef0123456789abcedf012 +``` + +Catalog + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: artist-lookup + annotations: + 'jenkins.io/job-full-name': departmentFoo:teamA/artistLookup-build +``` + +If the `departmentFoo:` part is omitted, the default instance will be assumed. + +The following config is an equivalent (but less clear) version of the above: + +```yaml +jenkins: + baseUrl: https://jenkins.example.com + username: backstage-bot + apiKey: 123456789abcdef0123456789abcedf012 + instances: + - name: departmentFoo + baseUrl: https://jenkins-foo.example.com + username: backstage-bot + apiKey: 123456789abcdef0123456789abcedf012 +``` + +### Custom JenkinsInfoProvider + +An example of a bespoke JenkinsInfoProvider which uses an organisation specific annotation to look up the Jenkins info (including jobFullName): + +```typescript +class AcmeJenkinsInfoProvider implements JenkinsInfoProvider { + constructor(private readonly catalog: CatalogClient) {} + + async getInstance(opt: { + entityRef: EntityName; + jobFullName?: string; + }): Promise { + const PAAS_ANNOTATION = 'acme.example.com/paas-project-name'; + + // lookup pass-project-name from entity annotation + const entity = await this.catalog.getEntityByName(opt.entityRef); + if (!entity) { + throw new Error( + `Couldn't find entity with name: ${stringifyEntityRef(opt.entityRef)}`, + ); + } + + const paasProjectName = entity.metadata.annotations?.[PAAS_ANNOTATION]; + if (!paasProjectName) { + throw new Error( + `Couldn't find paas annotation (${PAAS_ANNOTATION}) on entity with name: ${stringifyEntityRef( + opt.entityRef, + )}`, + ); + } + + // lookup department and team for paas project name + const { team, dept } = this.lookupPaasInfo(paasProjectName); + + const baseUrl = `https://jenkins-${dept}.example.com/`; + const jobFullName = `${team}/${paasProjectName}`; + const username = 'backstage-bot'; + const apiKey = this.getJenkinsApiKey(paasProjectName); + const creds = btoa(`${username}:${apiKey}`); + + return { + baseUrl, + headers: { + Authorization: `Basic ${creds}`, + }, + jobFullName, + }; + } + + private lookupPaasInfo(_: string): { team: string; dept: string } { + // Mock implementation, this would get info from the paas system somehow in reality. + return { + team: 'teamA', + dept: 'DepartmentFoo', + }; + } + + private getJenkinsApiKey(_: string): string { + // Mock implementation, this would get info from the paas system somehow in reality. + return '123456789abcdef0123456789abcedf012'; + } +} +``` + +No config would be needed if using this JenkinsInfoProvider + +A Catalog entity of the following will look for jobs for this entity at `https://jenkins-departmentFoo.example.com/job/teamA/job/artistLookupService` + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: artist-lookup + annotations: + 'acme.example.com/paas-project-name': artistLookupService +``` + +## Jenkins' terminology notes + +The domain model for Jenkins is not particularly clear but for the purposes of this plugin the following model has been assumed: + +Jenkins contains a tree of *job*s which have children of either; other *job*s (making it a _folder_) or *build*s (making it a _project_). +Concepts like _pipeline_ and *view*s are meaningless (pipelines are just jobs for our purposes, views are (as the name suggests) just views of subsets of jobs) + +A _job full name_ is a slash separated list of the names of the job, and the folders which contain it. For example `teamA/artistLookupService/develop`, and the same way that a filesystem path has folders and file names. diff --git a/plugins/jenkins-backend/api-report.md b/plugins/jenkins-backend/api-report.md new file mode 100644 index 0000000000..b94931e000 --- /dev/null +++ b/plugins/jenkins-backend/api-report.md @@ -0,0 +1,71 @@ +## API Report File for "@backstage/plugin-jenkins-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { CatalogClient } from '@backstage/catalog-client'; +import { Config } from '@backstage/config'; +import { EntityName } from '@backstage/catalog-model'; +import express from 'express'; +import { Logger as Logger_2 } from 'winston'; + +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// Warning: (ae-missing-release-tag) "DefaultJenkinsInfoProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { + // (undocumented) + static fromConfig(options: { + config: Config; + catalog: CatalogClient; + }): DefaultJenkinsInfoProvider; + // (undocumented) + getInstance(opt: { + entityRef: EntityName; + jobFullName?: string; + }): Promise; + // (undocumented) + static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-full-name'; + // (undocumented) + static readonly OLD_JENKINS_ANNOTATION = 'jenkins.io/github-folder'; +} + +// Warning: (ae-missing-release-tag) "JenkinsInfo" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface JenkinsInfo { + // (undocumented) + baseUrl: string; + // (undocumented) + headers?: Record; + // (undocumented) + jobFullName: string; +} + +// Warning: (ae-missing-release-tag) "JenkinsInfoProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface JenkinsInfoProvider { + // (undocumented) + getInstance(options: { + entityRef: EntityName; + jobFullName?: string; + }): Promise; +} + +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + jenkinsInfoProvider: JenkinsInfoProvider; + // (undocumented) + logger: Logger_2; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/jenkins-backend/config.d.ts b/plugins/jenkins-backend/config.d.ts new file mode 100644 index 0000000000..bfb519bb58 --- /dev/null +++ b/plugins/jenkins-backend/config.d.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export interface Config { + jenkins?: { + /** + * Default instance baseUrl, can be specified on a named instance called "default" + * @pattern "^https?://" + */ + baseUrl?: string; + /** + * Default instance username, can be specified on a named instance called "default" + */ + username?: string; + /** + * Default Instance apiKey, can be specified on a named instance called "default" + * @visibility secret + */ + apiKey?: string; + + instances?: { + /** + * Name of the instance, this will be used in an annotation on catalog entities to refer to jobs on this instance. + * + * Use a name of "default" to specify the jenkins instance details if the annotation doesn't explicitly name an + * instance. + */ + name: string; + + /** + * @pattern "^https?://" + */ + baseUrl: string; + username: string; + /** @visibility secret */ + apiKey: string; + }[]; + }; +} diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json new file mode 100644 index 0000000000..6f7beb9444 --- /dev/null +++ b/plugins/jenkins-backend/package.json @@ -0,0 +1,47 @@ +{ + "name": "@backstage/plugin-jenkins-backend", + "version": "0.1.2", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "configSchema": "config.d.ts", + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.8.7", + "@backstage/catalog-client": "^0.3.17", + "@backstage/catalog-model": "^0.9.0", + "@backstage/config": "^0.1.5", + "@types/express": "^4.17.6", + "cross-fetch": "^3.0.6", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "jenkins": "^0.28.1", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.7.5", + "@types/jenkins": "^0.23.1", + "@types/supertest": "^2.0.8", + "msw": "^0.29.0", + "supertest": "^4.0.2" + }, + "files": [ + "dist", + "config.d.ts" + ] +} diff --git a/plugins/jenkins/src/components/useProjectSlugFromEntity.ts b/plugins/jenkins-backend/src/index.ts similarity index 69% rename from plugins/jenkins/src/components/useProjectSlugFromEntity.ts rename to plugins/jenkins-backend/src/index.ts index 10789b383a..c5c541db2b 100644 --- a/plugins/jenkins/src/components/useProjectSlugFromEntity.ts +++ b/plugins/jenkins-backend/src/index.ts @@ -13,11 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { useEntity } from '@backstage/plugin-catalog-react'; -import { JENKINS_ANNOTATION } from '../constants'; -export const useProjectSlugFromEntity = () => { - const { entity } = useEntity(); - - return entity.metadata.annotations?.[JENKINS_ANNOTATION] ?? ''; -}; +export * from './service'; diff --git a/plugins/jenkins-backend/src/run.ts b/plugins/jenkins-backend/src/run.ts new file mode 100644 index 0000000000..b96989e4b8 --- /dev/null +++ b/plugins/jenkins-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/jenkins-backend/src/service/index.ts b/plugins/jenkins-backend/src/service/index.ts new file mode 100644 index 0000000000..329092e881 --- /dev/null +++ b/plugins/jenkins-backend/src/service/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export type { RouterOptions } from './router'; +export { createRouter } from './router'; +export type { JenkinsInfo, JenkinsInfoProvider } from './jenkinsInfoProvider'; +export { DefaultJenkinsInfoProvider } from './jenkinsInfoProvider'; diff --git a/plugins/jenkins-backend/src/service/jenkinsApi.test.ts b/plugins/jenkins-backend/src/service/jenkinsApi.test.ts new file mode 100644 index 0000000000..380a65f895 --- /dev/null +++ b/plugins/jenkins-backend/src/service/jenkinsApi.test.ts @@ -0,0 +1,397 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { JenkinsApiImpl } from './jenkinsApi'; +import jenkins from 'jenkins'; +import { JenkinsInfo } from './jenkinsInfoProvider'; +import { JenkinsBuild, JenkinsProject } from '../types'; + +jest.mock('jenkins'); +const mockedJenkinsClient = { + job: { + get: jest.fn(), + build: jest.fn(), + }, + build: { + get: jest.fn(), + }, +}; +const mockedJenkins = jenkins as jest.Mocked; +mockedJenkins.mockReturnValue(mockedJenkinsClient); + +const jobFullName = 'example-jobName/foo'; +const buildNumber = 19; +const jenkinsInfo: JenkinsInfo = { + baseUrl: 'https://jenkins.example.com', + headers: { headerName: 'headerValue' }, + jobFullName: 'example-jobName', +}; + +describe('JenkinsApi', () => { + const jenkinsApi = new JenkinsApiImpl(); + + describe('getProjects', () => { + const project: JenkinsProject = { + actions: [], + displayName: 'Example Build', + fullDisplayName: 'Example jobName » Example Build', + fullName: 'example-jobName/exampleBuild', + inQueue: false, + lastBuild: { + actions: [], + timestamp: 1, + building: false, + duration: 10, + result: 'success', + displayName: '#7', + fullDisplayName: 'Example jobName » Example Build #7', + url: 'https://jenkins.example.com/job/example-jobName/job/exampleBuild', + number: 7, + }, + }; + + describe('unfiltered', () => { + it('standard github layout', async () => { + mockedJenkinsClient.job.get.mockResolvedValueOnce({ jobs: [project] }); + + const result = await jenkinsApi.getProjects(jenkinsInfo); + + expect(mockedJenkins).toHaveBeenCalledWith({ + baseUrl: jenkinsInfo.baseUrl, + headers: jenkinsInfo.headers, + promisify: true, + }); + expect(mockedJenkinsClient.job.get).toBeCalledWith({ + name: jenkinsInfo.jobFullName, + tree: expect.anything(), + }); + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ + actions: [], + displayName: 'Example Build', + fullDisplayName: 'Example jobName » Example Build', + fullName: 'example-jobName/exampleBuild', + inQueue: false, + lastBuild: { + actions: [], + timestamp: 1, + building: false, + duration: 10, + result: 'success', + displayName: '#7', + fullDisplayName: 'Example jobName » Example Build #7', + url: + 'https://jenkins.example.com/job/example-jobName/job/exampleBuild', + number: 7, + status: 'success', + source: {}, + }, + status: 'success', + }); + }); + }); + describe('filtered by branch', () => { + it('standard github layout', async () => { + mockedJenkinsClient.job.get.mockResolvedValueOnce(project); + + const result = await jenkinsApi.getProjects( + jenkinsInfo, + 'testBranchName', + ); + + expect(mockedJenkins).toHaveBeenCalledWith({ + baseUrl: jenkinsInfo.baseUrl, + headers: jenkinsInfo.headers, + promisify: true, + }); + expect(mockedJenkinsClient.job.get).toBeCalledWith({ + name: `${jenkinsInfo.jobFullName}/testBranchName`, + tree: expect.anything(), + }); + expect(result).toHaveLength(1); + }); + }); + describe('augmented', () => { + const projectWithScmActions: JenkinsProject = { + actions: [ + {}, + {}, + {}, + {}, + { + _class: 'jenkins.scm.api.metadata.ContributorMetadataAction', + contributor: 'testuser', + contributorDisplayName: 'Mr. T User', + contributorEmail: null, + }, + {}, + { + _class: 'jenkins.scm.api.metadata.ObjectMetadataAction', + objectDescription: '', + objectDisplayName: 'Add LICENSE, CoC etc', + objectUrl: 'https://github.com/backstage/backstage/pull/1', + }, + {}, + {}, + { + _class: 'com.cloudbees.plugins.credentials.ViewCredentialsAction', + stores: {}, + }, + ], + displayName: 'Example Build', + fullDisplayName: 'Example jobName » Example Build', + fullName: 'example-jobName/exampleBuild', + inQueue: false, + lastBuild: { + actions: [ + { + _class: 'hudson.model.CauseAction', + causes: [ + { + _class: 'jenkins.branch.BranchIndexingCause', + shortDescription: 'Branch indexing', + }, + ], + }, + {}, + {}, + {}, + { + _class: 'org.jenkinsci.plugins.workflow.cps.EnvActionImpl', + environment: {}, + }, + {}, + {}, + {}, + {}, + {}, + { + _class: 'hudson.plugins.git.util.BuildData', + buildsByBranchName: { + 'PR-1': { + _class: 'hudson.plugins.git.util.Build', + buildNumber: 5, + buildResult: null, + marked: { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + branch: [ + { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + name: 'PR-1', + }, + ], + }, + revision: { + SHA1: '6c6b34c0fb91cf077a01fe62d3e8e996b4ea5861', + branch: [ + { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + name: 'PR-1', + }, + ], + }, + }, + }, + lastBuiltRevision: { + SHA1: '6c6b34c0fb91cf077a01fe62d3e8e996b4ea5861', + branch: [ + { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + name: 'PR-1', + }, + ], + }, + remoteUrls: ['https://github.com/backstage/backstage.git'], + scmName: '', + }, + { + _class: 'hudson.plugins.git.util.BuildData', + buildsByBranchName: { + master: { + _class: 'hudson.plugins.git.util.Build', + buildNumber: 5, + buildResult: null, + marked: { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + branch: [ + { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + name: 'master', + }, + ], + }, + revision: { + SHA1: '6c6b34c0fb91cf077a01fe62d3e8e996b4ea5861', + branch: [ + { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + name: 'master', + }, + ], + }, + }, + }, + lastBuiltRevision: { + SHA1: '6c6b34c0fb91cf077a01fe62d3e8e996b4ea5861', + branch: [ + { + SHA1: '14d31bde346fcad64ab939f82d195db36701cfcb', + name: 'master', + }, + ], + }, + remoteUrls: ['https://github.com/backstage/backstage.git'], + scmName: '', + }, + {}, + {}, + { + _class: 'hudson.tasks.junit.TestResultAction', + failCount: 2, + skipCount: 1, + totalCount: 635, + urlName: 'testReport', + }, + {}, + {}, + { + _class: + 'org.jenkinsci.plugins.pipeline.modeldefinition.actions.RestartDeclarativePipelineAction', + restartEnabled: false, + restartableStages: [], + }, + {}, + ], + timestamp: 1, + building: false, + duration: 10, + result: 'success', + displayName: '#7', + fullDisplayName: 'Example jobName » Example Build #7', + url: + 'https://jenkins.example.com/job/example-jobName/job/exampleBuild/7/', + number: 7, + }, + }; + + it('augments project', async () => { + mockedJenkinsClient.job.get.mockResolvedValueOnce({ + jobs: [projectWithScmActions], + }); + + const result = await jenkinsApi.getProjects(jenkinsInfo); + + expect(result).toHaveLength(1); + expect(result[0].status).toEqual('success'); + }); + it('augments build', async () => { + mockedJenkinsClient.job.get.mockResolvedValueOnce({ + jobs: [projectWithScmActions], + }); + + const result = await jenkinsApi.getProjects(jenkinsInfo); + + expect(result).toHaveLength(1); + // TODO: I am really just asserting the previous behaviour wth no understanding here. + // In my 2 Jenkins instances, 1 returns a lot of different and confusing BuildData sections and 1 returns none ☹️ + expect(result[0].lastBuild.source).toEqual({ + branchName: 'master', + commit: { + hash: '14d31bde', + }, + url: 'https://github.com/backstage/backstage/pull/1', + displayName: 'Add LICENSE, CoC etc', + author: 'Mr. T User', + }); + }); + it('finds test report', async () => { + mockedJenkinsClient.job.get.mockResolvedValueOnce({ + jobs: [projectWithScmActions], + }); + + const result = await jenkinsApi.getProjects(jenkinsInfo); + + expect(result).toHaveLength(1); + expect(result[0].lastBuild.tests).toEqual({ + total: 635, + passed: 632, + skipped: 1, + failed: 2, + testUrl: + 'https://jenkins.example.com/job/example-jobName/job/exampleBuild/7/testReport/', + }); + }); + }); + }); + it('getBuild', async () => { + const project: JenkinsProject = { + actions: [], + displayName: 'Example Build', + fullDisplayName: 'Example jobName » Example Build', + fullName: 'example-jobName/exampleBuild', + inQueue: false, + lastBuild: { + actions: [], + timestamp: 1, + building: false, + duration: 10, + result: 'success', + displayName: '#7', + fullDisplayName: 'Example jobName » Example Build #7', + url: 'https://jenkins.example.com/job/example-jobName/job/exampleBuild', + number: 7, + }, + }; + const build: JenkinsBuild = { + actions: [], + timestamp: 1, + building: false, + duration: 10, + result: 'success', + fullDisplayName: 'example-jobName/exampleBuild', + displayName: 'exampleBuild', + url: `https://jenkins.example.com/job/example-jobName/job/exampleBuild/build/${buildNumber}`, + number: buildNumber, + }; + mockedJenkinsClient.job.get.mockResolvedValueOnce(project); + mockedJenkinsClient.build.get.mockResolvedValueOnce(build); + + await jenkinsApi.getBuild(jenkinsInfo, jobFullName, buildNumber); + + expect(mockedJenkins).toHaveBeenCalledWith({ + baseUrl: jenkinsInfo.baseUrl, + headers: jenkinsInfo.headers, + promisify: true, + }); + expect(mockedJenkinsClient.job.get).toBeCalledWith({ + name: jobFullName, + depth: 1, + }); + expect(mockedJenkinsClient.build.get).toBeCalledWith( + jobFullName, + buildNumber, + ); + }); + it('buildProject', async () => { + await jenkinsApi.buildProject(jenkinsInfo, jobFullName); + + expect(mockedJenkins).toHaveBeenCalledWith({ + baseUrl: jenkinsInfo.baseUrl, + headers: jenkinsInfo.headers, + promisify: true, + }); + expect(mockedJenkinsClient.job.build).toBeCalledWith(jobFullName); + }); +}); diff --git a/plugins/jenkins-backend/src/service/jenkinsApi.ts b/plugins/jenkins-backend/src/service/jenkinsApi.ts new file mode 100644 index 0000000000..7df5c5db1d --- /dev/null +++ b/plugins/jenkins-backend/src/service/jenkinsApi.ts @@ -0,0 +1,283 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { JenkinsInfo } from './jenkinsInfoProvider'; +import jenkins from 'jenkins'; +import { + BackstageBuild, + BackstageProject, + JenkinsBuild, + JenkinsProject, + ScmDetails, +} from '../types'; + +export class JenkinsApiImpl { + private static readonly lastBuildTreeSpec = `lastBuild[ + number, + url, + fullDisplayName, + displayName, + building, + result, + timestamp, + duration, + actions[ + *[ + *[ + *[ + * + ] + ] + ] + ] + ],`; + + private static readonly jobTreeSpec = `actions[*], + ${JenkinsApiImpl.lastBuildTreeSpec} + jobs{0,1}, + name, + fullName, + displayName, + fullDisplayName, + inQueue`; + + private static readonly jobsTreeSpec = `jobs[ + ${JenkinsApiImpl.jobTreeSpec} + ]{0,50}`; + + /** + * Get a list of projects for the given JenkinsInfo. + * @see ../../../jenkins/src/api/JenkinsApi.ts#getProjects + */ + async getProjects(jenkinsInfo: JenkinsInfo, branch?: string) { + const client = await JenkinsApiImpl.getClient(jenkinsInfo); + const projects: BackstageProject[] = []; + + if (branch) { + // we have been asked to filter to a single branch. + // Assume jenkinsInfo.jobFullName is a folder which contains one job per branch. + // TODO: extract a strategy interface for this + const job = await client.job.get({ + name: `${jenkinsInfo.jobFullName}/${branch}`, + tree: JenkinsApiImpl.jobTreeSpec.replace(/\s/g, ''), + }); + projects.push(this.augmentProject(job)); + } else { + // We aren't filtering + // Assume jenkinsInfo.jobFullName is a folder which contains one job per branch. + const folder = await client.job.get({ + name: jenkinsInfo.jobFullName, + // Filter only be the information we need, instead of loading all fields. + // Limit to only show the latest build for each job and only load 50 jobs + // at all. + // Whitespaces are only included for readablity here and stripped out + // before sending to Jenkins + tree: JenkinsApiImpl.jobsTreeSpec.replace(/\s/g, ''), + }); + + // TODO: support this being a project itself. + for (const jobDetails of folder.jobs) { + // for each branch (we assume) + if (jobDetails?.jobs) { + // skipping folders inside folders for now + // TODO: recurse + } else { + projects.push(this.augmentProject(jobDetails)); + } + } + } + return projects; + } + + /** + * Get a single build. + * @see ../../../jenkins/src/api/JenkinsApi.ts#getBuild + */ + async getBuild( + jenkinsInfo: JenkinsInfo, + jobFullName: string, + buildNumber: number, + ) { + const client = await JenkinsApiImpl.getClient(jenkinsInfo); + + const project = await client.job.get({ + name: jobFullName, + depth: 1, + }); + + const build = await client.build.get(jobFullName, buildNumber); + const jobScmInfo = JenkinsApiImpl.extractScmDetailsFromJob(project); + + return this.augmentBuild(build, jobScmInfo); + } + + /** + * Trigger a build of a project + * @see ../../../jenkins/src/api/JenkinsApi.ts#retry + */ + async buildProject(jenkinsInfo: JenkinsInfo, jobFullName: string) { + const client = await JenkinsApiImpl.getClient(jenkinsInfo); + + // looks like the current SDK only supports triggering a new build + // can't see any support for replay (re-running the specific build with the same SCM info) + + // Note Jenkins itself has concepts of rebuild and replay on a job. + // The latter should be possible to trigger with a POST to /replay/rebuild + await client.job.build(jobFullName); + } + + // private helper methods + + private static async getClient(jenkinsInfo: JenkinsInfo) { + // The typings for the jenkins library are out of date so just cast to any + return jenkins({ + baseUrl: jenkinsInfo.baseUrl, + headers: jenkinsInfo.headers, + promisify: true, + }) as any; + } + + private augmentProject(project: JenkinsProject): BackstageProject { + let status: string; + if (project.inQueue) { + status = 'queued'; + } else if (project.lastBuild.building) { + status = 'running'; + } else if (!project.lastBuild.result) { + status = 'unknown'; + } else { + status = project.lastBuild.result; + } + + const jobScmInfo = JenkinsApiImpl.extractScmDetailsFromJob(project); + + return { + ...project, + lastBuild: this.augmentBuild(project.lastBuild, jobScmInfo), + status, + // actions: undefined, + }; + } + + private augmentBuild( + build: JenkinsBuild, + jobScmInfo: ScmDetails | undefined, + ): BackstageBuild { + const source = + build.actions + .filter( + (action: any) => + action._class === 'hudson.plugins.git.util.BuildData', + ) + .map((action: any) => { + const [first]: any = Object.values(action.buildsByBranchName); + const branch = first.revision.branch[0]; + return { + branchName: branch.name, + commit: { + hash: branch.SHA1.substring(0, 8), + }, + }; + }) + .pop() || {}; + + if (jobScmInfo) { + source.url = jobScmInfo.url; + source.displayName = jobScmInfo.displayName; + source.author = jobScmInfo.author; + } + + let status: string; + if (build.building) { + status = 'running'; + } else if (!build.result) { + status = 'unknown'; + } else { + status = build.result; + } + return { + ...build, + status, + source: source, + tests: this.getTestReport(build), + }; + } + + private static extractScmDetailsFromJob( + project: JenkinsProject, + ): ScmDetails | undefined { + const scmInfo: ScmDetails | undefined = project.actions + .filter( + (action: any) => + action._class === 'jenkins.scm.api.metadata.ObjectMetadataAction', + ) + .map((action: any) => { + return { + url: action?.objectUrl, + // https://javadoc.jenkins.io/plugin/scm-api/jenkins/scm/api/metadata/ObjectMetadataAction.html + // branch name for regular builds, pull request title on pull requests + displayName: action?.objectDisplayName, + }; + }) + .pop(); + + if (!scmInfo) { + return undefined; + } + + const author = project.actions + .filter( + (action: any) => + action._class === + 'jenkins.scm.api.metadata.ContributorMetadataAction', + ) + .map((action: any) => { + return action.contributorDisplayName; + }) + .pop(); + + if (author) { + scmInfo.author = author; + } + + return scmInfo; + } + + private getTestReport( + build: JenkinsBuild, + ): { + total: number; + passed: number; + skipped: number; + failed: number; + testUrl: string; + } { + return build.actions + .filter( + (action: any) => + action._class === 'hudson.tasks.junit.TestResultAction', + ) + .map((action: any) => { + return { + total: action.totalCount, + passed: action.totalCount - action.failCount - action.skipCount, + skipped: action.skipCount, + failed: action.failCount, + testUrl: `${build.url}${action.urlName}/`, + }; + }) + .pop(); + } +} diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts new file mode 100644 index 0000000000..7c4761a19a --- /dev/null +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts @@ -0,0 +1,269 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DefaultJenkinsInfoProvider, JenkinsInfo } from './jenkinsInfoProvider'; +import { CatalogClient } from '@backstage/catalog-client'; +import { ConfigReader } from '@backstage/config'; +import { Entity, EntityName } from '@backstage/catalog-model'; + +describe('DefaultJenkinsInfoProvider', () => { + const mockCatalog: jest.Mocked = ({ + getEntityByName: jest.fn(), + } as any) as jest.Mocked; + + const entityRef: EntityName = { + kind: 'Component', + namespace: 'foo', + name: 'bar', + }; + + function configureProvider(configData: any, entityData: any) { + const config = new ConfigReader(configData); + mockCatalog.getEntityByName.mockReturnValueOnce( + Promise.resolve(entityData as Entity), + ); + + return DefaultJenkinsInfoProvider.fromConfig({ + config, + catalog: mockCatalog, + }); + } + + it('Handles entity not found', async () => { + const provider = configureProvider({ jenkins: {} }, undefined); + await expect(provider.getInstance({ entityRef })).rejects.toThrowError(); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + }); + + it('Reads simple config and annotation', async () => { + const provider = configureProvider( + { + jenkins: { + baseUrl: 'https://jenkins.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/job-full-name': 'teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toStrictEqual({ + baseUrl: 'https://jenkins.example.com', + headers: { + Authorization: + 'Basic YmFja3N0YWdlIC0gYm90OjEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNlZGYwMTI=', + }, + jobFullName: 'teamA/artistLookup-build', + }); + }); + + it('Reads named default config and annotation', async () => { + const provider = configureProvider( + { + jenkins: { + instances: [ + { + name: 'default', + baseUrl: 'https://jenkins.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + ], + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/job-full-name': 'teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toMatchObject({ + baseUrl: 'https://jenkins.example.com', + jobFullName: 'teamA/artistLookup-build', + }); + }); + + it('Reads named default config (amongst named other configs) and annotation', async () => { + const provider = configureProvider( + { + jenkins: { + instances: [ + { + name: 'default', + baseUrl: 'https://jenkins.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + { + name: 'other', + baseUrl: 'https://jenkins-other.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + ], + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/job-full-name': 'teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toMatchObject({ + baseUrl: 'https://jenkins.example.com', + jobFullName: 'teamA/artistLookup-build', + }); + }); + + it('Reads named other config and named annotation', async () => { + const provider = configureProvider( + { + jenkins: { + instances: [ + { + name: 'default', + baseUrl: 'https://jenkins.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + { + name: 'other', + baseUrl: 'https://jenkins-other.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + ], + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/job-full-name': 'other:teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toMatchObject({ + baseUrl: 'https://jenkins-other.example.com', + jobFullName: 'teamA/artistLookup-build', + }); + }); + + it('Reads simple config and default named annotation', async () => { + const provider = configureProvider( + { + jenkins: { + baseUrl: 'https://jenkins.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/job-full-name': 'default:teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toMatchObject({ + baseUrl: 'https://jenkins.example.com', + jobFullName: 'teamA/artistLookup-build', + }); + }); + + it('Reads simple config and old annotation', async () => { + const provider = configureProvider( + { + jenkins: { + baseUrl: 'https://jenkins.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/github-folder': 'teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toMatchObject({ + baseUrl: 'https://jenkins.example.com', + jobFullName: 'teamA/artistLookup-build', + }); + }); + + it('Reads named other config (with on default config) and named annotation', async () => { + const provider = configureProvider( + { + jenkins: { + instances: [ + { + name: 'other', + baseUrl: 'https://jenkins-other.example.com', + username: 'backstage - bot', + apiKey: '123456789abcdef0123456789abcedf012', + }, + ], + }, + }, + { + metadata: { + annotations: { + 'jenkins.io/job-full-name': 'other:teamA/artistLookup-build', + }, + }, + }, + ); + const info: JenkinsInfo = await provider.getInstance({ entityRef }); + + expect(mockCatalog.getEntityByName).toBeCalledWith(entityRef); + expect(info).toMatchObject({ + baseUrl: 'https://jenkins-other.example.com', + jobFullName: 'teamA/artistLookup-build', + }); + }); +}); diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts new file mode 100644 index 0000000000..1f316aa6bc --- /dev/null +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts @@ -0,0 +1,232 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + Entity, + EntityName, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { CatalogClient } from '@backstage/catalog-client'; +import { Config } from '@backstage/config'; + +export interface JenkinsInfoProvider { + getInstance(options: { + /** + * The entity to get the info about. + */ + entityRef: EntityName; + /** + * A specific job to get. This is only passed in when we know about a job name we are interested in. + */ + jobFullName?: string; + }): Promise; +} + +export interface JenkinsInfo { + baseUrl: string; + headers?: Record; + jobFullName: string; // TODO: make this an array +} + +/** + * Use default config and annotations, build using fromConfig static function. + * + * This will fallback through various deprecated config and annotation schemes. + */ +export class DefaultJenkinsInfoProvider implements JenkinsInfoProvider { + static readonly OLD_JENKINS_ANNOTATION = 'jenkins.io/github-folder'; + static readonly NEW_JENKINS_ANNOTATION = 'jenkins.io/job-full-name'; + + private constructor( + private readonly config: { + name: string; + baseUrl: string; + username: string; + apiKey: string; + }[], + private readonly catalog: CatalogClient, + ) {} + + static fromConfig(options: { + config: Config; + catalog: CatalogClient; + }): DefaultJenkinsInfoProvider { + return new DefaultJenkinsInfoProvider( + this.loadConfig(options.config), + options.catalog, + ); + } + + async getInstance(opt: { + entityRef: EntityName; + jobFullName?: string; + }): Promise { + // load entity + const entity = await this.catalog.getEntityByName(opt.entityRef); + if (!entity) { + throw new Error( + `Couldn't find entity with name: ${stringifyEntityRef(opt.entityRef)}`, + ); + } + + // lookup `[jenkinsName#]jobFullName` from entity annotation + const jenkinsAndJobName = DefaultJenkinsInfoProvider.getEntityAnnotationValue( + entity, + ); + if (!jenkinsAndJobName) { + throw new Error( + `Couldn't find jenkins annotation (${ + DefaultJenkinsInfoProvider.NEW_JENKINS_ANNOTATION + }) on entity with name: ${stringifyEntityRef(opt.entityRef)}`, + ); + } + + let jobFullName; + let jenkinsName: string | undefined; + const splitIndex = jenkinsAndJobName.indexOf(':'); + if (splitIndex === -1) { + // no jenkinsName specified, use default + jobFullName = jenkinsAndJobName; + } else { + // There is a jenkinsName specified + jenkinsName = jenkinsAndJobName.substring(0, splitIndex); + jobFullName = jenkinsAndJobName.substring( + splitIndex + 1, + jenkinsAndJobName.length, + ); + } + + // lookup baseURL + creds from config + const instanceConfig = DefaultJenkinsInfoProvider.getInstanceConfig( + jenkinsName, + this.config, + ); + + const creds = Buffer.from( + `${instanceConfig.username}:${instanceConfig.apiKey}`, + 'binary', + ).toString('base64'); + + return { + baseUrl: instanceConfig.baseUrl, + headers: { + Authorization: `Basic ${creds}`, + }, + jobFullName, + }; + } + + private static getEntityAnnotationValue(entity: Entity) { + return ( + entity.metadata.annotations?.[ + DefaultJenkinsInfoProvider.OLD_JENKINS_ANNOTATION + ] || + entity.metadata.annotations?.[ + DefaultJenkinsInfoProvider.NEW_JENKINS_ANNOTATION + ] + ); + } + + private static getInstanceConfig( + jenkinsName: string | undefined, + config: { + name: string; + baseUrl: string; + username: string; + apiKey: string; + }[], + ): { name: string; baseUrl: string; username: string; apiKey: string } { + const DEFAULT_JENKINS_NAME = 'default'; + + if (!jenkinsName || jenkinsName === DEFAULT_JENKINS_NAME) { + // no name provided, use default + const instanceConfig = config.find(c => c.name === DEFAULT_JENKINS_NAME); + + if (!instanceConfig) { + throw new Error( + `Couldn't find a default jenkins instance in the config. Either configure an instance with name ${DEFAULT_JENKINS_NAME} or add a prefix to your annotation value.`, + ); + } + + return instanceConfig; + } + + // A name is provided, look it up. + const instanceConfig = config.find(c => c.name === jenkinsName); + + if (!instanceConfig) { + throw new Error( + `Couldn't find a jenkins instance in the config with name ${jenkinsName}`, + ); + } + return instanceConfig; + } + + private static loadConfig( + rootConfig: Config, + ): { name: string; baseUrl: string; username: string; apiKey: string }[] { + const DEFAULT_JENKINS_NAME = 'default'; + + const jenkinsConfig = rootConfig.getConfig('jenkins'); + + // load all named instance config + const namedInstanceConfig = + jenkinsConfig.getOptionalConfigArray('instances')?.map(c => ({ + name: c.getString('name'), + baseUrl: c.getString('baseUrl'), + username: c.getString('username'), + apiKey: c.getString('apiKey'), + })) || []; + + // load unnamed default config + const hasNamedDefault = namedInstanceConfig.some( + x => x.name === DEFAULT_JENKINS_NAME, + ); + + // Get these as optional strings and check to give a better error message + const baseUrl = jenkinsConfig.getOptionalString('baseUrl'); + const username = jenkinsConfig.getOptionalString('username'); + const apiKey = jenkinsConfig.getOptionalString('apiKey'); + + if (hasNamedDefault && (baseUrl || username || apiKey)) { + throw new Error( + `Found both a named jenkins instance with name ${DEFAULT_JENKINS_NAME} and top level baseUrl, username or apiKey config. Use only one style of config.`, + ); + } + + const unnamedNonePresent = !baseUrl && !username && !apiKey; + const unnamedAllPresent = baseUrl && username && apiKey; + if (!(unnamedAllPresent || unnamedNonePresent)) { + throw new Error( + `Found partial default jenkins config. All (or none) of baseUrl, username ans apiKey must be provided.`, + ); + } + + if (unnamedAllPresent) { + const unnamedInstanceConfig = [ + { name: DEFAULT_JENKINS_NAME, baseUrl, username, apiKey }, + ] as { + name: string; + baseUrl: string; + username: string; + apiKey: string; + }[]; + + return [...namedInstanceConfig, ...unnamedInstanceConfig]; + } + + return namedInstanceConfig; + } +} diff --git a/plugins/jenkins-backend/src/service/router.ts b/plugins/jenkins-backend/src/service/router.ts new file mode 100644 index 0000000000..cd250aea31 --- /dev/null +++ b/plugins/jenkins-backend/src/service/router.ts @@ -0,0 +1,128 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { errorHandler } from '@backstage/backend-common'; +import express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; +import { JenkinsInfoProvider } from './jenkinsInfoProvider'; +import { JenkinsApiImpl } from './jenkinsApi'; + +export interface RouterOptions { + logger: Logger; + jenkinsInfoProvider: JenkinsInfoProvider; +} + +export async function createRouter( + options: RouterOptions, +): Promise { + const { jenkinsInfoProvider } = options; + + const jenkinsApi = new JenkinsApiImpl(); + + const router = Router(); + router.use(express.json()); + + router.get( + '/v1/entity/:namespace/:kind/:name/projects', + async (request, response) => { + const { namespace, kind, name } = request.params; + const branch = request.query.branch; + let branchStr: string | undefined; + + if (branch === undefined) { + branchStr = undefined; + } else if (typeof branch === 'string') { + branchStr = branch; + } else { + // this was passed in as something weird -> 400 + // https://evanhahn.com/gotchas-with-express-query-parsing-and-how-to-avoid-them/ + response + .status(400) + .send('Something was unexpected about the branch queryString'); + + return; + } + + const jenkinsInfo = await jenkinsInfoProvider.getInstance({ + entityRef: { + kind, + namespace, + name, + }, + }); + const projects = await jenkinsApi.getProjects(jenkinsInfo, branchStr); + + response.json({ + projects: projects, + }); + }, + ); + + router.get( + '/v1/entity/:namespace/:kind/:name/job/:jobFullName/:buildNumber', + async (request, response) => { + const { + namespace, + kind, + name, + jobFullName, + buildNumber, + } = request.params; + + const jenkinsInfo = await jenkinsInfoProvider.getInstance({ + entityRef: { + kind, + namespace, + name, + }, + jobFullName, + }); + + const build = await jenkinsApi.getBuild( + jenkinsInfo, + jobFullName, + parseInt(buildNumber, 10), + ); + + response.json({ + build: build, + }); + }, + ); + + router.post( + '/v1/entity/:namespace/:kind/:name/job/:jobFullName/:buildNumber::rebuild', + async (request, response) => { + const { namespace, kind, name, jobFullName } = request.params; + + const jenkinsInfo = await jenkinsInfoProvider.getInstance({ + entityRef: { + kind, + namespace, + name, + }, + jobFullName, + }); + + await jenkinsApi.buildProject(jenkinsInfo, jobFullName); + response.json({}); + }, + ); + + router.use(errorHandler()); + return router; +} diff --git a/plugins/jenkins-backend/src/service/standaloneServer.ts b/plugins/jenkins-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..a1bc5dd256 --- /dev/null +++ b/plugins/jenkins-backend/src/service/standaloneServer.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createServiceBuilder } from '@backstage/backend-common'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; +import { EntityRef } from '@backstage/catalog-model'; +import { JenkinsInfo } from './jenkinsInfoProvider'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'jenkins-backend' }); + logger.debug('Starting application server...'); + const router = await createRouter({ + logger, + jenkinsInfoProvider: { + async getInstance(_: { entityRef: EntityRef }): Promise { + return { baseUrl: 'https://example.com/', jobFullName: 'build-foo' }; + }, + }, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/jenkins', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/jenkins-backend/src/setupTests.ts b/plugins/jenkins-backend/src/setupTests.ts new file mode 100644 index 0000000000..ba33cf996b --- /dev/null +++ b/plugins/jenkins-backend/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; diff --git a/plugins/jenkins-backend/src/types.ts b/plugins/jenkins-backend/src/types.ts new file mode 100644 index 0000000000..2a0c5ae7f9 --- /dev/null +++ b/plugins/jenkins-backend/src/types.ts @@ -0,0 +1,87 @@ +/* + * Copyright 2021 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ScmDetails { + url?: string; + displayName?: string; + author?: string; +} + +interface CommonBuild { + // standard Jenkins + timestamp: number; + building: boolean; + duration: number; + result?: string; + fullDisplayName: string; + displayName: string; + url: string; + number: number; +} + +export interface JenkinsBuild extends CommonBuild { + // read by us from jenkins but not passed to frontend + actions: any; +} + +/** + * A build as presented by this plugin to the backstage jenkins plugin + */ +export interface BackstageBuild extends CommonBuild { + // added by us + source?: { + branchName: string; + displayName: string; + url: string; + commit: { + hash: string; + }; + author: string; + }; + tests: { + passed: number; + skipped: number; + failed: number; + total: number; + testUrl: string; + }; + status: string; // == building ? 'running' : result, +} + +export interface CommonProject { + // standard Jenkins + lastBuild: CommonBuild; + displayName: string; + fullDisplayName: string; + fullName: string; + inQueue: boolean; +} + +export interface JenkinsProject extends CommonProject { + // standard Jenkins + lastBuild: JenkinsBuild; + + // read by us from jenkins but not passed to frontend + actions: object[]; +} + +export interface BackstageProject extends CommonProject { + // standard Jenkins + lastBuild: BackstageBuild; + + // added by us + status: string; // == inQueue ? 'queued' : lastBuild.building ? 'running' : lastBuild.result, +} diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index fbfb17926b..db61a470f7 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,85 @@ # @backstage/plugin-jenkins +## 0.5.0 + +### Minor Changes + +- 6c7f00eb0: ## Extract an entity-oriented Jenkins Backend + + Change the Jenkins plugin from talking directly with a single Jenkins instance (via the proxy) to having a specific + backend plugin which talks to the Jenkins instances. + + Existing users of the Jenkins plugin will need to configure a Jenkins backend instead of a proxy. + Typically, this means creating a `src/plugins/jenkins.ts` file, adding a reference to it to `src/index.ts` and changing app-config.yaml + + ### jenkins.ts + + ```typescript + import { + createRouter, + DefaultJenkinsInfoProvider, + } from '@backstage/plugin-jenkins-backend'; + import { CatalogClient } from '@backstage/catalog-client'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin({ + logger, + config, + discovery, + }: PluginEnvironment): Promise { + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + logger, + jenkinsInfoProvider: new DefaultJenkinsInfoProvider( + catalogClient, + config, + ), + }); + } + ``` + + ### app-config.yaml + + For example + + ```yaml + proxy: + '/jenkins/api': + target: 'https://jenkins.example.com:8080' # your Jenkins URL + changeOrigin: true + headers: + Authorization: Basic ${JENKINS_BASIC_AUTH_HEADER} + ``` + + Would become: + + ```yaml + jenkins: + baseUrl: https://jenkins.example.com:8080 + username: backstage-bot + apiKey: ${JENKINS_PASSWORD} + ``` + + ## Change JavaScript API + + As part of the above change, the JavaScript API exposed by the plugin as a possible place for customisation has changed. + The ApiRef now has an id of `plugin.jenkins.service2` and has entity-based functions to match the endpoints exposed by + the new backend plugin + + ## Change BuildWithStepsPage route + + The plugin originally provided a route to view a particular build of a particular branch so that if you wanted to view + the master branch of an entity annotated with `'jenkins.io/github-folder': teamA/artistLookup-build` you could typically + access `/catalog/default/component/artist-lookup/ci-cd/run/master/7` but would now have to access + `/catalog/default/component/artist-lookup/ci-cd/build/teamA%2FartistLookup-build%2Fmaster/7` + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index 69304cb847..1af511b16f 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -16,45 +16,54 @@ cd packages/app yarn add @backstage/plugin-jenkins ``` -2. Add the `EntityJenkinsContent` extension to the entity page in the app: +2. Add and configure the backend plugin according to it's instructions + +3. Add the `EntityJenkinsContent` extension to the `CI/CD` page and `EntityLatestJenkinsRunCard` to the `overview` page in the app (or wherever you'd prefer): + +Note that if you configured a custom JenkinsInfoProvider in step 2, you may need a custom isJenkinsAvailable. ```tsx // In packages/app/src/components/catalog/EntityPage.tsx -import { EntityJenkinsContent } from '@backstage/plugin-jenkins'; +import { + EntityJenkinsContent, + EntityLatestJenkinsRunCard, + isJenkinsAvailable, +} from '@backstage/plugin-jenkins'; // You can add the tab to any number of pages, the service page is shown as an // example here const serviceEntityPage = ( - {/* other tabs... */} - - + + {/* ... */} + + + + + + + {/* ... */} + + {/* other tabs... */} + + + + + + {/* ... */} + + + {/* ... */} + +); ``` -3. Add proxy configuration to `app-config.yaml` +4. Run app with `yarn start` +5. Add the Jenkins folder annotation to your `catalog-info.yaml`. -```yaml -proxy: - '/jenkins/api': - target: 'http://localhost:8080' # your Jenkins URL - changeOrigin: true - headers: - Authorization: Basic ${JENKINS_BASIC_AUTH_HEADER} -``` - -4. Add an environment variable which contains the Jenkins credentials (NOTE: - use an API token, not your password). Here `user` is the name of the user - created in Jenkins. - -```shell -export JENKINS_BASIC_AUTH_HEADER=$(echo -n user:api-token | base64) -``` - -5. Run the app with `yarn start` - -6. Add the Jenkins folder annotation to your `catalog-info.yaml`, (NOTE: - currently this plugin only supports folders and Git SCM) +Currently, this plugin only supports folders and Git SCM. +Note that if you configured a custom JenkinsInfoProvider in step 2, you may need to use a different annotation scheme here ```yaml apiVersion: backstage.io/v1alpha1 @@ -75,21 +84,6 @@ spec: 8. Click the component in the catalog. You should now see Jenkins builds, and a last build result for your master build. -Note: If you are not using environment variables, you can directly type the API -token into `app-config.yaml`. - -```yaml -proxy: - '/jenkins/api': - target: 'http://localhost:8080' # your Jenkins URL - changeOrigin: true - headers: - Authorization: Basic YWRtaW46MTFlYzI1NmU0Mzg1MDFjM2Y1Yzc2Yjc1MWE3ZTQ3YWY4Mw== -``` - -The string starting with `YWR...` is the base64 encoding of the user and their -API token, e.g. `admin:11ec256e438501c3f5c76b751a7e47af83`. - ## Features - View all runs inside a folder diff --git a/plugins/jenkins/api-report.md b/plugins/jenkins/api-report.md index 47ec8d655f..883e74071d 100644 --- a/plugins/jenkins/api-report.md +++ b/plugins/jenkins/api-report.md @@ -9,14 +9,21 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +import { EntityName } from '@backstage/catalog-model'; +import { EntityRef } from '@backstage/catalog-model'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntityJenkinsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityJenkinsContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLatestJenkinsRunCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLatestJenkinsRunCard: ({ branch, @@ -26,46 +33,95 @@ export const EntityLatestJenkinsRunCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "isJenkinsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isJenkinsAvailable: (entity: Entity) => boolean; export { isJenkinsAvailable }; export { isJenkinsAvailable as isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "JENKINS_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const JENKINS_ANNOTATION = 'jenkins.io/github-folder'; +// Warning: (ae-missing-release-tag) "JenkinsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) -export class JenkinsApi { - constructor(options: Options); +export interface JenkinsApi { + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (ae-forgotten-export) The symbol "Build" needs to be exported by the entry point index.d.ts + getBuild(options: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise; + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters + // Warning: (ae-forgotten-export) The symbol "Project" needs to be exported by the entry point index.d.ts + getProjects(options: { + entity: EntityRef; + filter: { + branch?: string; + }; + }): Promise; // (undocumented) - extractJobDetailsFromBuildName( - buildName: string, - ): { - jobName: string; - buildNumber: number; - }; - // (undocumented) - extractScmDetailsFromJob(jobDetails: any): any | undefined; - // (undocumented) - getBuild(buildName: string): Promise; - // (undocumented) - getFolder(folderName: string): Promise; - // (undocumented) - getJob(jobName: string): Promise; - // (undocumented) - getLastBuild(jobName: string): Promise; - // (undocumented) - mapJenkinsBuildToCITable( - jenkinsResult: any, - jobScmInfo?: any, - ): CITableBuildInfo; - // (undocumented) - retry(buildName: string): Promise; + retry(options: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise; } +// Warning: (ae-missing-release-tag) "jenkinsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const jenkinsApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "JenkinsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class JenkinsClient implements JenkinsApi { + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }); + // (undocumented) + getBuild({ + entity, + jobFullName, + buildNumber, + }: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise; + // (undocumented) + getProjects({ + entity, + filter, + }: { + entity: EntityName; + filter: { + branch?: string; + }; + }): Promise; + // (undocumented) + retry({ + entity, + jobFullName, + buildNumber, + }: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise; +} + +// Warning: (ae-missing-release-tag) "jenkinsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const jenkinsPlugin: BackstagePlugin< { @@ -76,6 +132,8 @@ const jenkinsPlugin: BackstagePlugin< export { jenkinsPlugin }; export { jenkinsPlugin as plugin }; +// Warning: (ae-missing-release-tag) "LatestRunCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LatestRunCard: ({ branch, @@ -85,6 +143,9 @@ export const LatestRunCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props) => JSX.Element; diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 6cc5436574..6030beee8e 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.4.7", + "version": "0.5.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,14 +32,13 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "jenkins": "^0.28.0", "luxon": "^1.25.0", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -48,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/jenkins/src/api/JenkinsApi.ts b/plugins/jenkins/src/api/JenkinsApi.ts index c378e15693..4b23fb4292 100644 --- a/plugins/jenkins/src/api/JenkinsApi.ts +++ b/plugins/jenkins/src/api/JenkinsApi.ts @@ -14,246 +14,205 @@ * limitations under the License. */ -import { CITableBuildInfo } from '../components/BuildsPage/lib/CITable'; -import { createApiRef, DiscoveryApi } from '@backstage/core-plugin-api'; - -const jenkins = require('jenkins'); +import { + createApiRef, + DiscoveryApi, + IdentityApi, +} from '@backstage/core-plugin-api'; +import { EntityName, EntityRef } from '@backstage/catalog-model'; export const jenkinsApiRef = createApiRef({ - id: 'plugin.jenkins.service', + id: 'plugin.jenkins.service2', description: 'Used by the Jenkins plugin to make requests', }); -const DEFAULT_PROXY_PATH = '/jenkins/api'; +export interface Build { + // standard Jenkins + timestamp: number; + building: boolean; + duration: number; + result?: string; + fullDisplayName: string; + displayName: string; + url: string; + number: number; -type Options = { - discoveryApi: DiscoveryApi; - /** - * Path to use for requests via the proxy, defaults to /jenkins/api - */ - proxyPath?: string; -}; - -export class JenkinsApi { - private readonly discoveryApi: DiscoveryApi; - private readonly proxyPath: string; - - constructor(options: Options) { - this.discoveryApi = options.discoveryApi; - this.proxyPath = options.proxyPath ?? DEFAULT_PROXY_PATH; - } - - private async getClient() { - const proxyUrl = await this.discoveryApi.getBaseUrl('proxy'); - return jenkins({ baseUrl: proxyUrl + this.proxyPath, promisify: true }); - } - - async retry(buildName: string) { - const client = await this.getClient(); - // looks like the current SDK only supports triggering a new build - // can't see any support for replay (re-running the specific build with the same SCM info) - return await client.job.build(buildName); - } - - async getLastBuild(jobName: string) { - const client = await this.getClient(); - const job = await client.job.get(jobName); - - const lastBuild = await client.build.get(jobName, job.lastBuild.number); - return lastBuild; - } - - extractScmDetailsFromJob(jobDetails: any): any | undefined { - const scmInfo = jobDetails.actions - .filter( - (action: any) => - action._class === 'jenkins.scm.api.metadata.ObjectMetadataAction', - ) - .map((action: any) => { - return { - url: action?.objectUrl, - // https://javadoc.jenkins.io/plugin/scm-api/jenkins/scm/api/metadata/ObjectMetadataAction.html - // branch name for regular builds, pull request title on pull requests - displayName: action?.objectDisplayName, - }; - }) - .pop(); - - if (!scmInfo) { - return undefined; - } - - const author = jobDetails.actions - .filter( - (action: any) => - action._class === - 'jenkins.scm.api.metadata.ContributorMetadataAction', - ) - .map((action: any) => { - return action.contributorDisplayName; - }) - .pop(); - - if (author) { - scmInfo.author = author; - } - - return scmInfo; - } - - async getJob(jobName: string) { - const client = await this.getClient(); - return client.job.get({ - name: jobName, - depth: 1, - }); - } - - async getFolder(folderName: string) { - const client = await this.getClient(); - const folder = await client.job.get({ - name: folderName, - // Filter only be the information we need, instead of loading all fields. - // Limit to only show the latest build for each job and only load 50 jobs - // at all. - // Whitespaces are only included for readablity here and stripped out - // before sending to Jenkins - tree: `jobs[ - actions[*], - builds[ - number, - url, - fullDisplayName, - building, - result, - actions[ - *[ - *[ - *[ - * - ] - ] - ] - ] - ]{0,1}, - jobs{0,1}, - name - ]{0,50} - `.replace(/\s/g, ''), - }); - const results = []; - - for (const jobDetails of folder.jobs) { - const jobScmInfo = this.extractScmDetailsFromJob(jobDetails); - if (jobDetails?.jobs) { - // skipping folders inside folders for now - } else { - for (const buildDetails of jobDetails.builds) { - const ciTable = this.mapJenkinsBuildToCITable( - buildDetails, - jobScmInfo, - ); - results.push(ciTable); - } - } - } - return results; - } - - private getTestReport( - jenkinsResult: any, - ): { - total: number; + // added by us + source?: { + branchName: string; + displayName: string; + url: string; + commit: { + hash: string; + }; + author: string; + }; + tests: { passed: number; skipped: number; failed: number; + total: number; testUrl: string; - } { - return jenkinsResult.actions - .filter( - (action: any) => - action._class === 'hudson.tasks.junit.TestResultAction', - ) - .map((action: any) => { - return { - total: action.totalCount, - passed: action.totalCount - action.failCount - action.skipCount, - skipped: action.skipCount, - failed: action.failCount, - testUrl: `${jenkinsResult.url}${action.urlName}/`, - }; - }) - .pop(); + }; + status: string; // == building ? 'running' : result, +} + +export interface Project { + // standard Jenkins + lastBuild: Build; + displayName: string; + fullDisplayName: string; + fullName: string; + inQueue: string; + // added by us + status: string; // == inQueue ? 'queued' : lastBuild.building ? 'running' : lastBuild.result, + onRestartClick: () => Promise; // TODO rename to handle.* ? also, should this be on lastBuild? +} + +export interface JenkinsApi { + /** + * Get the projects (jobs which have builds, not folders) including info about their lastBuild. + * + * Deciding what jobs are for an entity can be configured by the backstage _Integrator_ in the plugin-jenkins-backend setup + * and by the _Software Engineer_ using annotations agreed with the _Integrator_. + * + * Typically, a folder job will be identified and the backend plugin will recursively look for projects (jobs with builds) within that folder. + * + * @param options.entity the entity whose jobs should be retrieved. + * @param options.filter a filter on jobs. Currently this just takes a branch (and assumes certain structures in jenkins) + */ + getProjects(options: { + entity: EntityRef; + filter: { branch?: string }; + }): Promise; + + /** + * Get a single build. + * + * This takes an entity to support selecting between multiple jenkins instances. + * + * TODO: abstract jobFullName (so we could support differentiating between the same named job on multiple instances). + * @param options.entity + * @param options.jobFullName + * @param options.buildNumber + */ + getBuild(options: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise; + + retry(options: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise; +} + +export class JenkinsClient implements JenkinsApi { + private readonly discoveryApi: DiscoveryApi; + private readonly identityApi: IdentityApi; + + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }) { + this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; } - mapJenkinsBuildToCITable( - jenkinsResult: any, - jobScmInfo?: any, - ): CITableBuildInfo { - const source = - jenkinsResult.actions - .filter( - (action: any) => - action._class === 'hudson.plugins.git.util.BuildData', - ) - .map((action: any) => { - const [first]: any = Object.values(action.buildsByBranchName); - const branch = first.revision.branch[0]; - return { - branchName: branch.name, - commit: { - hash: branch.SHA1.substring(0, 8), - }, - }; - }) - .pop() || {}; + async getProjects({ + entity, + filter, + }: { + entity: EntityName; + filter: { branch?: string }; + }): Promise { + const url = new URL( + `${await this.discoveryApi.getBaseUrl( + 'jenkins', + )}/v1/entity/${encodeURIComponent(entity.namespace)}/${encodeURIComponent( + entity.kind, + )}/${encodeURIComponent(entity.name)}/projects`, + ); - if (jobScmInfo) { - source.url = jobScmInfo?.url; - source.displayName = jobScmInfo?.displayName; - source.author = jobScmInfo?.author; + if (filter.branch) { + url.searchParams.append('branch', filter.branch); } - const path = new URL(jenkinsResult.url).pathname; - - return { - id: path, - buildNumber: jenkinsResult.number, - buildUrl: jenkinsResult.url, - buildName: jenkinsResult.fullDisplayName, - status: jenkinsResult.building ? 'running' : jenkinsResult.result, - onRestartClick: () => { - // TODO: this won't handle non root context path, need a better way to get the job name - const { jobName } = this.extractJobDetailsFromBuildName(path); - return this.retry(jobName); + const idToken = await this.identityApi.getIdToken(); + const response = await fetch(url.href, { + method: 'GET', + headers: { + ...(idToken && { Authorization: `Bearer ${idToken}` }), }, - source: source, - tests: this.getTestReport(jenkinsResult), - }; + }); + + return ( + (await response.json()).projects?.map((p: Project) => ({ + ...p, + onRestartClick: async () => { + await this.retry({ + entity, + jobFullName: p.fullName, + buildNumber: String(p.lastBuild.number), + }); + }, + })) || [] + ); } - async getBuild(buildName: string) { - const client = await this.getClient(); - const { jobName, buildNumber } = this.extractJobDetailsFromBuildName( - buildName, - ); - const buildResult = await client.build.get(jobName, buildNumber); - return buildResult; + async getBuild({ + entity, + jobFullName, + buildNumber, + }: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise { + const url = `${await this.discoveryApi.getBaseUrl( + 'jenkins', + )}/v1/entity/${encodeURIComponent(entity.namespace)}/${encodeURIComponent( + entity.kind, + )}/${encodeURIComponent(entity.name)}/job/${encodeURIComponent( + jobFullName, + )}/${encodeURIComponent(buildNumber)}`; + + const idToken = await this.identityApi.getIdToken(); + const response = await fetch(url, { + method: 'GET', + headers: { + ...(idToken && { Authorization: `Bearer ${idToken}` }), + }, + }); + + return (await response.json()).build; } - extractJobDetailsFromBuildName(buildName: string) { - const trimmedBuild = buildName.replace(/\/job/g, '').replace(/\/$/, ''); + async retry({ + entity, + jobFullName, + buildNumber, + }: { + entity: EntityName; + jobFullName: string; + buildNumber: string; + }): Promise { + const url = `${await this.discoveryApi.getBaseUrl( + 'jenkins', + )}/v1/entity/${encodeURIComponent(entity.namespace)}/${encodeURIComponent( + entity.kind, + )}/${encodeURIComponent(entity.name)}/job/${encodeURIComponent( + jobFullName, + )}/${encodeURIComponent(buildNumber)}:rebuild`; - const split = trimmedBuild.split('/'); - const buildNumber = parseInt(split[split.length - 1], 10); - const jobName = trimmedBuild.slice( - 0, - trimmedBuild.length - buildNumber.toString(10).length - 1, - ); - - return { - jobName, - buildNumber, - }; + const idToken = await this.identityApi.getIdToken(); + await fetch(url, { + method: 'POST', + headers: { + ...(idToken && { Authorization: `Bearer ${idToken}` }), + }, + }); } } diff --git a/plugins/jenkins/src/api/index.ts b/plugins/jenkins/src/api/index.ts index 7bf664fbed..af1829c0b4 100644 --- a/plugins/jenkins/src/api/index.ts +++ b/plugins/jenkins/src/api/index.ts @@ -14,4 +14,6 @@ * limitations under the License. */ -export { JenkinsApi, jenkinsApiRef } from './JenkinsApi'; +export { JenkinsClient, jenkinsApiRef } from './JenkinsApi'; + +export type { JenkinsApi } from './JenkinsApi'; diff --git a/plugins/jenkins/src/components/BuildWithStepsPage/BuildWithStepsPage.tsx b/plugins/jenkins/src/components/BuildWithStepsPage/BuildWithStepsPage.tsx index 0bfd2bb5d8..e06332f8f0 100644 --- a/plugins/jenkins/src/components/BuildWithStepsPage/BuildWithStepsPage.tsx +++ b/plugins/jenkins/src/components/BuildWithStepsPage/BuildWithStepsPage.tsx @@ -30,7 +30,6 @@ import React from 'react'; import { buildRouteRef } from '../../plugin'; import { JenkinsRunStatus } from '../BuildsPage/lib/Status'; import { useBuildWithSteps } from '../useBuildWithSteps'; -import { useProjectSlugFromEntity } from '../useProjectSlugFromEntity'; import { Breadcrumbs, Content, Link } from '@backstage/core-components'; import { useRouteRefParams } from '@backstage/core-plugin-api'; @@ -49,17 +48,16 @@ const useStyles = makeStyles(theme => ({ })); const BuildWithStepsView = () => { - const projectName = useProjectSlugFromEntity(); - const { branch, buildNumber } = useRouteRefParams(buildRouteRef); + // TODO: Add a test that react-router decodes this (even though `generatePath` doesn't encode it for you!) + const { jobFullName, buildNumber } = useRouteRefParams(buildRouteRef); const classes = useStyles(); - const buildPath = `${projectName}/${encodeURIComponent( - branch, - )}/${buildNumber}`; - const [{ value }] = useBuildWithSteps(buildPath); + + const [{ value }] = useBuildWithSteps({ jobFullName, buildNumber }); return (
+ {/* TODO: don't hardcode this link */} Projects Run @@ -104,7 +102,7 @@ const BuildWithStepsView = () => { Jenkins - + View on Jenkins{' '} @@ -112,10 +110,11 @@ const BuildWithStepsView = () => { + {/* TODO: be SCM agnostic */} GitHub - + View on GitHub{' '} diff --git a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx index 6e456bd281..3e5eeffdaa 100644 --- a/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx +++ b/plugins/jenkins/src/components/BuildsPage/lib/CITable/CITable.tsx @@ -20,34 +20,9 @@ import JenkinsLogo from '../../../../assets/JenkinsLogo.svg'; import { generatePath, Link as RouterLink } from 'react-router-dom'; import { JenkinsRunStatus } from '../Status'; import { useBuilds } from '../../../useBuilds'; -import { useProjectSlugFromEntity } from '../../../useProjectSlugFromEntity'; import { buildRouteRef } from '../../../../plugin'; import { Table, TableColumn } from '@backstage/core-components'; - -export type CITableBuildInfo = { - id: string; - buildName: string; - buildNumber: number; - buildUrl: string; - source: { - branchName: string; - url: string; - displayName: string; - author?: string; - commit: { - hash: string; - }; - }; - status: string; - tests?: { - total: number; - passed: number; - skipped: number; - failed: number; - testUrl: string; - }; - onRestartClick: () => void; -}; +import { Project } from '../../../../api/JenkinsApi'; const FailCount = ({ count }: { count: number }): JSX.Element | null => { if (count !== 0) { @@ -107,44 +82,51 @@ const FailSkippedWidget = ({ const generatedColumns: TableColumn[] = [ { title: 'Build', - field: 'buildName', + field: 'fullName', highlight: true, - render: (row: Partial) => { - if (!row.source?.branchName || !row.buildNumber) { - return <>{row.buildName}; + render: (row: Partial) => { + if (!row.fullName || !row.lastBuild?.number) { + return ( + <> + {row.fullName || + row.fullDisplayName || + row.displayName || + 'Unknown'} + + ); } return ( - {row.buildName} + {row.fullDisplayName} ); }, }, { title: 'Source', - field: 'source.branchName', - render: (row: Partial) => ( + field: 'lastBuild.source.branchName', + render: (row: Partial) => ( <>

- - {row.source?.branchName} + + {row.lastBuild?.source?.branchName}

-

{row.source?.commit?.hash}

+

{row.lastBuild?.source?.commit?.hash}

), }, { title: 'Status', field: 'status', - render: (row: Partial) => { + render: (row: Partial) => { return ( @@ -155,21 +137,22 @@ const generatedColumns: TableColumn[] = [ { title: 'Tests', sorting: false, - render: (row: Partial) => { + render: (row: Partial) => { return ( <>

- {row.tests && ( - - {row.tests.passed} / {row.tests.total} passed + {row.lastBuild?.tests && ( + + {row.lastBuild?.tests.passed} / {row.lastBuild?.tests.total}{' '} + passed )} - {!row.tests && 'n/a'} + {!row.lastBuild?.tests && 'n/a'}

); @@ -178,7 +161,7 @@ const generatedColumns: TableColumn[] = [ { title: 'Actions', sorting: false, - render: (row: Partial) => ( + render: (row: Partial) => ( @@ -192,8 +175,7 @@ const generatedColumns: TableColumn[] = [ type Props = { loading: boolean; retry: () => void; - builds: CITableBuildInfo[]; - projectName: string; + projects?: Project[]; page: number; onChangePage: (page: number) => void; total: number; @@ -202,12 +184,11 @@ type Props = { }; export const CITableView = ({ - projectName, loading, pageSize, page, retry, - builds, + projects, onChangePage, onChangePageSize, total, @@ -226,14 +207,14 @@ export const CITableView = ({ onClick: () => retry(), }, ]} - data={builds ?? []} + data={projects ?? []} onChangePage={onChangePage} onChangeRowsPerPage={onChangePageSize} title={ Jenkins logo - Project: {projectName} + Projects } columns={generatedColumns} @@ -242,9 +223,7 @@ export const CITableView = ({ }; export const CITable = () => { - const projectName = useProjectSlugFromEntity(); - - const [tableProps, { setPage, retry, setPageSize }] = useBuilds(projectName); + const [tableProps, { setPage, retry, setPageSize }] = useBuilds(); return ( ', () => { const entity = { @@ -33,7 +34,10 @@ describe('', () => { }; const jenkinsApi: Partial = { - getLastBuild: () => Promise.resolve({ timestamp: 0, result: 'success' }), + getProjects: () => + Promise.resolve([ + { lastBuild: { timestamp: 0, status: 'success' } }, + ] as Project[]), }; it('should show success status of latest build', async () => { @@ -52,7 +56,7 @@ describe('', () => { it('should show the appropriate error in case of a connection error', async () => { const jenkinsApiWithError: Partial = { - getLastBuild: () => Promise.reject(new Error('Unauthorized')), + getProjects: () => Promise.reject(new Error('Unauthorized')), }; const apis = ApiRegistry.from([[jenkinsApiRef, jenkinsApiWithError]]); @@ -71,7 +75,7 @@ describe('', () => { it('should show the appropriate error in case Jenkins project is not found', async () => { const jenkinsApiWithError: Partial = { - getLastBuild: () => + getProjects: () => Promise.reject({ notFound: true, message: 'jenkins-project not found', diff --git a/plugins/jenkins/src/components/Cards/Cards.tsx b/plugins/jenkins/src/components/Cards/Cards.tsx index 21044a194d..f0b1ff2bca 100644 --- a/plugins/jenkins/src/components/Cards/Cards.tsx +++ b/plugins/jenkins/src/components/Cards/Cards.tsx @@ -19,13 +19,13 @@ import { DateTime, Duration } from 'luxon'; import React from 'react'; import { JenkinsRunStatus } from '../BuildsPage/lib/Status'; import { ErrorType, useBuilds } from '../useBuilds'; -import { useProjectSlugFromEntity } from '../useProjectSlugFromEntity'; import { InfoCard, InfoCardVariants, StructuredMetadataTable, WarningPanel, } from '@backstage/core-components'; +import { Project } from '../../api/JenkinsApi'; const useStyles = makeStyles({ externalLinkIcon: { @@ -39,16 +39,18 @@ const WidgetContent = ({ latestRun, }: { loading?: boolean; - latestRun: any; + latestRun?: Project; branch: string; }) => { const classes = useStyles(); if (loading || !latestRun) return ; - const displayDate = DateTime.fromMillis(latestRun.timestamp).toRelative(); + const displayDate = DateTime.fromMillis( + latestRun.lastBuild.timestamp, + ).toRelative(); const displayDuration = - (latestRun.building ? 'Running for ' : '') + + (latestRun.lastBuild.building ? 'Running for ' : '') + DateTime.local() - .minus(Duration.fromMillis(latestRun.duration)) + .minus(Duration.fromMillis(latestRun.lastBuild.duration)) .toRelative({ locale: 'en' }) ?.replace(' ago', ''); @@ -57,16 +59,14 @@ const WidgetContent = ({ metadata={{ status: ( <> - + ), build: latestRun.fullDisplayName, 'latest run': displayDate, duration: displayDuration, link: ( - + See more on Jenkins{' '} @@ -100,9 +100,8 @@ export const LatestRunCard = ({ branch: string; variant?: InfoCardVariants; }) => { - const projectName = useProjectSlugFromEntity(); - const [{ builds, loading, error }] = useBuilds(projectName, branch); - const latestRun = builds ?? {}; + const [{ projects, loading, error }] = useBuilds({ branch }); + const latestRun = projects?.[0]; return ( {!error ? ( diff --git a/plugins/jenkins/src/components/useBuildWithSteps.ts b/plugins/jenkins/src/components/useBuildWithSteps.ts index 9625d8abbb..c73a116aae 100644 --- a/plugins/jenkins/src/components/useBuildWithSteps.ts +++ b/plugins/jenkins/src/components/useBuildWithSteps.ts @@ -18,34 +18,36 @@ import { useAsyncRetry } from 'react-use'; import { jenkinsApiRef } from '../api'; import { useAsyncPolling } from './useAsyncPolling'; import { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { getEntityName } from '@backstage/catalog-model'; const INTERVAL_AMOUNT = 1500; -export function useBuildWithSteps(buildName: string) { + +/** + * Hook to expose a specific build. + * @param jobFullName the full name of the project (job with builds, not a folder). e.g. "department-A/team-1/project-foo/master" + * @param buildNumber the number of the build. e.g. "13" + */ +export function useBuildWithSteps({ + jobFullName, + buildNumber, +}: { + jobFullName: string; + buildNumber: string; +}) { const api = useApi(jenkinsApiRef); const errorApi = useApi(errorApiRef); + const { entity } = useEntity(); const getBuildWithSteps = useCallback(async () => { try { - const build = await api.getBuild(buildName); - - const { jobName } = api.extractJobDetailsFromBuildName(buildName); - const job = await api.getJob(jobName); - const jobInfo = api.extractScmDetailsFromJob(job); - - return Promise.resolve(api.mapJenkinsBuildToCITable(build, jobInfo)); + const entityName = await getEntityName(entity); + return api.getBuild({ entity: entityName, jobFullName, buildNumber }); } catch (e) { errorApi.post(e); return Promise.reject(e); } - }, [buildName, api, errorApi]); - - const restartBuild = async () => { - try { - await api.retry(buildName); - } catch (e) { - errorApi.post(e); - } - }; + }, [buildNumber, jobFullName, entity, api, errorApi]); const { loading, value, retry } = useAsyncRetry(() => getBuildWithSteps(), [ getBuildWithSteps, @@ -59,7 +61,6 @@ export function useBuildWithSteps(buildName: string) { return [ { loading, value, retry }, { - restartBuild, getBuildWithSteps, startPolling, stopPolling, diff --git a/plugins/jenkins/src/components/useBuilds.ts b/plugins/jenkins/src/components/useBuilds.ts index 49be4893a7..820cb099a1 100644 --- a/plugins/jenkins/src/components/useBuilds.ts +++ b/plugins/jenkins/src/components/useBuilds.ts @@ -17,13 +17,23 @@ import { useState } from 'react'; import { useAsyncRetry } from 'react-use'; import { jenkinsApiRef } from '../api'; import { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { getEntityName } from '@backstage/catalog-model'; export enum ErrorType { CONNECTION_ERROR, NOT_FOUND, } -export function useBuilds(projectName: string, branch?: string) { +/** + * Hook to expose the latest build for all the pipelines/projects for an entity. + * If `branch` is provided, the latest build for only that branch will be given (but still as a list) + * + * TODO: deprecate branch and add a generic filter concept. + */ +export function useBuilds({ branch }: { branch?: string } = {}) { + const { entity } = useEntity(); + const entityName = getEntityName(entity); const api = useApi(jenkinsApiRef); const errorApi = useApi(errorApiRef); @@ -35,27 +45,24 @@ export function useBuilds(projectName: string, branch?: string) { errorType: ErrorType; }>(); - const restartBuild = async (buildName: string) => { + const restartBuild = async (jobFullName: string, buildNumber: string) => { try { - await api.retry(buildName); + await api.retry({ entity: entityName, jobFullName, buildNumber }); } catch (e) { errorApi.post(e); } }; - const { loading, value: builds, retry } = useAsyncRetry(async () => { + const { loading, value: projects, retry } = useAsyncRetry(async () => { try { - let build; - if (branch) { - build = await api.getLastBuild(`${projectName}/${branch}`); - } else { - build = await api.getFolder(`${projectName}`); - } + const build = await api.getProjects({ + entity: getEntityName(entity), + filter: { branch }, + }); - const size = Array.isArray(build) ? build?.[0].build_num! : 1; - setTotal(size); + setTotal(build.length); - return build || []; + return build; } catch (e) { const errorType = e.notFound ? ErrorType.NOT_FOUND @@ -63,24 +70,22 @@ export function useBuilds(projectName: string, branch?: string) { setError({ message: e.message, errorType }); throw e; } - }, [api, errorApi, projectName, branch]); + }, [api, errorApi, entity, branch]); return [ { page, pageSize, loading, - builds, - projectName, + projects, total, error, }, { - builds, setPage, setPageSize, restartBuild, - retry, + retry, // fetch data again }, ] as const; } diff --git a/plugins/jenkins/src/plugin.ts b/plugins/jenkins/src/plugin.ts index 2548612896..e975361da2 100644 --- a/plugins/jenkins/src/plugin.ts +++ b/plugins/jenkins/src/plugin.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { JenkinsApi, jenkinsApiRef } from './api'; import { createApiFactory, createComponentExtension, @@ -22,7 +21,9 @@ import { createRoutableExtension, createRouteRef, discoveryApiRef, + identityApiRef, } from '@backstage/core-plugin-api'; +import { JenkinsClient, jenkinsApiRef } from './api'; export const rootRouteRef = createRouteRef({ path: '', @@ -30,9 +31,9 @@ export const rootRouteRef = createRouteRef({ }); export const buildRouteRef = createRouteRef({ - path: 'run/:branch/:buildNumber', - params: ['branch', 'buildNumber'], - title: 'Jenkins run', + path: 'build/:jobFullName/:buildNumber', + params: ['jobFullName', 'buildNumber'], + title: 'Jenkins build', }); export const jenkinsPlugin = createPlugin({ @@ -40,8 +41,9 @@ export const jenkinsPlugin = createPlugin({ apis: [ createApiFactory({ api: jenkinsApiRef, - deps: { discoveryApi: discoveryApiRef }, - factory: ({ discoveryApi }) => new JenkinsApi({ discoveryApi }), + deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, + factory: ({ discoveryApi, identityApi }) => + new JenkinsClient({ discoveryApi, identityApi }), }), ], routes: { diff --git a/plugins/kafka-backend/api-report.md b/plugins/kafka-backend/api-report.md index f817da4a3b..4065244e6a 100644 --- a/plugins/kafka-backend/api-report.md +++ b/plugins/kafka-backend/api-report.md @@ -7,6 +7,9 @@ import { Config } from '@backstage/config'; import express from 'express'; import { Logger as Logger_2 } from 'winston'; +// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index c7cb9c99ad..e81a64ef58 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-kafka +## 0.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.10 ### Patch Changes diff --git a/plugins/kafka/api-report.md b/plugins/kafka/api-report.md index 2a1001773e..6bc169dd24 100644 --- a/plugins/kafka/api-report.md +++ b/plugins/kafka/api-report.md @@ -9,20 +9,28 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntityKafkaContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityKafkaContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "isPluginApplicableToEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isPluginApplicableToEntity: (entity: Entity) => boolean; export { isPluginApplicableToEntity as isKafkaAvailable }; export { isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "KAFKA_CONSUMER_GROUP_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const KAFKA_CONSUMER_GROUP_ANNOTATION = 'kafka.apache.org/consumer-groups'; +// Warning: (ae-missing-release-tag) "kafkaPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const kafkaPlugin: BackstagePlugin< { @@ -33,6 +41,9 @@ const kafkaPlugin: BackstagePlugin< export { kafkaPlugin }; export { kafkaPlugin as plugin }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props) => JSX.Element; diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 882edcd462..a7328b0c0c 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kafka", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -34,10 +34,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index dff8e1833d..b5870c43e3 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-kubernetes-backend +## 0.3.11 + +### Patch Changes + +- 5bd57f8f5: Support assume role on kubernetes api configuration for AWS. +- Updated dependencies + - @backstage/backend-common@0.8.7 + +## 0.3.10 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + ## 0.3.9 ### Patch Changes diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index bd8a7038c5..72d80050d8 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -10,6 +10,16 @@ import { KubernetesFetchError } from '@backstage/plugin-kubernetes-common'; import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { Logger as Logger_2 } from 'winston'; +// Warning: (ae-missing-release-tag) "AWSClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface AWSClusterDetails extends ClusterDetails { + // (undocumented) + assumeRole?: string; +} + +// Warning: (ae-missing-release-tag) "ClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ClusterDetails { // (undocumented) @@ -24,9 +34,13 @@ export interface ClusterDetails { url: string; } +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "CustomResource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CustomResource { // (undocumented) @@ -37,6 +51,8 @@ export interface CustomResource { plural: string; } +// Warning: (ae-missing-release-tag) "FetchResponseWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface FetchResponseWrapper { // (undocumented) @@ -45,12 +61,21 @@ export interface FetchResponseWrapper { responses: FetchResponse[]; } +// Warning: (ae-missing-release-tag) "GKEClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface GKEClusterDetails extends ClusterDetails {} + +// Warning: (ae-missing-release-tag) "KubernetesClustersSupplier" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface KubernetesClustersSupplier { // (undocumented) getClusters(): Promise; } +// Warning: (ae-missing-release-tag) "KubernetesFetcher" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface KubernetesFetcher { // (undocumented) @@ -59,6 +84,8 @@ export interface KubernetesFetcher { ): Promise; } +// Warning: (ae-missing-release-tag) "KubernetesObjectTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type KubernetesObjectTypes = | 'pods' @@ -70,12 +97,17 @@ export type KubernetesObjectTypes = | 'ingresses' | 'customresources'; +// Warning: (ae-missing-release-tag) "KubernetesServiceLocator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface KubernetesServiceLocator { // (undocumented) getClustersByServiceId(serviceId: string): Promise; } +// Warning: (ae-forgotten-export) The symbol "KubernetesFanOutHandler" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "makeRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const makeRouter: ( logger: Logger_2, @@ -83,10 +115,16 @@ export const makeRouter: ( clusterDetails: ClusterDetails[], ) => express.Router; +// Warning: (ae-missing-release-tag) "ObjectFetchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ObjectFetchParams { // (undocumented) - clusterDetails: ClusterDetails; + clusterDetails: + | AWSClusterDetails + | GKEClusterDetails + | ServiceAccountClusterDetails + | ClusterDetails; // (undocumented) customResources: CustomResource[]; // (undocumented) @@ -97,6 +135,8 @@ export interface ObjectFetchParams { serviceId: string; } +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) @@ -107,6 +147,13 @@ export interface RouterOptions { logger: Logger_2; } +// Warning: (ae-missing-release-tag) "ServiceAccountClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ServiceAccountClusterDetails extends ClusterDetails {} + +// Warning: (ae-missing-release-tag) "ServiceLocatorMethod" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ServiceLocatorMethod = 'multiTenant' | 'http'; diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 9888447464..1467dba9d6 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.3.9", + "version": "0.3.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,12 +31,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", + "@backstage/backend-common": "^0.8.7", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/plugin-kubernetes-common": "^0.1.2", "@google-cloud/container": "^2.2.0", - "@kubernetes/client-node": "^0.14.0", + "@kubernetes/client-node": "^0.15.0", "@types/express": "^4.17.6", "aws-sdk": "^2.840.0", "aws4": "^1.11.0", @@ -53,9 +53,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.0", + "@backstage/cli": "^0.7.5", "@types/aws4": "^1.5.1", - "supertest": "^6.1.3" + "supertest": "^6.1.3", + "aws-sdk-mock": "^5.2.1", + "bdd-lazy-var": "^2.6.0" }, "files": [ "dist", diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts index ac4a742828..1642ca5580 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts @@ -97,4 +97,48 @@ describe('ConfigClusterLocator', () => { }, ]); }); + + it('one aws cluster with assumeRole and one without', async () => { + const config: Config = new ConfigReader({ + clusters: [ + { + name: 'cluster1', + serviceAccountToken: 'token', + url: 'http://localhost:8080', + authProvider: 'aws', + skipTLSVerify: false, + }, + { + assumeRole: 'SomeRole', + name: 'cluster2', + url: 'http://localhost:8081', + authProvider: 'aws', + skipTLSVerify: true, + }, + ], + }); + + const sut = ConfigClusterLocator.fromConfig(config); + + const result = await sut.getClusters(); + + expect(result).toStrictEqual([ + { + assumeRole: undefined, + name: 'cluster1', + serviceAccountToken: 'token', + url: 'http://localhost:8080', + authProvider: 'aws', + skipTLSVerify: false, + }, + { + assumeRole: 'SomeRole', + name: 'cluster2', + serviceAccountToken: undefined, + url: 'http://localhost:8081', + authProvider: 'aws', + skipTLSVerify: true, + }, + ]); + }); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts index 268cca0193..2899445b2a 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts @@ -29,13 +29,32 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier { // is required if authProvider is serviceAccount return new ConfigClusterLocator( config.getConfigArray('clusters').map(c => { - return { + const authProvider = c.getString('authProvider'); + const clusterDetails = { name: c.getString('name'), url: c.getString('url'), serviceAccountToken: c.getOptionalString('serviceAccountToken'), skipTLSVerify: c.getOptionalBoolean('skipTLSVerify') ?? false, - authProvider: c.getString('authProvider'), + authProvider: authProvider, }; + + switch (authProvider) { + case 'google': { + return clusterDetails; + } + case 'aws': { + const assumeRole = c.getOptionalString('assumeRole'); + return { assumeRole, ...clusterDetails }; + } + case 'serviceAccount': { + return clusterDetails; + } + default: { + throw new Error( + `authProvider "${authProvider}" has no config associated with it`, + ); + } + } }), ); } diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts index 92212d0224..3cc6c216ae 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts @@ -16,7 +16,7 @@ import { Config } from '@backstage/config'; import * as container from '@google-cloud/container'; -import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; +import { GKEClusterDetails, KubernetesClustersSupplier } from '../types/types'; type GkeClusterLocatorOptions = { projectId: string; @@ -49,7 +49,7 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { ); } - async getClusters(): Promise { + async getClusters(): Promise { const { projectId, region, skipTLSVerify } = this.options; const request = { parent: `projects/${projectId}/locations/${region}`, diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.test.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.test.ts index 348ad541d5..9060ce9239 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.test.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.test.ts @@ -14,15 +14,58 @@ * limitations under the License. */ import AWS from 'aws-sdk'; +import AWSMock from 'aws-sdk-mock'; import { AwsIamKubernetesAuthTranslator } from './AwsIamKubernetesAuthTranslator'; +import { get, def } from 'bdd-lazy-var'; describe('AwsIamKubernetesAuthTranslator tests', () => { + let role: any = undefined; + const credentials: any = { + accessKeyId: 'bloop', + secretAccessKey: 'omg-so-secret', + sessionToken: 'token', + }; + + let assumeResponse: any = { + Credentials: { + AccessKeyId: credentials.accessKeyId, + SecretAccessKey: credentials.secretAccessKey, + SessionToken: credentials.sessionToken, + }, + }; + + let credentialsResponse: any = new AWS.Credentials(credentials); + + AWSMock.setSDKInstance(AWS); + beforeEach(() => { jest.resetAllMocks(); }); - it('returns a signed url for aws credentials', async () => { + + afterAll(() => { + jest.resetAllMocks(); + }); + + def('subject', () => { + AWSMock.mock('STS', 'assumeRole', (_params: any, callback: Function) => { + callback(null, assumeResponse); + }); + const authTranslator = new AwsIamKubernetesAuthTranslator(); + jest + .spyOn(authTranslator, 'awsGetCredentials') + .mockImplementation(async () => credentialsResponse); + + return authTranslator.decorateClusterDetailsWithAuth({ + assumeRole: role, + name: 'test-cluster', + url: '', + authProvider: 'aws', + }); + }); + + it('returns a signed url for aws credentials', async () => { // These credentials are not real. // Pulled from example in docs: https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html AWS.config.credentials = new AWS.Credentials( @@ -30,24 +73,47 @@ describe('AwsIamKubernetesAuthTranslator tests', () => { 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', ); - const clusterDetails = await authTranslator.decorateClusterDetailsWithAuth({ - name: 'test-cluster', - url: '', - authProvider: 'aws', - }); - expect(clusterDetails.serviceAccountToken).toBeDefined(); + const subject = await get('subject'); + expect(subject.serviceAccountToken).toBeDefined(); }); - it('throws when unable to get aws credentials', async () => { - AWS.config.credentials = undefined; - const authTranslator = new AwsIamKubernetesAuthTranslator(); - const promise = authTranslator.decorateClusterDetailsWithAuth({ - name: 'test-cluster', - url: '', - authProvider: 'aws', - }); - await expect(promise).rejects.toThrow( - 'Could not load credentials from any providers', + describe('When the role is assumed', () => { + // These credentials are not real. + // Pulled from example in docs: https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html + AWS.config.credentials = new AWS.Credentials( + 'AKIAIOSFODNN7EXAMPLE', + 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', ); + role = 'SomeRole'; + + describe('When the role is valid', () => { + it('returns a signed url for aws credentials', async () => { + const subject = await get('subject'); + expect(subject.serviceAccountToken).toBeDefined(); + }); + }); + + describe('When the role is invalid', () => { + it('returns the original AWS credentials', async () => { + assumeResponse = undefined; + await expect(get('subject')).rejects.toThrow(/Unable to assume role:/); + }); + }); + }); + + describe('When no creds are returned from AWS', () => { + it('throws unable to get aws credentials', async () => { + credentialsResponse = new Error(); + await expect(get('subject')).rejects.toThrow('No AWS credentials found.'); + }); + }); + + describe('When invalid creds are returned from AWS', () => { + it('throws credentials are invalid to get aws credentials', async () => { + credentialsResponse = new AWS.Credentials(credentialsResponse); + await expect(get('subject')).rejects.toThrow( + 'Invalid AWS credentials found.', + ); + }); }); }); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.ts index 0909926d0d..101bed32a3 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AwsIamKubernetesAuthTranslator.ts @@ -15,7 +15,7 @@ */ import AWS, { Credentials } from 'aws-sdk'; import { sign } from 'aws4'; -import { ClusterDetails } from '../types/types'; +import { AWSClusterDetails } from '../types/types'; import { KubernetesAuthTranslator } from './types'; const base64 = (str: string) => @@ -29,23 +29,78 @@ const pipe = (fns: ReadonlyArray) => (thing: string): string => const removePadding = replace(/=+$/, ''); const makeUrlSafe = pipe([replace('+', '-'), replace('/', '_')]); +type SigningCreds = { + accessKeyId: string | undefined; + secretAccessKey: string | undefined; + sessionToken: string | undefined; +}; + export class AwsIamKubernetesAuthTranslator implements KubernetesAuthTranslator { - async getBearerToken(clusterName: string): Promise { - const credentials = await new Promise((resolve, reject) => { + validCredentials(creds: SigningCreds): boolean { + return ((creds?.accessKeyId && + creds?.secretAccessKey && + creds?.sessionToken) as unknown) as boolean; + } + + awsGetCredentials = async (): Promise => { + return new Promise((resolve, reject) => { AWS.config.getCredentials(err => { if (err) { - reject(err); - } else { - resolve(AWS.config.credentials); + return reject(err); } + + return resolve(AWS.config.credentials as Credentials); }); }); + }; + + async getCredentials(assumeRole: string | undefined): Promise { + return new Promise(async (resolve, reject) => { + const awsCreds = await this.awsGetCredentials(); + + if (!(awsCreds instanceof Credentials)) + return reject(Error('No AWS credentials found.')); + + let creds: SigningCreds = { + accessKeyId: awsCreds.accessKeyId, + secretAccessKey: awsCreds.secretAccessKey, + sessionToken: awsCreds.sessionToken, + }; + + if (!this.validCredentials(creds)) + return reject(Error('Invalid AWS credentials found.')); + if (!assumeRole) return resolve(creds); + + try { + const params = { + RoleArn: assumeRole, + RoleSessionName: 'backstage-login', + }; + const assumedRole = await new AWS.STS().assumeRole(params).promise(); + + if (!assumedRole.Credentials) { + throw new Error(`No credentials returned for role ${assumeRole}`); + } + + creds = { + accessKeyId: assumedRole.Credentials.AccessKeyId, + secretAccessKey: assumedRole.Credentials.SecretAccessKey, + sessionToken: assumedRole.Credentials.SessionToken, + }; + } catch (e) { + console.warn(`There was an error assuming the role: ${e}`); + return reject(Error(`Unable to assume role: ${e}`)); + } + return resolve(creds); + }); + } + async getBearerToken( + clusterName: string, + assumeRole: string | undefined, + ): Promise { + const credentials = await this.getCredentials(assumeRole); - if (!(credentials instanceof Credentials)) { - throw new Error('no AWS credentials found.'); - } - await credentials.getPromise(); const request = { host: `sts.amazonaws.com`, path: `/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Expires=60`, @@ -54,11 +109,8 @@ export class AwsIamKubernetesAuthTranslator }, signQuery: true, }; - const signedRequest = sign(request, { - accessKeyId: credentials.accessKeyId, - secretAccessKey: credentials.secretAccessKey, - sessionToken: credentials.sessionToken, - }); + + const signedRequest = sign(request, credentials); return pipe([ (signed: any) => `https://${signed.host}${signed.path}`, @@ -70,15 +122,16 @@ export class AwsIamKubernetesAuthTranslator } async decorateClusterDetailsWithAuth( - clusterDetails: ClusterDetails, - ): Promise { - const clusterDetailsWithAuthToken: ClusterDetails = Object.assign( + clusterDetails: AWSClusterDetails, + ): Promise { + const clusterDetailsWithAuthToken: AWSClusterDetails = Object.assign( {}, clusterDetails, ); clusterDetailsWithAuthToken.serviceAccountToken = await this.getBearerToken( clusterDetails.name, + clusterDetails.assumeRole, ); return clusterDetailsWithAuthToken; } diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts index 02a7f314af..eacba4d3e1 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts @@ -15,16 +15,16 @@ */ import { KubernetesAuthTranslator } from './types'; -import { ClusterDetails } from '../types/types'; +import { GKEClusterDetails } from '../types/types'; import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class GoogleKubernetesAuthTranslator implements KubernetesAuthTranslator { async decorateClusterDetailsWithAuth( - clusterDetails: ClusterDetails, + clusterDetails: GKEClusterDetails, requestBody: KubernetesRequestBody, - ): Promise { - const clusterDetailsWithAuthToken: ClusterDetails = Object.assign( + ): Promise { + const clusterDetailsWithAuthToken: GKEClusterDetails = Object.assign( {}, clusterDetails, ); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts index c433abf4de..1c3add3c0d 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts @@ -15,18 +15,18 @@ */ import { KubernetesAuthTranslator } from './types'; -import { ClusterDetails } from '../types/types'; +import { ServiceAccountClusterDetails } from '../types/types'; import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class ServiceAccountKubernetesAuthTranslator implements KubernetesAuthTranslator { async decorateClusterDetailsWithAuth( - clusterDetails: ClusterDetails, + clusterDetails: ServiceAccountClusterDetails, // To ignore TS6133 linting error where it detects 'requestBody' is declared but its value is never read. // @ts-ignore-start requestBody: KubernetesRequestBody, // eslint-disable-line @typescript-eslint/no-unused-vars // @ts-ignore-end - ): Promise { + ): Promise { return clusterDetails; } } diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index c6e1668a5a..6409229968 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -27,7 +27,11 @@ export interface CustomResource { export interface ObjectFetchParams { serviceId: string; - clusterDetails: ClusterDetails; + clusterDetails: + | AWSClusterDetails + | GKEClusterDetails + | ServiceAccountClusterDetails + | ClusterDetails; objectTypesToFetch: Set; labelSelector: string; customResources: CustomResource[]; @@ -77,3 +81,9 @@ export interface ClusterDetails { serviceAccountToken?: string | undefined; skipTLSVerify?: boolean; } + +export interface GKEClusterDetails extends ClusterDetails {} +export interface ServiceAccountClusterDetails extends ClusterDetails {} +export interface AWSClusterDetails extends ClusterDetails { + assumeRole?: string; +} diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index 34d6bd8414..04f54b1f1a 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -12,9 +12,13 @@ import { V1Pod } from '@kubernetes/client-node'; import { V1ReplicaSet } from '@kubernetes/client-node'; import { V1Service } from '@kubernetes/client-node'; +// Warning: (ae-missing-release-tag) "AuthProviderType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type AuthProviderType = 'google' | 'serviceAccount' | 'aws'; +// Warning: (ae-missing-release-tag) "ClusterObjects" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ClusterObjects { // (undocumented) @@ -27,6 +31,8 @@ export interface ClusterObjects { resources: FetchResponse[]; } +// Warning: (ae-missing-release-tag) "ConfigMapFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ConfigMapFetchResponse { // (undocumented) @@ -35,6 +41,8 @@ export interface ConfigMapFetchResponse { type: 'configmaps'; } +// Warning: (ae-missing-release-tag) "CustomResourceFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface CustomResourceFetchResponse { // (undocumented) @@ -43,6 +51,8 @@ export interface CustomResourceFetchResponse { type: 'customresources'; } +// Warning: (ae-missing-release-tag) "DeploymentFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface DeploymentFetchResponse { // (undocumented) @@ -51,6 +61,8 @@ export interface DeploymentFetchResponse { type: 'deployments'; } +// Warning: (ae-missing-release-tag) "FetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type FetchResponse = | PodFetchResponse @@ -62,6 +74,8 @@ export type FetchResponse = | IngressesFetchResponse | CustomResourceFetchResponse; +// Warning: (ae-missing-release-tag) "HorizontalPodAutoscalersFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface HorizontalPodAutoscalersFetchResponse { // (undocumented) @@ -70,6 +84,8 @@ export interface HorizontalPodAutoscalersFetchResponse { type: 'horizontalpodautoscalers'; } +// Warning: (ae-missing-release-tag) "IngressesFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface IngressesFetchResponse { // (undocumented) @@ -78,6 +94,8 @@ export interface IngressesFetchResponse { type: 'ingresses'; } +// Warning: (ae-missing-release-tag) "KubernetesErrorTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type KubernetesErrorTypes = | 'BAD_REQUEST' @@ -85,6 +103,8 @@ export type KubernetesErrorTypes = | 'SYSTEM_ERROR' | 'UNKNOWN_ERROR'; +// Warning: (ae-missing-release-tag) "KubernetesFetchError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface KubernetesFetchError { // (undocumented) @@ -95,6 +115,8 @@ export interface KubernetesFetchError { statusCode?: number; } +// Warning: (ae-missing-release-tag) "KubernetesRequestBody" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface KubernetesRequestBody { // (undocumented) @@ -105,12 +127,16 @@ export interface KubernetesRequestBody { entity: Entity; } +// Warning: (ae-missing-release-tag) "ObjectsByEntityResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ObjectsByEntityResponse { // (undocumented) items: ClusterObjects[]; } +// Warning: (ae-missing-release-tag) "PodFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface PodFetchResponse { // (undocumented) @@ -119,6 +145,8 @@ export interface PodFetchResponse { type: 'pods'; } +// Warning: (ae-missing-release-tag) "ReplicaSetsFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ReplicaSetsFetchResponse { // (undocumented) @@ -127,6 +155,8 @@ export interface ReplicaSetsFetchResponse { type: 'replicasets'; } +// Warning: (ae-missing-release-tag) "ServiceFetchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ServiceFetchResponse { // (undocumented) diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 1b645690b6..5321c48e73 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@kubernetes/client-node": "^0.14.0" + "@kubernetes/client-node": "^0.15.0" }, "devDependencies": { "@backstage/cli": "^0.7.0" diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index dd9f4d8bfa..a370dd9af5 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-kubernetes +## 0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/kubernetes/api-report.md b/plugins/kubernetes/api-report.md index 9fe56bc5ca..0a3b466942 100644 --- a/plugins/kubernetes/api-report.md +++ b/plugins/kubernetes/api-report.md @@ -12,11 +12,16 @@ import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { OAuthApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntityKubernetesContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityKubernetesContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "KubernetesAuthProvidersApi" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "KubernetesAuthProviders" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { constructor(options: { googleAuthApi: OAuthApi }); @@ -27,9 +32,13 @@ export class KubernetesAuthProviders implements KubernetesAuthProvidersApi { ): Promise; } +// Warning: (ae-missing-release-tag) "kubernetesAuthProvidersApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const kubernetesAuthProvidersApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "kubernetesPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const kubernetesPlugin: BackstagePlugin< { @@ -40,6 +49,9 @@ const kubernetesPlugin: BackstagePlugin< export { kubernetesPlugin }; export { kubernetesPlugin as plugin }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props) => JSX.Element; diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index fdfb2a3431..ee58c6b222 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.4.7", + "version": "0.4.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "dependencies": { "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/plugin-kubernetes-common": "^0.1.2", "@backstage/theme": "^0.2.8", - "@kubernetes/client-node": "^0.14.0", + "@kubernetes/client-node": "^0.15.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -50,10 +50,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 9bee12436d..ea2d2efac5 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-lighthouse +## 0.2.20 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.19 ### Patch Changes diff --git a/plugins/lighthouse/api-report.md b/plugins/lighthouse/api-report.md index 840daa3987..dca8f80471 100644 --- a/plugins/lighthouse/api-report.md +++ b/plugins/lighthouse/api-report.md @@ -12,9 +12,14 @@ import { Entity } from '@backstage/catalog-model'; import { InfoCardVariants } from '@backstage/core-components'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "Audit" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Audit = AuditRunning | AuditFailed | AuditCompleted; +// Warning: (ae-forgotten-export) The symbol "AuditBase" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "AuditCompleted" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AuditCompleted extends AuditBase { // (undocumented) @@ -27,6 +32,8 @@ export interface AuditCompleted extends AuditBase { timeCompleted: string; } +// Warning: (ae-missing-release-tag) "AuditFailed" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AuditFailed extends AuditBase { // (undocumented) @@ -35,15 +42,22 @@ export interface AuditFailed extends AuditBase { timeCompleted: string; } +// Warning: (ae-missing-release-tag) "AuditRunning" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface AuditRunning extends AuditBase { // (undocumented) status: 'RUNNING'; } +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddedRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EmbeddedRouter: (_props: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLastLighthouseAuditCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLastLighthouseAuditCard: ({ dense, @@ -53,11 +67,15 @@ export const EntityLastLighthouseAuditCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityLighthouseContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityLighthouseContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "FetchError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class FetchError extends Error { // (undocumented) @@ -66,11 +84,15 @@ export class FetchError extends Error { get name(): string; } +// Warning: (ae-missing-release-tag) "isLighthouseAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isLighthouseAvailable: (entity: Entity) => boolean; export { isLighthouseAvailable }; export { isLighthouseAvailable as isPluginApplicableToEntity }; +// Warning: (ae-missing-release-tag) "LASListRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface LASListRequest { // (undocumented) @@ -79,6 +101,8 @@ export interface LASListRequest { offset?: number; } +// Warning: (ae-missing-release-tag) "LASListResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface LASListResponse { // (undocumented) @@ -91,6 +115,8 @@ export interface LASListResponse { total: number; } +// Warning: (ae-missing-release-tag) "LastLighthouseAuditCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LastLighthouseAuditCard: ({ dense, @@ -100,6 +126,8 @@ export const LastLighthouseAuditCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "LighthouseApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LighthouseApi = { url: string; @@ -109,9 +137,13 @@ export type LighthouseApi = { getWebsiteByUrl: (websiteUrl: string) => Promise; }; +// Warning: (ae-missing-release-tag) "lighthouseApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const lighthouseApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "LighthouseCategoryAbbr" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface LighthouseCategoryAbbr { // (undocumented) @@ -122,6 +154,8 @@ export interface LighthouseCategoryAbbr { title: string; } +// Warning: (ae-missing-release-tag) "LighthouseCategoryId" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type LighthouseCategoryId = | 'pwa' @@ -130,9 +164,13 @@ export type LighthouseCategoryId = | 'accessibility' | 'best-practices'; +// Warning: (ae-missing-release-tag) "LighthousePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const LighthousePage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "lighthousePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const lighthousePlugin: BackstagePlugin< { @@ -144,6 +182,8 @@ const lighthousePlugin: BackstagePlugin< export { lighthousePlugin }; export { lighthousePlugin as plugin }; +// Warning: (ae-missing-release-tag) "LighthouseRestApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class LighthouseRestApi implements LighthouseApi { constructor(url: string); @@ -164,9 +204,13 @@ export class LighthouseRestApi implements LighthouseApi { url: string; } +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: () => JSX.Element; +// Warning: (ae-missing-release-tag) "TriggerAuditPayload" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TriggerAuditPayload { // (undocumented) @@ -181,6 +225,8 @@ export interface TriggerAuditPayload { url: string; } +// Warning: (ae-missing-release-tag) "Website" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface Website { // (undocumented) @@ -191,6 +237,8 @@ export interface Website { url: string; } +// Warning: (ae-missing-release-tag) "WebsiteListResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type WebsiteListResponse = LASListResponse; diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index f648041655..8ceaa35d20 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.2.19", + "version": "0.2.20", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,9 +33,9 @@ "dependencies": { "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.4", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/newrelic/api-report.md b/plugins/newrelic/api-report.md index 005097c442..94c3278df1 100644 --- a/plugins/newrelic/api-report.md +++ b/plugins/newrelic/api-report.md @@ -8,9 +8,13 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "NewRelicPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const NewRelicPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "newRelicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const newRelicPlugin: BackstagePlugin< { diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index dc5dbecf19..e927cf6eef 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index f6042fc618..ecf570b6b8 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-org +## 0.3.17 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.3.16 ### Patch Changes diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md index d541505265..78a2f82561 100644 --- a/plugins/org/api-report.md +++ b/plugins/org/api-report.md @@ -11,6 +11,8 @@ import { GroupEntity } from '@backstage/catalog-model'; import { InfoCardVariants } from '@backstage/core-components'; import { UserEntity } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "EntityGroupProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityGroupProfileCard: ({ variant, @@ -19,11 +21,15 @@ export const EntityGroupProfileCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityMembersListCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityMembersListCard: (_props: { entity?: GroupEntity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityOwnershipCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityOwnershipCard: ({ variant, @@ -32,6 +38,8 @@ export const EntityOwnershipCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityUserProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityUserProfileCard: ({ variant, @@ -40,6 +48,8 @@ export const EntityUserProfileCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "GroupProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const GroupProfileCard: ({ variant, @@ -48,14 +58,20 @@ export const GroupProfileCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "MembersListCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const MembersListCard: (_props: { entity?: GroupEntity }) => JSX.Element; +// Warning: (ae-missing-release-tag) "orgPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const orgPlugin: BackstagePlugin<{}, {}>; export { orgPlugin }; export { orgPlugin as plugin }; +// Warning: (ae-missing-release-tag) "OwnershipCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const OwnershipCard: ({ variant, @@ -64,6 +80,8 @@ export const OwnershipCard: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "UserProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserProfileCard: ({ variant, diff --git a/plugins/org/package.json b/plugins/org/package.json index 3962121289..f721675584 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.3.16", + "version": "0.3.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -35,10 +35,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 9cb7fe11b4..a83afa09f3 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-pagerduty +## 0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index e0278c5d27..0740b440ee 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -12,34 +12,54 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { PropsWithChildren } from 'react'; +// Warning: (ae-missing-release-tag) "EntityPagerDutyCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityPagerDutyCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "isPluginApplicableToEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isPluginApplicableToEntity: (entity: Entity) => boolean; export { isPluginApplicableToEntity as isPagerDutyAvailable }; export { isPluginApplicableToEntity }; +// Warning: (ae-forgotten-export) The symbol "PagerDutyApi" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "pagerDutyApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const pagerDutyApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "PagerDutyCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const PagerDutyCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "PagerDutyClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class PagerDutyClient implements PagerDutyApi { + // Warning: (ae-forgotten-export) The symbol "ClientApiConfig" needs to be exported by the entry point index.d.ts constructor(config: ClientApiConfig); // (undocumented) static fromConfig( configApi: ConfigApi, discoveryApi: DiscoveryApi, ): PagerDutyClient; + // Warning: (ae-forgotten-export) The symbol "Incident" needs to be exported by the entry point index.d.ts + // // (undocumented) getIncidentsByServiceId(serviceId: string): Promise; + // Warning: (ae-forgotten-export) The symbol "OnCall" needs to be exported by the entry point index.d.ts + // // (undocumented) getOnCallByPolicyId(policyId: string): Promise; + // Warning: (ae-forgotten-export) The symbol "Service" needs to be exported by the entry point index.d.ts + // // (undocumented) getServiceByIntegrationKey(integrationKey: string): Promise; + // Warning: (ae-forgotten-export) The symbol "TriggerAlarmRequest" needs to be exported by the entry point index.d.ts + // // (undocumented) triggerAlarm({ integrationKey, @@ -49,16 +69,23 @@ export class PagerDutyClient implements PagerDutyApi { }: TriggerAlarmRequest): Promise; } +// Warning: (ae-missing-release-tag) "pagerDutyPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const pagerDutyPlugin: BackstagePlugin<{}, {}>; export { pagerDutyPlugin }; export { pagerDutyPlugin as plugin }; +// Warning: (ae-forgotten-export) The symbol "TriggerButtonProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "TriggerButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function TriggerButton({ children, }: PropsWithChildren): JSX.Element; +// Warning: (ae-missing-release-tag) "UnauthorizedError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class UnauthorizedError extends Error {} diff --git a/plugins/pagerduty/doc/pd1.png b/plugins/pagerduty/doc/pd1.png index 8242792b5a..dc5a1a94a2 100644 Binary files a/plugins/pagerduty/doc/pd1.png and b/plugins/pagerduty/doc/pd1.png differ diff --git a/plugins/pagerduty/doc/pd3.png b/plugins/pagerduty/doc/pd3.png index 89d7f1820d..0546b4e7b5 100644 Binary files a/plugins/pagerduty/doc/pd3.png and b/plugins/pagerduty/doc/pd3.png differ diff --git a/plugins/pagerduty/doc/pd4.png b/plugins/pagerduty/doc/pd4.png index 70584bc59e..a9aa93c2ba 100644 Binary files a/plugins/pagerduty/doc/pd4.png and b/plugins/pagerduty/doc/pd4.png differ diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 9cb95c25d7..2a5c086e50 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-pagerduty", - "version": "0.3.7", + "version": "0.3.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/proxy-backend/api-report.md b/plugins/proxy-backend/api-report.md index 812c3f7221..eb7fbec12e 100644 --- a/plugins/proxy-backend/api-report.md +++ b/plugins/proxy-backend/api-report.md @@ -8,6 +8,9 @@ import express from 'express'; import { Logger as Logger_2 } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; +// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; diff --git a/plugins/register-component/CHANGELOG.md b/plugins/register-component/CHANGELOG.md index 642f465f54..083f45c321 100644 --- a/plugins/register-component/CHANGELOG.md +++ b/plugins/register-component/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-register-component +## 0.2.20 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.2.19 ### Patch Changes diff --git a/plugins/register-component/api-report.md b/plugins/register-component/api-report.md index a150f4be2c..39aeb39505 100644 --- a/plugins/register-component/api-report.md +++ b/plugins/register-component/api-report.md @@ -8,6 +8,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "RegisterComponentPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RegisterComponentPage: ({ catalogRouteRef, @@ -15,6 +17,8 @@ export const RegisterComponentPage: ({ catalogRouteRef: RouteRef; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "registerComponentPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const registerComponentPlugin: BackstagePlugin< { @@ -25,6 +29,8 @@ const registerComponentPlugin: BackstagePlugin< export { registerComponentPlugin as plugin }; export { registerComponentPlugin }; +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated export const Router: ({ catalogRouteRef, diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 5996b865fd..c9a4ca161b 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-register-component", - "version": "0.2.19", + "version": "0.2.20", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,10 +46,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index 7f4a7e1b8e..f7172844ae 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-rollbar-backend +## 0.1.13 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + ## 0.1.12 ### Patch Changes diff --git a/plugins/rollbar-backend/api-report.md b/plugins/rollbar-backend/api-report.md index 02a79fa267..ecac80237f 100644 --- a/plugins/rollbar-backend/api-report.md +++ b/plugins/rollbar-backend/api-report.md @@ -7,9 +7,13 @@ import { Config } from '@backstage/config'; import express from 'express'; import { Logger as Logger_2 } from 'winston'; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "getRequestHeaders" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function getRequestHeaders( token: string, @@ -19,6 +23,8 @@ export function getRequestHeaders( }; }; +// Warning: (ae-missing-release-tag) "RollbarApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class RollbarApi { constructor(accessToken: string, logger: Logger_2); @@ -30,8 +36,12 @@ export class RollbarApi { item_id?: number; }, ): Promise; + // Warning: (ae-forgotten-export) The symbol "RollbarProject" needs to be exported by the entry point index.d.ts + // // (undocumented) getAllProjects(): Promise; + // Warning: (ae-forgotten-export) The symbol "RollbarItemCount" needs to be exported by the entry point index.d.ts + // // (undocumented) getOccuranceCounts( projectName: string, @@ -42,8 +52,12 @@ export class RollbarApi { ): Promise; // (undocumented) getProject(projectName: string): Promise; + // Warning: (ae-forgotten-export) The symbol "RollbarItemsResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) getProjectItems(projectName: string): Promise; + // Warning: (ae-forgotten-export) The symbol "RollbarTopActiveItem" needs to be exported by the entry point index.d.ts + // // (undocumented) getTopActiveItems( projectName: string, @@ -54,6 +68,8 @@ export class RollbarApi { ): Promise; } +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 6f6d87e30e..5ab7b1cf4a 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.12", + "version": "0.1.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.3", + "@backstage/backend-common": "^0.8.6", "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "axios": "^0.21.1", @@ -47,7 +47,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.1", + "@backstage/cli": "^0.7.4", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 5ba8a5fee2..5d04f96d6e 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-rollbar +## 0.3.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/rollbar/api-report.md b/plugins/rollbar/api-report.md index c2ea5bb364..4009702da4 100644 --- a/plugins/rollbar/api-report.md +++ b/plugins/rollbar/api-report.md @@ -12,30 +12,47 @@ import { Entity } from '@backstage/catalog-model'; import { IdentityApi } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EntityPageRollbar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityPageRollbar: (_props: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityRollbarContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityRollbarContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "isPluginApplicableToEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const isPluginApplicableToEntity: (entity: Entity) => boolean; export { isPluginApplicableToEntity }; export { isPluginApplicableToEntity as isRollbarAvailable }; +// Warning: (ae-missing-release-tag) "ROLLBAR_ANNOTATION" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ROLLBAR_ANNOTATION = 'rollbar.com/project-slug'; +// Warning: (ae-missing-release-tag) "RollbarApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RollbarApi { + // Warning: (ae-forgotten-export) The symbol "RollbarProject" needs to be exported by the entry point index.d.ts + // // (undocumented) getAllProjects(): Promise; // (undocumented) getProject(projectName: string): Promise; + // Warning: (ae-forgotten-export) The symbol "RollbarItemsResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) getProjectItems(project: string): Promise; + // Warning: (ae-forgotten-export) The symbol "RollbarTopActiveItem" needs to be exported by the entry point index.d.ts + // // (undocumented) getTopActiveItems( project: string, @@ -43,9 +60,13 @@ export interface RollbarApi { ): Promise; } +// Warning: (ae-missing-release-tag) "rollbarApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const rollbarApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "RollbarClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class RollbarClient implements RollbarApi { constructor(options: { @@ -66,6 +87,8 @@ export class RollbarClient implements RollbarApi { ): Promise; } +// Warning: (ae-missing-release-tag) "rollbarPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const rollbarPlugin: BackstagePlugin< { @@ -76,6 +99,9 @@ const rollbarPlugin: BackstagePlugin< export { rollbarPlugin as plugin }; export { rollbarPlugin }; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: (_props: Props_2) => JSX.Element; diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 4cbffe3fff..f95bba8076 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar", - "version": "0.3.8", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend-module-cookiecutter/.eslintrc.js b/plugins/scaffolder-backend-module-cookiecutter/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/scaffolder-backend-module-cookiecutter/README.md b/plugins/scaffolder-backend-module-cookiecutter/README.md new file mode 100644 index 0000000000..77ae93c5f3 --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/README.md @@ -0,0 +1,162 @@ +# scaffolder-backend-module-cookiecutter + +Welcome to the `fetch:cookiecutter` action for the `scaffolder-backend`. + +## Getting started + +You need to configure the action in your backend: + +## From your Backstage root directory + +``` +cd packages/backend +yarn add @backstage/plugin-scaffolder-backend-module-cookiecutter +``` + +Configure the action: +(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): + +```typescript +// packages/backend/src/plugins/scaffolder.ts + +const actions = [ + createFetchCookiecutterAction({ + integrations, + reader, + containerRunner, + }), + ...createBuiltInActions({ + ... + }) +]; + +return await createRouter({ + containerRunner, + logger, + config, + database, + catalogClient, + reader, + actions, +}); +``` + +After that you can use the action in your template: + +```yaml +apiVersion: backstage.io/v1beta2 +kind: Template +metadata: + name: cookiecutter-demo + title: Cookiecutter Test + description: Cookiecutter example +spec: + owner: backstage/techdocs-core + type: service + + parameters: + - title: Fill in some steps + required: + - name + - owner + properties: + name: + title: Name + type: string + description: Unique name of the component + ui:autofocus: true + ui:options: + rows: 5 + owner: + title: Owner + type: string + description: Owner of the component + ui:field: OwnerPicker + ui:options: + allowedKinds: + - Group + system: + title: System + type: string + description: System of the component + ui:field: EntityPicker + ui:options: + allowedKinds: + - System + defaultKind: System + + - title: Choose a location + required: + - repoUrl + - dryRun + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com + dryRun: + title: Only perform a dry run, don't publish anything + type: boolean + default: false + + steps: + - id: fetch-base + name: Fetch Base + action: fetch:cookiecutter + input: + url: ./template + values: + name: '{{ parameters.name }}' + owner: '{{ parameters.owner }}' + system: '{{ parameters.system }}' + destination: '{{ parseRepoUrl parameters.repoUrl }}' + + - id: publish + if: '{{ not parameters.dryRun }}' + name: Publish + action: publish:github + input: + allowedHosts: ['github.com'] + description: 'This is {{ parameters.name }}' + repoUrl: '{{ parameters.repoUrl }}' + + - id: register + if: '{{ not parameters.dryRun }}' + name: Register + action: catalog:register + input: + repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}' + catalogInfoPath: '/catalog-info.yaml' + + - name: Results + if: '{{ parameters.dryRun }}' + action: debug:log + input: + listWorkspace: true + + output: + links: + - title: Repository + url: '{{ steps.publish.output.remoteUrl }}' + - title: Open in catalog + icon: 'catalog' + entityRef: '{{ steps.register.output.entityRef }}' +``` + +You can also visit the `/create/actions` route in your Backstage application to find out more about the parameters this action accepts when it's installed to configure how you like. + +### Environment setup + +The environment needs to have either `cookiecutter` installed and be available in the `PATH` or access to a `docker` daemon so it can spin up a docker container with `cookiecutter` available. + +If you are running Backstage from a Docker container and you want to avoid calling a container inside a container, you can set up `cookiecutter` in your own image, this will use the local installation instead. + +You can do so by including the following lines in the last step of your Dockerfile: + +```dockerfile +RUN apt-get update && apt-get install -y python3 python3-pip +RUN pip3 install cookiecutter +``` diff --git a/plugins/scaffolder-backend-module-cookiecutter/api-report.md b/plugins/scaffolder-backend-module-cookiecutter/api-report.md new file mode 100644 index 0000000000..8e46667fce --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/api-report.md @@ -0,0 +1,23 @@ +## API Report File for "@backstage/plugin-scaffolder-backend-module-cookiecutter" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ContainerRunner } from '@backstage/backend-common'; +import { ScmIntegrations } from '@backstage/integration'; +import { TemplateAction } from '@backstage/plugin-scaffolder-backend'; +import { UrlReader } from '@backstage/backend-common'; + +// Warning: (ae-missing-release-tag) "createFetchCookiecutterAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function createFetchCookiecutterAction(options: { + reader: UrlReader; + integrations: ScmIntegrations; + containerRunner: ContainerRunner; +}): TemplateAction; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json new file mode 100644 index 0000000000..f321d6b45c --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -0,0 +1,45 @@ +{ + "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.8.6", + "@backstage/errors": "^0.1.1", + "@backstage/integration": "^0.5.7", + "@backstage/plugin-scaffolder-backend": "^0.14.0", + "@backstage/config": "^0.1.5", + "command-exists": "^1.2.9", + "fs-extra": "10.0.0", + "winston": "^3.2.1", + "cross-fetch": "^3.0.6", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.7.3", + "@types/fs-extra": "^9.0.1", + "@types/mock-fs": "^4.13.0", + "@types/jest": "^26.0.7", + "@types/command-exists": "^1.2.0", + "mock-fs": "^4.13.0", + "msw": "^0.29.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts similarity index 96% rename from plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts rename to plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts index 86f953a6a5..bc3fe3eca2 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts @@ -17,9 +17,12 @@ const runCommand = jest.fn(); const commandExists = jest.fn(); const fetchContents = jest.fn(); -jest.mock('./helpers', () => ({ fetchContents })); +jest.mock('@backstage/plugin-scaffolder-backend', () => ({ + ...jest.requireActual('@backstage/plugin-scaffolder-backend'), + fetchContents, + runCommand, +})); jest.mock('command-exists', () => commandExists); -jest.mock('../helpers', () => ({ runCommand })); import { getVoidLogger, @@ -33,7 +36,7 @@ import os from 'os'; import { PassThrough } from 'stream'; import { createFetchCookiecutterAction } from './cookiecutter'; import { join } from 'path'; -import { ActionContext } from '../../types'; +import type { ActionContext } from '@backstage/plugin-scaffolder-backend'; describe('fetch:cookiecutter', () => { const integrations = ScmIntegrations.fromConfig( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts similarity index 94% rename from plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts rename to plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts index 048b2bba64..1f6fbdb7d8 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts @@ -26,9 +26,11 @@ import commandExists from 'command-exists'; import fs from 'fs-extra'; import path, { resolve as resolvePath } from 'path'; import { Writable } from 'stream'; -import { runCommand } from '../helpers'; -import { createTemplateAction } from '../../createTemplateAction'; -import { fetchContents } from './helpers'; +import { + runCommand, + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-backend'; export class CookiecutterRunner { private readonly containerRunner: ContainerRunner; @@ -136,7 +138,7 @@ export function createFetchCookiecutterAction(options: { }>({ id: 'fetch:cookiecutter', description: - "Downloads a template from the given URL into the workspace, and runs cookiecutter on it. This action is deprecated in favor of 'fetch:template'. See https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetch-cookiecutter-to-fetch-template for more details.", + 'Downloads a template from the given URL into the workspace, and runs cookiecutter on it.', schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/index.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/index.ts new file mode 100644 index 0000000000..bbe5a16a04 --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export { createFetchCookiecutterAction } from './cookiecutter'; diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/index.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/index.ts new file mode 100644 index 0000000000..1b47db9e03 --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export * from './fetch'; diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/index.ts b/plugins/scaffolder-backend-module-cookiecutter/src/index.ts new file mode 100644 index 0000000000..4f06b14a86 --- /dev/null +++ b/plugins/scaffolder-backend-module-cookiecutter/src/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export * from './actions'; diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 45ba753e34..857667bc6d 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.1.3 + +### Patch Changes + +- e114cc7e0: updated paths to consider differences between platform (windows corrected) +- Updated dependencies + - @backstage/backend-common@0.8.6 + - @backstage/plugin-scaffolder-backend@0.14.0 + ## 0.1.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/api-report.md b/plugins/scaffolder-backend-module-rails/api-report.md index dc26d22fe4..7d1576c7f1 100644 --- a/plugins/scaffolder-backend-module-rails/api-report.md +++ b/plugins/scaffolder-backend-module-rails/api-report.md @@ -8,6 +8,8 @@ import { ScmIntegrations } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-backend'; import { UrlReader } from '@backstage/backend-common'; +// Warning: (ae-missing-release-tag) "createFetchRailsAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createFetchRailsAction(options: { reader: UrlReader; diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 4d734f8440..3062136a76 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", - "@backstage/plugin-scaffolder-backend": "^0.13.0", + "@backstage/backend-common": "^0.8.6", + "@backstage/plugin-scaffolder-backend": "^0.14.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", @@ -29,7 +29,7 @@ "fs-extra": "^9.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.1", + "@backstage/cli": "^0.7.4", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "@types/command-exists": "^1.2.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 5563bb0570..8e55ca4b59 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-scaffolder-backend +## 0.14.1 + +### Patch Changes + +- c73f53bc2: Add new built-in action ci:github-actions-dispatch +- 7cea90592: - Move out the `cookiecutter` templating to its own module that is depended on by the `scaffolder-backend` plugin. No breaking change yet, but we will drop first class support for `cookiecutter` in the future and it will become an opt-in feature. +- eb740ee24: Moved sample software templates to the [backstage/software-templates](https://github.com/backstage/software-templates) repository. If you previously referenced the sample templates straight from `scaffolder-backend` plugin in the main [backstage/backstage](https://github.com/backstage/backstage) repository in your `app-config.yaml`, these references will need to be updated. + + See https://github.com/backstage/software-templates + +- Updated dependencies + - @backstage/catalog-client@0.3.17 + - @backstage/backend-common@0.8.7 + +## 0.14.0 + +### Minor Changes + +- 96fc27698: Updated inputs for the `publish:github:pull-request` action. + + Now requires a `repoUrl` instead of separate `owner` and `repo` inputs. This aligns with the output of the `RepoUrlPicker` ui field used by the pull-request sample template. + +### Patch Changes + +- e75506fe7: Unsubscribe from broker after response is flushed +- ea1d956ef: Updating fs-extra to 10.0.0 to handle broken symbolic links correctly +- 31de5f27f: Add new `fetch:template` action which handles the same responsibilities as `fetch:cookiecutter` without the external dependency on `cookiecutter`. For information on migrating from `fetch:cookiecutter` to `fetch:template`, see the [migration guide](https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetch-cookiecutter-to-fetch-template) in the docs. +- 84d329e2a: Scaffolder: Added an 'eq' handlebars helper for use in software template YAML files. This can be used to execute a step depending on the value of an input, e.g.: + + ```yaml + steps: + id: 'conditional-step' + action: 'custom-action' + if: '{{ eq parameters.myvalue "custom" }}', + ``` + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + ## 0.13.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index d6aa1e4e75..baf85bce63 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -8,6 +8,7 @@ import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { ContainerRunner } from '@backstage/backend-common'; +import { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter'; import { createPullRequest } from 'octokit-plugin-create-pull-request'; import express from 'express'; import { JsonObject } from '@backstage/config'; @@ -21,6 +22,9 @@ import { TemplateEntityV1beta2 } from '@backstage/catalog-model'; import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; +// Warning: (ae-forgotten-export) The symbol "InputBase" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ActionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ActionContext = { baseUrl?: string; @@ -33,6 +37,8 @@ export type ActionContext = { createTemporaryDirectory(): Promise; }; +// Warning: (ae-missing-release-tag) "CatalogEntityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class CatalogEntityClient { constructor(catalogClient: CatalogApi); @@ -44,6 +50,8 @@ export class CatalogEntityClient { ): Promise; } +// Warning: (ae-missing-release-tag) "createBuiltinActions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createBuiltinActions: (options: { reader: UrlReader; @@ -53,79 +61,112 @@ export const createBuiltinActions: (options: { config: Config; }) => TemplateAction[]; +// Warning: (ae-missing-release-tag) "createCatalogRegisterAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createCatalogRegisterAction(options: { catalogClient: CatalogApi; integrations: ScmIntegrations; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createCatalogWriteAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createCatalogWriteAction(): TemplateAction; +// Warning: (ae-missing-release-tag) "createDebugLogAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createDebugLogAction(): TemplateAction; -// @public (undocumented) -export function createFetchCookiecutterAction(options: { - reader: UrlReader; - integrations: ScmIntegrations; - containerRunner: ContainerRunner; -}): TemplateAction; +export { createFetchCookiecutterAction }; +// Warning: (ae-missing-release-tag) "createFetchPlainAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createFetchPlainAction(options: { reader: UrlReader; integrations: ScmIntegrations; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createFetchTemplateAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createFetchTemplateAction(options: { reader: UrlReader; integrations: ScmIntegrations; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createFilesystemDeleteAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createFilesystemDeleteAction: () => TemplateAction; +// Warning: (ae-missing-release-tag) "createFilesystemRenameAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createFilesystemRenameAction: () => TemplateAction; +// Warning: (ae-missing-release-tag) "createGithubActionsDispatchAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function createGithubActionsDispatchAction(options: { + integrations: ScmIntegrationRegistry; +}): TemplateAction; + +// Warning: (ae-missing-release-tag) "createPublishAzureAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createPublishAzureAction(options: { integrations: ScmIntegrationRegistry; config: Config; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createPublishBitbucketAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createPublishBitbucketAction(options: { integrations: ScmIntegrationRegistry; config: Config; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createPublishFileAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export function createPublishFileAction(): TemplateAction; +// Warning: (ae-missing-release-tag) "createPublishGithubAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createPublishGithubAction(options: { integrations: ScmIntegrationRegistry; config: Config; }): TemplateAction; +// Warning: (ae-forgotten-export) The symbol "CreateGithubPullRequestActionOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createPublishGithubPullRequestAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createPublishGithubPullRequestAction: ({ integrations, clientFactory, }: CreateGithubPullRequestActionOptions) => TemplateAction; +// Warning: (ae-missing-release-tag) "createPublishGitlabAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createPublishGitlabAction(options: { integrations: ScmIntegrationRegistry; config: Config; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "createTemplateAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const createTemplateAction: < Input extends Partial<{ @@ -135,6 +176,8 @@ export const createTemplateAction: < templateAction: TemplateAction, ) => TemplateAction; +// Warning: (ae-missing-release-tag) "fetchContents" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function fetchContents({ reader, @@ -150,6 +193,8 @@ export function fetchContents({ outputPath: string; }): Promise; +// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RouterOptions { // (undocumented) @@ -170,6 +215,9 @@ export interface RouterOptions { taskWorkers?: number; } +// Warning: (ae-forgotten-export) The symbol "RunCommandOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "runCommand" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const runCommand: ({ command, @@ -177,6 +225,8 @@ export const runCommand: ({ logStream, }: RunCommandOptions) => Promise; +// Warning: (ae-missing-release-tag) "TemplateAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TemplateAction = { id: string; @@ -188,6 +238,8 @@ export type TemplateAction = { handler: (ctx: ActionContext) => Promise; }; +// Warning: (ae-missing-release-tag) "TemplateActionRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class TemplateActionRegistry { // (undocumented) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index d31e6c0d5f..490c7cc0d2 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.13.0", + "version": "0.14.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", - "@backstage/catalog-client": "^0.3.16", + "@backstage/backend-common": "^0.8.7", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.0", "@gitbeaker/core": "^30.2.0", "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", @@ -65,8 +66,8 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/test-utils": "^0.1.15", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder-backend/sample-templates/all-templates.yaml b/plugins/scaffolder-backend/sample-templates/all-templates.yaml index a5bcd864b1..2d5adb8e3b 100644 --- a/plugins/scaffolder-backend/sample-templates/all-templates.yaml +++ b/plugins/scaffolder-backend/sample-templates/all-templates.yaml @@ -5,5 +5,10 @@ metadata: description: A collection of all Backstage example templates spec: targets: - - ./local-templates.yaml - ./remote-templates.yaml + +# For local development of a template, you can reference your local templates here. +# Examples: +# +# - ./local-template/template.yaml +# - ../all-templates/local/template.yaml diff --git a/plugins/scaffolder-backend/sample-templates/local-templates.yaml b/plugins/scaffolder-backend/sample-templates/local-templates.yaml deleted file mode 100644 index 3b3acbae7e..0000000000 --- a/plugins/scaffolder-backend/sample-templates/local-templates.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Location -metadata: - name: example-templates-local - description: A collection of locally available Backstage example templates -spec: - targets: - - ./docs-template/template.yaml - - ./react-ssr-template/template.yaml - - ./create-react-app/template.yaml - - ./springboot-grpc-template/template.yaml - - ./v1beta2-demo/template.yaml - - ./pull-request/template.yaml diff --git a/plugins/scaffolder-backend/sample-templates/remote-templates.yaml b/plugins/scaffolder-backend/sample-templates/remote-templates.yaml index 7846a0994e..81787b4297 100644 --- a/plugins/scaffolder-backend/sample-templates/remote-templates.yaml +++ b/plugins/scaffolder-backend/sample-templates/remote-templates.yaml @@ -7,3 +7,9 @@ spec: type: url targets: - https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml + - https://github.com/backstage/software-templates/blob/main/scaffolder-templates/create-react-app/template.yaml + - https://github.com/backstage/software-templates/blob/main/scaffolder-templates/docs-template/template.yaml + - https://github.com/backstage/software-templates/blob/main/scaffolder-templates/pull-request/template.yaml + - https://github.com/backstage/software-templates/blob/main/scaffolder-templates/react-ssr-template/template.yaml + - https://github.com/backstage/software-templates/blob/main/scaffolder-templates/springboot-grpc-template/template.yaml + - https://github.com/backstage/software-templates/blob/main/scaffolder-templates/v1beta2-demo/template.yaml diff --git a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts index 75cdfc208f..7505c3a30e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts @@ -15,6 +15,11 @@ */ export const mockGithubClient = { + rest: { + actions: { + createWorkflowDispatch: jest.fn(), + }, + }, repos: { createInOrg: jest.fn(), createForAuthenticatedUser: jest.fn(), diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 6541625035..b3d1460270 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -24,11 +24,8 @@ import { } from './catalog'; import { createDebugLogAction } from './debug'; -import { - createFetchCookiecutterAction, - createFetchPlainAction, - createFetchTemplateAction, -} from './fetch'; +import { createFetchPlainAction, createFetchTemplateAction } from './fetch'; +import { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter'; import { createFilesystemDeleteAction, createFilesystemRenameAction, @@ -40,6 +37,7 @@ import { createPublishGithubPullRequestAction, createPublishGitlabAction, } from './publish'; +import { createGithubActionsDispatchAction } from './github'; export const createBuiltinActions = (options: { reader: UrlReader; @@ -94,5 +92,8 @@ export const createBuiltinActions = (options: { createCatalogWriteAction(), createFilesystemDeleteAction(), createFilesystemRenameAction(), + createGithubActionsDispatchAction({ + integrations, + }), ]; }; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts index fea32df39c..c9a16c274e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts @@ -15,6 +15,5 @@ */ export { createFetchPlainAction } from './plain'; -export { createFetchCookiecutterAction } from './cookiecutter'; export { createFetchTemplateAction } from './template'; export { fetchContents } from './helpers'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts new file mode 100644 index 0000000000..29e4338a39 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.test.ts @@ -0,0 +1,117 @@ +/* + * 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. + */ + +jest.mock('@octokit/rest'); + +import { createGithubActionsDispatchAction } from './githubActionsDispatch'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; + +describe('github:actions:dispatch', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createGithubActionsDispatchAction({ integrations }); + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + workflowId: 'a-workflow-id', + branchOrTagName: 'main', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + const { mockGithubClient } = require('@octokit/rest'); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should throw an error when the repoUrl is not well formed', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'github.com?repo=bob' }, + }), + ).rejects.toThrow(/missing owner/); + + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'github.com?owner=owner' }, + }), + ).rejects.toThrow(/missing repo/); + }); + + it('should throw if there is no integration config provided', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'missing.com?repo=bob&owner=owner' }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); + + it('should throw if there is no token in the integration config that is returned', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + repoUrl: 'ghe.github.com?repo=bob&owner=owner', + }, + }), + ).rejects.toThrow(/No token available for host/); + }); + + it('should call the githubApis for creating WorkflowDispatch', async () => { + mockGithubClient.rest.actions.createWorkflowDispatch.mockResolvedValue({ + data: { + foo: 'bar', + }, + }); + + const repoUrl = 'github.com?repo=repo&owner=owner'; + const workflowId = 'dispatch_workflow'; + const branchOrTagName = 'main'; + const ctx = Object.assign({}, mockContext, { + input: { repoUrl, workflowId, branchOrTagName }, + }); + await action.handler(ctx); + + expect( + mockGithubClient.rest.actions.createWorkflowDispatch, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + workflow_id: workflowId, + ref: branchOrTagName, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts new file mode 100644 index 0000000000..7f48a155ed --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts @@ -0,0 +1,115 @@ +/* + * 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. + */ +import { InputError } from '@backstage/errors'; +import { + GithubCredentialsProvider, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { Octokit } from '@octokit/rest'; +import { parseRepoUrl } from '../publish/util'; +import { createTemplateAction } from '../../createTemplateAction'; + +export function createGithubActionsDispatchAction(options: { + integrations: ScmIntegrationRegistry; +}) { + const { integrations } = options; + + const credentialsProviders = new Map( + integrations.github.list().map(integration => { + const provider = GithubCredentialsProvider.create(integration.config); + return [integration.config.host, provider]; + }), + ); + + return createTemplateAction<{ + repoUrl: string; + workflowId: string; + branchOrTagName: string; + }>({ + id: 'github:actions:dispatch', + description: + 'Dispatches a GitHub Action workflow for a given branch or tag', + schema: { + input: { + type: 'object', + required: ['repoUrl', 'workflowId', 'branchOrTagName'], + properties: { + repoUrl: { + title: 'Repository Location', + description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, + type: 'string', + }, + workflowId: { + title: 'Workflow ID', + description: 'The GitHub Action Workflow filename', + type: 'string', + }, + branchOrTagName: { + title: 'Branch or Tag name', + description: + 'The git branch or tag name used to dispatch the workflow', + type: 'string', + }, + }, + }, + }, + async handler(ctx) { + const { repoUrl, workflowId, branchOrTagName } = ctx.input; + + const { owner, repo, host } = parseRepoUrl(repoUrl); + + ctx.logger.info( + `Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`, + ); + + const credentialsProvider = credentialsProviders.get(host); + const integrationConfig = integrations.github.byHost(host); + + if (!credentialsProvider || !integrationConfig) { + throw new InputError( + `No matching integration configuration for host ${host}, please check your integrations config`, + ); + } + + const { token } = await credentialsProvider.getCredentials({ + url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent( + repo, + )}`, + }); + + if (!token) { + throw new InputError( + `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`, + ); + } + + const client = new Octokit({ + auth: token, + baseUrl: integrationConfig.config.apiBaseUrl, + previews: ['nebula-preview'], + }); + + await client.rest.actions.createWorkflowDispatch({ + owner, + repo, + workflow_id: workflowId, + ref: branchOrTagName, + }); + + ctx.logger.info(`Workflow ${workflowId} dispatched successfully`); + }, + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts new file mode 100644 index 0000000000..50abd2c3dc --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { createGithubActionsDispatchAction } from './githubActionsDispatch'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts index 849b056ac9..2265f9b4f7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts @@ -20,4 +20,7 @@ export * from './debug'; export * from './fetch'; export * from './filesystem'; export * from './publish'; +export * from './github'; + +export { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter'; export { runCommand } from './helpers'; diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 53a2043456..3e80dc993e 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder +## 0.10.1 + +### Patch Changes + +- 73951fc44: Add options to mask or hide values on review state +- 976b61080: Updated the software templates list page (`ScaffolderPage`) to use the `useEntityListProvider` hook from #5643. This reduces the code footprint, making it easier to customize the display of this page, and consolidates duplicate approaches to querying the catalog with filters. + + - The `useEntityTypeFilter` hook has been updated along with the underlying `EntityTypeFilter` to work with multiple values, to allow more flexibility for different user interfaces. It's unlikely that this change affects you; however, if you're using either of these directly, you'll need to update your usage. + - `SearchToolbar` was renamed to `EntitySearchBar` and moved to `catalog-react` to be usable by other entity list pages + - `UserListPicker` now has an `availableTypes` prop to restrict which user-related options to present + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.10.0 ### Minor Changes diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 2f9d10b7ac..1be7e8fec8 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -23,11 +23,15 @@ import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { ScmIntegrationRegistry } from '@backstage/integration'; +// Warning: (ae-missing-release-tag) "createScaffolderFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createScaffolderFieldExtension( options: FieldExtensionOptions, ): Extension<() => null>; +// Warning: (ae-missing-release-tag) "CustomFieldValidator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type CustomFieldValidator = | ((data: T, field: FieldValidation) => void) @@ -39,9 +43,13 @@ export type CustomFieldValidator = }, ) => void); +// Warning: (ae-missing-release-tag) "EntityPickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityPickerFieldExtension: () => null; +// Warning: (ae-missing-release-tag) "FieldExtensionOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type FieldExtensionOptions = { name: string; @@ -49,12 +57,18 @@ export type FieldExtensionOptions = { validation?: CustomFieldValidator; }; +// Warning: (ae-missing-release-tag) "OwnerPickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const OwnerPickerFieldExtension: () => null; +// Warning: (ae-missing-release-tag) "RepoUrlPickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const RepoUrlPickerFieldExtension: () => null; +// Warning: (ae-missing-release-tag) "ScaffolderApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ScaffolderApi { // (undocumented) @@ -67,15 +81,25 @@ export interface ScaffolderApi { host: string; }[] >; + // Warning: (ae-forgotten-export) The symbol "ScaffolderTask" needs to be exported by the entry point index.d.ts + // // (undocumented) getTask(taskId: string): Promise; + // Warning: (ae-forgotten-export) The symbol "TemplateParameterSchema" needs to be exported by the entry point index.d.ts + // // (undocumented) getTemplateParameterSchema( templateName: EntityName, ): Promise; + // Warning: (ae-forgotten-export) The symbol "ListActionsResponse" needs to be exported by the entry point index.d.ts + // // (undocumented) listActions(): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen scaffold(templateName: string, values: Record): Promise; + // Warning: (ae-forgotten-export) The symbol "LogEvent" needs to be exported by the entry point index.d.ts + // // (undocumented) streamLogs({ taskId, @@ -86,9 +110,13 @@ export interface ScaffolderApi { }): Observable; } +// Warning: (ae-missing-release-tag) "scaffolderApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const scaffolderApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "ScaffolderClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ScaffolderClient implements ScaffolderApi { constructor(options: { @@ -114,6 +142,8 @@ export class ScaffolderClient implements ScaffolderApi { ): Promise; // (undocumented) listActions(): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen scaffold(templateName: string, values: Record): Promise; // (undocumented) streamLogs({ @@ -125,12 +155,18 @@ export class ScaffolderClient implements ScaffolderApi { }): Observable; } +// Warning: (ae-missing-release-tag) "ScaffolderFieldExtensions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ScaffolderFieldExtensions: React_2.ComponentType; +// Warning: (ae-missing-release-tag) "ScaffolderPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ScaffolderPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "scaffolderPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const scaffolderPlugin: BackstagePlugin< { diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 24b48e7fd1..edb2f44d94 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.10.0", + "version": "0.10.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,12 +33,12 @@ "@backstage/catalog-client": "^0.3.16", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -63,10 +63,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx new file mode 100644 index 0000000000..5f7091832f --- /dev/null +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx @@ -0,0 +1,88 @@ +/* + * 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. + */ + +import { getReviewData } from './MultistepJsonForm'; + +describe('MultistepJsonForm', () => { + const formDataMock = { + password: 'password', + masked: 'Some info to mask', + open: 'Some open info', + hidden: 'Some info to hide', + 'other-open': 'Other open info', + }; + + const stepsMock = [ + { + title: 'The test template', + schema: { + title: 'The test template', + properties: { + password: { + title: 'Password', + type: 'string', + 'ui:widget': 'password', + }, + masked: { + title: 'Masked', + type: 'string', + 'ui:backstage': { + review: { + show: true, + mask: '******', + }, + }, + }, + open: { + title: 'Open info', + type: 'string', + }, + }, + }, + }, + { + title: 'Other fields', + schema: { + title: 'Other fields', + properties: { + hidden: { + title: 'Hidden', + type: 'string', + 'ui:backstage': { + review: { + show: false, + }, + }, + }, + 'other-open': { + title: 'Other Open Info', + type: 'string', + }, + }, + }, + }, + ]; + + test('Fields are defined to be hidden or masked', () => { + const reviewData = getReviewData(formDataMock, stepsMock); + + expect(reviewData.password).toBe('******'); + expect(reviewData.masked).toBe('******'); + expect(reviewData.open).toBe('Some open info'); + expect(reviewData.hidden).toBeUndefined(); + expect(reviewData['other-open']).toBe('Other open info'); + }); +}); diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index 0fc998f82e..cdcc22da29 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -24,7 +24,7 @@ import { Stepper, Typography, } from '@material-ui/core'; -import { FormProps, IChangeEvent, withTheme } from '@rjsf/core'; +import { FormProps, IChangeEvent, UiSchema, withTheme } from '@rjsf/core'; import { Theme as MuiTheme } from '@rjsf/material-ui'; import React, { useState } from 'react'; import { transformSchemaToProps } from './schema'; @@ -49,6 +49,59 @@ type Props = { fields?: FormProps['fields']; }; +export function getUiSchemasFromSteps(steps: Step[]): UiSchema[] { + const uiSchemas: Array = []; + steps.forEach(step => { + const schemaProps = step.schema.properties as JsonObject; + for (const key in schemaProps) { + if (schemaProps.hasOwnProperty(key)) { + const uiSchema = schemaProps[key] as UiSchema; + uiSchema.name = key; + uiSchemas.push(uiSchema); + } + } + }); + return uiSchemas; +} + +export function getReviewData(formData: Record, steps: Step[]) { + const uiSchemas = getUiSchemasFromSteps(steps); + const reviewData: Record = {}; + for (const key in formData) { + if (formData.hasOwnProperty(key)) { + const uiSchema = uiSchemas.find(us => us.name === key); + + if (!uiSchema) { + reviewData[key] = formData[key]; + continue; + } + + if (uiSchema['ui:widget'] === 'password') { + reviewData[key] = '******'; + continue; + } + + if (!uiSchema['ui:backstage'] || !uiSchema['ui:backstage'].review) { + reviewData[key] = formData[key]; + continue; + } + + const review = uiSchema['ui:backstage'].review as JsonObject; + if (!review.show) { + continue; + } + + if (review.mask) { + reviewData[key] = review.mask; + continue; + } + reviewData[key] = formData[key]; + } + } + + return reviewData; +} + export const MultistepJsonForm = ({ steps, formData, @@ -64,8 +117,9 @@ export const MultistepJsonForm = ({ setActiveStep(0); onReset(); }; - const handleNext = () => + const handleNext = () => { setActiveStep(Math.min(activeStep + 1, steps.length)); + }; const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0)); return ( @@ -113,7 +167,10 @@ export const MultistepJsonForm = ({ Review and create - + diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 3fed099500..e82a2640d6 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -57,7 +57,7 @@ export const ScaffolderPageContents = () => { pageTitleOverride="Create a New Component" title={ <> - Create a New Component + Create a New Component } subtitle="Create new software components using standard templates" diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index e72255f129..05533376db 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -160,7 +160,7 @@ export const TemplatePage = ({ pageTitleOverride="Create a New Component" title={ <> - Create a New Component + Create a New Component } subtitle="Create new software components using standard templates" diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 1a231c252e..3aa1ca4e56 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-search-backend-node +## 0.4.0 + +### Minor Changes + +- 97b2eb37b: Change return value of `SearchEngine.index` to `Promise` to support + implementation of external search engines. + ## 0.3.0 ### Minor Changes diff --git a/plugins/search-backend-node/api-report.md b/plugins/search-backend-node/api-report.md index d327dee01b..4418c4c590 100644 --- a/plugins/search-backend-node/api-report.md +++ b/plugins/search-backend-node/api-report.md @@ -11,13 +11,18 @@ import { default as lunr_2 } from 'lunr'; import { SearchQuery } from '@backstage/search-common'; import { SearchResultSet } from '@backstage/search-common'; +// Warning: (ae-missing-release-tag) "IndexBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class IndexBuilder { + // Warning: (ae-forgotten-export) The symbol "IndexBuilderOptions" needs to be exported by the entry point index.d.ts constructor({ logger, searchEngine }: IndexBuilderOptions); + // Warning: (ae-forgotten-export) The symbol "RegisterCollatorParameters" needs to be exported by the entry point index.d.ts addCollator({ collator, defaultRefreshIntervalSeconds, }: RegisterCollatorParameters): void; + // Warning: (ae-forgotten-export) The symbol "RegisterDecoratorParameters" needs to be exported by the entry point index.d.ts addDecorator({ decorator }: RegisterDecoratorParameters): void; build(): Promise<{ scheduler: Scheduler; @@ -26,6 +31,8 @@ export class IndexBuilder { getSearchEngine(): SearchEngine; } +// Warning: (ae-missing-release-tag) "LunrSearchEngine" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class LunrSearchEngine implements SearchEngine { constructor({ logger }: { logger: Logger_2 }); @@ -39,12 +46,18 @@ export class LunrSearchEngine implements SearchEngine { protected lunrIndices: Record; // (undocumented) query(query: SearchQuery): Promise; + // Warning: (ae-forgotten-export) The symbol "LunrQueryTranslator" needs to be exported by the entry point index.d.ts + // // (undocumented) setTranslator(translator: LunrQueryTranslator): void; + // Warning: (ae-forgotten-export) The symbol "QueryTranslator" needs to be exported by the entry point index.d.ts + // // (undocumented) protected translator: QueryTranslator; } +// Warning: (ae-missing-release-tag) "Scheduler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class Scheduler { constructor({ logger }: { logger: Logger_2 }); @@ -53,6 +66,8 @@ export class Scheduler { stop(): void; } +// Warning: (ae-missing-release-tag) "SearchEngine" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export interface SearchEngine { index(type: string, documents: IndexableDocument[]): Promise; diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index f889c10188..c30cf252da 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "0.3.0", + "version": "0.4.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,8 +25,8 @@ "@types/lunr": "^2.3.3" }, "devDependencies": { - "@backstage/backend-common": "^0.8.5", - "@backstage/cli": "^0.7.2" + "@backstage/backend-common": "^0.8.6", + "@backstage/cli": "^0.7.4" }, "files": [ "dist" diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 678bcc8169..637a1f02db 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-search-backend +## 0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.8.6 + - @backstage/plugin-search-backend-node@0.4.0 + ## 0.2.2 ### Patch Changes diff --git a/plugins/search-backend/api-report.md b/plugins/search-backend/api-report.md index b4202a1916..2a7457eb81 100644 --- a/plugins/search-backend/api-report.md +++ b/plugins/search-backend/api-report.md @@ -7,6 +7,9 @@ import express from 'express'; import { Logger as Logger_2 } from 'winston'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; +// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter({ engine, diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index d7e163198f..982c5a9ae7 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "0.2.2", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,9 +19,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", + "@backstage/backend-common": "^0.8.6", "@backstage/search-common": "^0.1.2", - "@backstage/plugin-search-backend-node": "^0.3.0", + "@backstage/plugin-search-backend-node": "^0.4.0", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -29,7 +29,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.7.1", + "@backstage/cli": "^0.7.4", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 18fe34c720..0e19f854bd 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-search +## 0.4.4 + +### Patch Changes + +- 9266b80ab: Adding a type filter to new search +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/plugin-catalog-react@0.3.1 + +## 0.4.3 + +### Patch Changes + +- 078d4973e: Handle request errors properly and display them in the results list. +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.4.2 ### Patch Changes diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 27ecf213bb..525136713d 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -17,6 +17,8 @@ import { SearchQuery } from '@backstage/search-common'; import { SearchResult as SearchResult_2 } from '@backstage/search-common'; import { SearchResultSet } from '@backstage/search-common'; +// Warning: (ae-missing-release-tag) "DefaultResultListItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DefaultResultListItem: ({ result, @@ -24,6 +26,9 @@ export const DefaultResultListItem: ({ result: IndexableDocument; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "FiltersProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Filters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Filters: ({ filters, @@ -33,27 +38,42 @@ export const Filters: ({ updateChecked, }: FiltersProps) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "FiltersButtonProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "FiltersButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const FiltersButton: ({ numberOfSelectedFilters, handleToggleFilters, }: FiltersButtonProps) => JSX.Element; +// Warning: (ae-missing-release-tag) "FiltersState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type FiltersState = { selected: string; checked: Array; }; +// Warning: (ae-missing-release-tag) "SearchPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "SearchApi" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "searchApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const searchApiRef: ApiRef; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "SearchBarNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated (undocumented) export const SearchBarNext: ({ className, @@ -63,6 +83,8 @@ export const SearchBarNext: ({ debounceTime?: number | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SearchContextProvider: ({ initialState, @@ -71,6 +93,8 @@ export const SearchContextProvider: ({ initialState?: SettableSearchContext | undefined; }>) => JSX.Element; +// Warning: (ae-missing-release-tag) "SearchFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SearchFilter: { ({ component: Element, ...props }: Props_2): JSX.Element; @@ -78,6 +102,8 @@ export const SearchFilter: { Select(props: Omit & Component): JSX.Element; }; +// Warning: (ae-missing-release-tag) "SearchFilterNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated (undocumented) export const SearchFilterNext: { ({ component: Element, ...props }: Props_2): JSX.Element; @@ -85,12 +111,18 @@ export const SearchFilterNext: { Select(props: Omit & Component): JSX.Element; }; +// Warning: (ae-missing-release-tag) "SearchPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SearchPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "SearchPageNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public @deprecated (undocumented) export const SearchPageNext: () => JSX.Element; +// Warning: (ae-missing-release-tag) "searchPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const searchPlugin: BackstagePlugin< { @@ -102,6 +134,8 @@ const searchPlugin: BackstagePlugin< export { searchPlugin as plugin }; export { searchPlugin }; +// Warning: (ae-missing-release-tag) "SearchResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SearchResult: ({ children, @@ -109,11 +143,33 @@ export const SearchResult: ({ children: (results: { results: SearchResult_2[] }) => JSX.Element; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "SearchTypeProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SearchType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const SearchType: ({ + values, + className, + name, + defaultValue, +}: SearchTypeProps) => JSX.Element; + +// Warning: (ae-missing-release-tag) "SidebarSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SidebarSearch: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "SearchContextValue" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "useSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const useSearch: () => SearchContextValue; +// Warnings were encountered during analysis: +// +// src/components/SearchContext/SearchContext.d.ts:18:5 - (ae-forgotten-export) The symbol "SettableSearchContext" needs to be exported by the entry point index.d.ts +// src/components/SearchFilter/SearchFilter.d.ts:13:5 - (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// src/components/SearchFilter/SearchFilter.d.ts:14:5 - (ae-forgotten-export) The symbol "Component" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/search/package.json b/plugins/search/package.json index c40885e357..a6ca45497a 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "0.4.2", + "version": "0.4.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,10 @@ "dependencies": { "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/errors": "^0.1.1", + "@backstage/plugin-catalog-react": "^0.3.1", "@backstage/search-common": "^0.1.2", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -48,10 +49,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", diff --git a/plugins/search/src/apis.test.ts b/plugins/search/src/apis.test.ts index 43b77e4927..cc59ca1a77 100644 --- a/plugins/search/src/apis.test.ts +++ b/plugins/search/src/apis.test.ts @@ -44,7 +44,7 @@ describe('apis', () => { const json = jest.fn(); const originalFetch = window.fetch; - window.fetch = jest.fn().mockResolvedValue({ json }); + window.fetch = jest.fn().mockResolvedValue({ json, ok: true }); afterAll(() => { window.fetch = originalFetch; diff --git a/plugins/search/src/apis.ts b/plugins/search/src/apis.ts index da8332bd44..fa6d9ce908 100644 --- a/plugins/search/src/apis.ts +++ b/plugins/search/src/apis.ts @@ -14,13 +14,14 @@ * limitations under the License. */ -import { SearchQuery, SearchResultSet } from '@backstage/search-common'; -import qs from 'qs'; import { createApiRef, DiscoveryApi, IdentityApi, } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; +import { SearchQuery, SearchResultSet } from '@backstage/search-common'; +import qs from 'qs'; export const searchApiRef = createApiRef({ id: 'plugin.search.queryservice', @@ -52,6 +53,11 @@ export class SearchClient implements SearchApi { const response = await fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : {}, }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + return response.json(); } } diff --git a/plugins/search/src/components/SearchResult/SearchResult.test.tsx b/plugins/search/src/components/SearchResult/SearchResult.test.tsx index 9508eec259..bb8f393d67 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.test.tsx +++ b/plugins/search/src/components/SearchResult/SearchResult.test.tsx @@ -14,11 +14,11 @@ * limitations under the License. */ +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; import React from 'react'; -import { render, waitFor } from '@testing-library/react'; - -import { SearchResult } from './SearchResult'; import { useSearch } from '../SearchContext'; +import { SearchResult } from './SearchResult'; jest.mock('../SearchContext', () => ({ ...jest.requireActual('../SearchContext'), @@ -33,7 +33,9 @@ describe('SearchResult', () => { result: { loading: true }, }); - const { getByRole } = render({() => <>}); + const { getByRole } = await renderInTestApp( + {() => <>}, + ); await waitFor(() => { expect(getByRole('progressbar')).toBeInTheDocument(); @@ -41,16 +43,18 @@ describe('SearchResult', () => { }); it('Alert rendered on Error state', async () => { - const error = 'error'; + const error = new Error('some error'); (useSearch as jest.Mock).mockReturnValueOnce({ result: { loading: false, error }, }); - const { getByRole } = render({() => <>}); + const { getByRole } = await renderInTestApp( + {() => <>}, + ); await waitFor(() => { expect(getByRole('alert')).toHaveTextContent( - `Error encountered while fetching search results. ${error}`, + new RegExp(`Error encountered while fetching search results.*${error}`), ); }); }); @@ -60,7 +64,9 @@ describe('SearchResult', () => { result: { loading: false, error: '', value: undefined }, }); - const { getByRole } = render({() => <>}); + const { getByRole } = await renderInTestApp( + {() => <>}, + ); await waitFor(() => { expect( @@ -74,7 +80,9 @@ describe('SearchResult', () => { result: { loading: false, error: '', value: { results: [] } }, }); - const { getByRole } = render({() => <>}); + const { getByRole } = await renderInTestApp( + {() => <>}, + ); await waitFor(() => { expect( @@ -83,12 +91,12 @@ describe('SearchResult', () => { }); }); - it('Calls children with results set to result.value', () => { + it('Calls children with results set to result.value', async () => { (useSearch as jest.Mock).mockReturnValueOnce({ result: { loading: false, error: '', value: { results: [] } }, }); - render( + await renderInTestApp( {({ results }) => { expect(results).toEqual([]); diff --git a/plugins/search/src/components/SearchResult/SearchResult.tsx b/plugins/search/src/components/SearchResult/SearchResult.tsx index 7ca5964d21..26a2484a86 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.tsx +++ b/plugins/search/src/components/SearchResult/SearchResult.tsx @@ -14,13 +14,15 @@ * limitations under the License. */ -import React from 'react'; +import { + EmptyState, + Progress, + ResponseErrorPanel, +} from '@backstage/core-components'; import { SearchResult } from '@backstage/search-common'; -import { Alert } from '@material-ui/lab'; +import React from 'react'; import { useSearch } from '../SearchContext'; -import { EmptyState, Progress } from '@backstage/core-components'; - type Props = { children: (results: { results: SearchResult[] }) => JSX.Element; }; @@ -35,9 +37,10 @@ const SearchResultComponent = ({ children }: Props) => { } if (error) { return ( - - Error encountered while fetching search results. {error.toString()} - + ); } diff --git a/plugins/search/src/components/SearchType/SearchType.test.tsx b/plugins/search/src/components/SearchType/SearchType.test.tsx new file mode 100644 index 0000000000..1ca032e41d --- /dev/null +++ b/plugins/search/src/components/SearchType/SearchType.test.tsx @@ -0,0 +1,231 @@ +/* + * 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. + */ + +import React from 'react'; +import { screen, render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { SearchType } from './SearchType'; + +import { SearchContextProvider } from '../SearchContext'; +import { useApi } from '@backstage/core-plugin-api'; + +jest.mock('@backstage/core-plugin-api', () => ({ + ...jest.requireActual('@backstage/core-plugin-api'), + useApi: jest.fn().mockReturnValue({}), +})); + +describe('SearchType', () => { + const initialState = { + term: '', + filters: {}, + types: [], + pageCursor: '', + }; + + const name = 'field'; + const values = ['value1', 'value2']; + const typeValues = ['preselected']; + + const query = jest.fn().mockResolvedValue({}); + (useApi as jest.Mock).mockReturnValue({ query: query }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + describe('Type Filter', () => { + it('Renders field name and values when provided as props', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(name)).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect( + screen.getByRole('option', { name: values[0] }), + ).toBeInTheDocument(); + expect( + screen.getByRole('option', { name: values[1] }), + ).toBeInTheDocument(); + }); + + it('Renders correctly based on type filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(name)).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect(screen.getByRole('option', { name: values[0] })).toHaveAttribute( + 'aria-selected', + 'true', + ); + expect( + screen.getByRole('option', { name: values[1] }), + ).not.toHaveAttribute('aria-selected'); + expect(screen.getByRole('option', { name: 'All' })).not.toHaveAttribute( + 'aria-selected', + ); + }); + + it('Renders correctly based on type filter defaultValue', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(name)).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect(screen.getByRole('option', { name: values[0] })).toHaveAttribute( + 'aria-selected', + 'true', + ); + expect( + screen.getByRole('option', { name: values[1] }), + ).not.toHaveAttribute('aria-selected'); + expect(screen.getByRole('option', { name: 'All' })).not.toHaveAttribute( + 'aria-selected', + ); + }); + + it('Selecting a value sets type filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(name)).toBeInTheDocument(); + }); + + const button = screen.getByRole('button'); + + userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('option', { name: values[0] })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + types: [values[0]], + }), + ); + }); + + userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('option', { name: 'All' })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + types: [], + }), + ); + }); + }); + + it('Selecting a value maintains unrelated filter state, selecting All defaults to default empty state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(name)).toBeInTheDocument(); + }); + + const button = screen.getByRole('button'); + + userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('option', { name: values[0] })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + types: [...typeValues, values[0]], + }), + ); + }); + + userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByRole('option', { name: 'All' })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith(expect.objectContaining([])); + }); + }); + }); +}); diff --git a/plugins/search/src/components/SearchType/SearchType.tsx b/plugins/search/src/components/SearchType/SearchType.tsx new file mode 100644 index 0000000000..6e57879064 --- /dev/null +++ b/plugins/search/src/components/SearchType/SearchType.tsx @@ -0,0 +1,110 @@ +/* + * 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. + */ +import { useSearch } from '../SearchContext'; +import { useEffectOnce } from 'react-use'; +import React, { ChangeEvent } from 'react'; +import { + Chip, + FormControl, + InputLabel, + makeStyles, + MenuItem, + Select, +} from '@material-ui/core'; + +const useStyles = makeStyles({ + label: { + textTransform: 'capitalize', + }, + chips: { + display: 'flex', + flexWrap: 'wrap', + }, + chip: { + margin: 2, + }, +}); + +export type SearchTypeProps = { + className?: string; + name: string; + values?: string[]; + defaultValue?: string[] | string | null; +}; + +const SearchType = ({ + values = [], + className, + name, + defaultValue, +}: SearchTypeProps) => { + const classes = useStyles(); + const { types, setTypes } = useSearch(); + + useEffectOnce(() => { + if (defaultValue && Array.isArray(defaultValue)) { + setTypes(defaultValue); + } else if (defaultValue) { + setTypes([defaultValue]); + } + }); + + const handleChange = (e: ChangeEvent<{ value: unknown }>) => { + const value = e.target.value as string[]; + if (!value || value.includes('*')) { + setTypes([]); + } else { + setTypes(value.filter(it => it !== 'All')); + } + }; + + return ( + + + {name} + + + + ); +}; + +export { SearchType }; diff --git a/plugins/search/src/components/SearchType/index.ts b/plugins/search/src/components/SearchType/index.ts new file mode 100644 index 0000000000..400f3d2a46 --- /dev/null +++ b/plugins/search/src/components/SearchType/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { SearchType } from './SearchType'; diff --git a/plugins/search/src/components/index.tsx b/plugins/search/src/components/index.tsx index 245c31598d..889eb36334 100644 --- a/plugins/search/src/components/index.tsx +++ b/plugins/search/src/components/index.tsx @@ -16,6 +16,7 @@ export * from './Filters'; export * from './SearchFilter'; +export * from './SearchType'; export * from './SearchBar'; export * from './SearchPage'; export * from './SearchResult'; diff --git a/plugins/search/src/index.ts b/plugins/search/src/index.ts index ecd6015b1c..f09ba739bd 100644 --- a/plugins/search/src/index.ts +++ b/plugins/search/src/index.ts @@ -32,6 +32,7 @@ export { useSearch, SearchPage as Router, SearchFilter, + SearchType, SearchFilterNext, SidebarSearch, } from './components'; diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index 5153be2b93..d341c1803e 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-sentry +## 0.3.15 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.3.14 ### Patch Changes diff --git a/plugins/sentry/api-report.md b/plugins/sentry/api-report.md index ebb4cfa357..92ea326f87 100644 --- a/plugins/sentry/api-report.md +++ b/plugins/sentry/api-report.md @@ -12,18 +12,26 @@ import { Entity } from '@backstage/catalog-model'; import { InfoCardVariants } from '@backstage/core-components'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntitySentryCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySentryCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "EntitySentryContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySentryContent: () => JSX.Element; +// Warning: (ae-missing-release-tag) "MockSentryApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class MockSentryApi implements SentryApi { // (undocumented) fetchIssues(): Promise; } +// Warning: (ae-missing-release-tag) "ProductionSentryApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class ProductionSentryApi implements SentryApi { constructor(discoveryApi: DiscoveryApi, organization: string); @@ -31,18 +39,26 @@ export class ProductionSentryApi implements SentryApi { fetchIssues(project: string, statsFor: string): Promise; } +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: ({ entity }: { entity: Entity }) => JSX.Element; +// Warning: (ae-missing-release-tag) "SentryApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface SentryApi { // (undocumented) fetchIssues(project: string, statsFor: string): Promise; } +// Warning: (ae-missing-release-tag) "sentryApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const sentryApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "SentryIssue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type SentryIssue = { platform: SentryPlatform; @@ -77,6 +93,8 @@ export type SentryIssue = { statusDetails: any; }; +// Warning: (ae-missing-release-tag) "SentryIssuesWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SentryIssuesWidget: ({ entity, @@ -88,6 +106,8 @@ export const SentryIssuesWidget: ({ variant?: InfoCardVariants | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "sentryPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const sentryPlugin: BackstagePlugin< { @@ -98,5 +118,12 @@ const sentryPlugin: BackstagePlugin< export { sentryPlugin as plugin }; export { sentryPlugin }; +// Warnings were encountered during analysis: +// +// src/api/sentry-issue.d.ts:16:5 - (ae-forgotten-export) The symbol "SentryPlatform" needs to be exported by the entry point index.d.ts +// src/api/sentry-issue.d.ts:21:9 - (ae-forgotten-export) The symbol "EventPoint" needs to be exported by the entry point index.d.ts +// src/api/sentry-issue.d.ts:31:5 - (ae-forgotten-export) The symbol "SentryIssueMetadata" needs to be exported by the entry point index.d.ts +// src/api/sentry-issue.d.ts:44:5 - (ae-forgotten-export) The symbol "SentryProject" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 4d670a8a7f..989a18a6d9 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.3.14", + "version": "0.3.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,10 +47,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/shortcuts/api-report.md b/plugins/shortcuts/api-report.md index 7aa54b8c3b..3d6a56244a 100644 --- a/plugins/shortcuts/api-report.md +++ b/plugins/shortcuts/api-report.md @@ -11,6 +11,8 @@ import { Observable } from '@backstage/core-plugin-api'; import ObservableImpl from 'zen-observable'; import { StorageApi } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "LocalStoredShortcuts" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class LocalStoredShortcuts implements ShortcutApi { constructor(storageApi: StorageApi); @@ -26,6 +28,8 @@ export class LocalStoredShortcuts implements ShortcutApi { update(shortcut: Shortcut): Promise; } +// Warning: (ae-missing-release-tag) "Shortcut" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type Shortcut = { id: string; @@ -33,6 +37,8 @@ export type Shortcut = { title: string; }; +// Warning: (ae-missing-release-tag) "ShortcutApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface ShortcutApi { add(shortcut: Omit): Promise; @@ -42,12 +48,18 @@ export interface ShortcutApi { update(shortcut: Shortcut): Promise; } +// Warning: (ae-missing-release-tag) "Shortcuts" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Shortcuts: () => JSX.Element; +// Warning: (ae-missing-release-tag) "shortcutsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const shortcutsApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "shortcutsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const shortcutsPlugin: BackstagePlugin<{}, {}>; diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 1dbbbb1a18..293d71eeb9 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -36,10 +36,10 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 390d8e4a68..0da72704da 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-sonarqube +## 0.1.22 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.1.21 ### Patch Changes diff --git a/plugins/sonarqube/api-report.md b/plugins/sonarqube/api-report.md index 92d0cfa476..642e1d956f 100644 --- a/plugins/sonarqube/api-report.md +++ b/plugins/sonarqube/api-report.md @@ -9,6 +9,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { InfoCardVariants } from '@backstage/core-components'; +// Warning: (ae-missing-release-tag) "EntitySonarQubeCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySonarQubeCard: ({ variant, @@ -24,9 +26,13 @@ export const EntitySonarQubeCard: ({ | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "isSonarQubeAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const isSonarQubeAvailable: (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "SonarQubeCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SonarQubeCard: ({ variant, @@ -37,10 +43,16 @@ export const SonarQubeCard: ({ duplicationRatings?: DuplicationRating[] | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "sonarQubePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const sonarQubePlugin: BackstagePlugin<{}, {}>; export { sonarQubePlugin as plugin }; export { sonarQubePlugin }; +// Warnings were encountered during analysis: +// +// src/components/SonarQubeCard/SonarQubeCard.d.ts:11:5 - (ae-forgotten-export) The symbol "DuplicationRating" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 3d120abf49..a132da5702 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube", - "version": "0.1.21", + "version": "0.1.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,9 +33,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -48,10 +48,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index a3a60c1b9d..083d5b5749 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-splunk-on-call +## 0.3.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.3.4 ### Patch Changes diff --git a/plugins/splunk-on-call/api-report.md b/plugins/splunk-on-call/api-report.md index d17eb01564..02ebcc324f 100644 --- a/plugins/splunk-on-call/api-report.md +++ b/plugins/splunk-on-call/api-report.md @@ -12,33 +12,55 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "EntitySplunkOnCallCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntitySplunkOnCallCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "isSplunkOnCallAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const isSplunkOnCallAvailable: (entity: Entity) => boolean; +// Warning: (ae-forgotten-export) The symbol "SplunkOnCallApi" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "splunkOnCallApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const splunkOnCallApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "SplunkOnCallClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class SplunkOnCallClient implements SplunkOnCallApi { + // Warning: (ae-forgotten-export) The symbol "ClientApiConfig" needs to be exported by the entry point index.d.ts constructor(config: ClientApiConfig); // (undocumented) static fromConfig( configApi: ConfigApi, discoveryApi: DiscoveryApi, ): SplunkOnCallClient; + // Warning: (ae-forgotten-export) The symbol "EscalationPolicyInfo" needs to be exported by the entry point index.d.ts + // // (undocumented) getEscalationPolicies(): Promise; + // Warning: (ae-forgotten-export) The symbol "Incident" needs to be exported by the entry point index.d.ts + // // (undocumented) getIncidents(): Promise; + // Warning: (ae-forgotten-export) The symbol "OnCall" needs to be exported by the entry point index.d.ts + // // (undocumented) getOnCallUsers(): Promise; + // Warning: (ae-forgotten-export) The symbol "Team" needs to be exported by the entry point index.d.ts + // // (undocumented) getTeams(): Promise; + // Warning: (ae-forgotten-export) The symbol "User" needs to be exported by the entry point index.d.ts + // // (undocumented) getUsers(): Promise; + // Warning: (ae-forgotten-export) The symbol "TriggerAlarmRequest" needs to be exported by the entry point index.d.ts + // // (undocumented) incidentAction({ routingKey, @@ -50,6 +72,8 @@ export class SplunkOnCallClient implements SplunkOnCallApi { }: TriggerAlarmRequest): Promise; } +// Warning: (ae-missing-release-tag) "SplunkOnCallPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const SplunkOnCallPage: { ({ title, subtitle, pageTitle }: SplunkOnCallPageProps): JSX.Element; @@ -60,6 +84,8 @@ export const SplunkOnCallPage: { }; }; +// Warning: (ae-missing-release-tag) "splunkOnCallPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const splunkOnCallPlugin: BackstagePlugin< { @@ -70,8 +96,14 @@ const splunkOnCallPlugin: BackstagePlugin< export { splunkOnCallPlugin as plugin }; export { splunkOnCallPlugin }; +// Warning: (ae-missing-release-tag) "UnauthorizedError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class UnauthorizedError extends Error {} +// Warnings were encountered during analysis: +// +// src/plugin.d.ts:7:5 - (ae-forgotten-export) The symbol "SplunkOnCallPageProps" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index b553c4510d..d779df3f97 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-splunk-on-call", - "version": "0.3.4", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,10 +46,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/tech-radar/api-report.md b/plugins/tech-radar/api-report.md index 4087e0d196..c93c10cb47 100644 --- a/plugins/tech-radar/api-report.md +++ b/plugins/tech-radar/api-report.md @@ -9,6 +9,8 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "RadarEntry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RadarEntry { // (undocumented) @@ -27,18 +29,24 @@ export interface RadarEntry { url: string; } +// Warning: (ae-missing-release-tag) "RadarEntrySnapshot" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RadarEntrySnapshot { // (undocumented) date: Date; // (undocumented) description?: string; + // Warning: (ae-forgotten-export) The symbol "MovedState" needs to be exported by the entry point index.d.ts + // // (undocumented) moved?: MovedState; // (undocumented) ringId: string; } +// Warning: (ae-missing-release-tag) "RadarQuadrant" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RadarQuadrant { // (undocumented) @@ -47,6 +55,8 @@ export interface RadarQuadrant { name: string; } +// Warning: (ae-missing-release-tag) "RadarRing" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface RadarRing { // (undocumented) @@ -57,6 +67,8 @@ export interface RadarRing { name: string; } +// Warning: (ae-missing-release-tag) "RadarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: { ({ title, subtitle, pageTitle, ...props }: TechRadarPageProps): JSX.Element; @@ -67,19 +79,27 @@ export const Router: { }; }; +// Warning: (ae-missing-release-tag) "TechRadarApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TechRadarApi { load: (id: string | undefined) => Promise; } +// Warning: (ae-missing-release-tag) "techRadarApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const techRadarApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "RadarComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TechRadarComponent: ( props: TechRadarComponentProps, ) => JSX.Element; +// Warning: (ae-missing-release-tag) "TechRadarComponentProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TechRadarComponentProps { // (undocumented) @@ -92,6 +112,8 @@ export interface TechRadarComponentProps { width: number; } +// Warning: (ae-missing-release-tag) "TechRadarLoaderResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TechRadarLoaderResponse { // (undocumented) @@ -102,6 +124,8 @@ export interface TechRadarLoaderResponse { rings: RadarRing[]; } +// Warning: (ae-missing-release-tag) "TechRadarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TechRadarPage: { ({ title, subtitle, pageTitle, ...props }: TechRadarPageProps): JSX.Element; @@ -112,6 +136,8 @@ export const TechRadarPage: { }; }; +// Warning: (ae-missing-release-tag) "techRadarPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const techRadarPlugin: BackstagePlugin< { @@ -122,5 +148,9 @@ const techRadarPlugin: BackstagePlugin< export { techRadarPlugin as plugin }; export { techRadarPlugin }; +// Warnings were encountered during analysis: +// +// src/components/RadarPage.d.ts:9:5 - (ae-forgotten-export) The symbol "TechRadarPageProps" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index b6e9ccacc8..305061087b 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -44,10 +44,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index c6dc26cb63..4acab08f26 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,116 @@ # @backstage/plugin-techdocs-backend +## 0.9.0 + +### Minor Changes + +- d32d01e5b: Improve the annotation `backstage.io/techdocs-ref: dir:` that links to a path that is relative to the source of the annotated entity. + This annotation works with the basic and the recommended flow, however, it will be most useful with the basic approach. + + This change remove the deprecation of the `dir` reference and provides first-class support for it. + In addition, this change removes the support of the deprecated `github`, `gitlab`, and `azure/api` locations from the `dir` reference preparer. + + #### Example Usage + + The annotation is convenient if the documentation is stored in the same location, i.e. the same git repository, as the `catalog-info.yaml`. + While it is still supported to add full URLs such as `backstage.io/techdocs-ref: url:https://...` for custom setups, documentation is mostly stored in the same repository as the entity definition. + By automatically resolving the target relative to the registration location of the entity, the configuration overhead for this default setup is minimized. + Since it leverages the `@backstage/integrations` package for the URL resolution, this is compatible with every supported source. + + Consider the following examples: + + 1. "I have a repository with a single `catalog-info.yaml` and a TechDocs page in the root folder!" + + ``` + https://github.com/backstage/example/tree/main/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: example + | > annotations: + | > backstage.io/techdocs-ref: dir:. # -> same folder + | > spec: {} + |- docs/ + |- mkdocs.yml + ``` + + 2. "I have a repository with a single `catalog-info.yaml` and my TechDocs page in located in a folder!" + + ``` + https://bitbucket.org/my-owner/my-project/src/master/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: example + | > annotations: + | > backstage.io/techdocs-ref: dir:./some-folder # -> subfolder + | > spec: {} + |- some-folder/ + |- docs/ + |- mkdocs.yml + ``` + + 3. "I have a mono repository that hosts multiple components!" + + ``` + https://dev.azure.com/organization/project/_git/repository + |- my-1st-module/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: my-1st-module + | > annotations: + | > backstage.io/techdocs-ref: dir:. # -> same folder + | > spec: {} + |- docs/ + |- mkdocs.yml + |- my-2nd-module/ + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Component + | > metadata: + | > name: my-2nd-module + | > annotations: + | > backstage.io/techdocs-ref: dir:. # -> same folder + | > spec: {} + |- docs/ + |- mkdocs.yml + |- catalog-info.yaml + | > apiVersion: backstage.io/v1alpha1 + | > kind: Location + | > metadata: + | > name: example + | > spec: + | > targets: + | > - ./*/catalog-info.yaml + ``` + +### Patch Changes + +- 9266b80ab: Implements tech docs collator to retrieve and expose search indexes for entities that have tech docs configured +- Updated dependencies + - @backstage/techdocs-common@0.7.0 + - @backstage/catalog-client@0.3.17 + - @backstage/backend-common@0.8.7 + +## 0.8.7 + +### Patch Changes + +- f1200f44c: Rewrite the `/sync/:namespace/:kind/:name` endpoint to support an event-stream as response. + This change allows the sync process to take longer than a normal HTTP timeout. + The stream also emits log events, so the caller can follow the build process in the frontend. +- 35a67722b: It is no longer required to provide a generator and a preparer to the TechDocs + router factory when running TechDocs in the "recommended" (e.g. externally + prepared and generated docs) configuration. +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. +- Updated dependencies + - @backstage/backend-common@0.8.6 + - @backstage/techdocs-common@0.6.8 + ## 0.8.6 ### Patch Changes diff --git a/plugins/techdocs-backend/api-report.md b/plugins/techdocs-backend/api-report.md index 5777a3fd56..f617b796aa 100644 --- a/plugins/techdocs-backend/api-report.md +++ b/plugins/techdocs-backend/api-report.md @@ -3,18 +3,72 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; +import { DocumentCollator } from '@backstage/search-common'; import express from 'express'; import { GeneratorBuilder } from '@backstage/techdocs-common'; +import { IndexableDocument } from '@backstage/search-common'; import { Knex } from 'knex'; import { Logger as Logger_2 } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PreparerBuilder } from '@backstage/techdocs-common'; import { PublisherBase } from '@backstage/techdocs-common'; +// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "DefaultTechDocsCollator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class DefaultTechDocsCollator implements DocumentCollator { + constructor({ + discovery, + locationTemplate, + logger, + catalogClient, + parallelismLimit, + }: { + discovery: PluginEndpointDiscovery; + logger: Logger_2; + locationTemplate?: string; + catalogClient?: CatalogApi; + parallelismLimit?: number; + }); + // (undocumented) + protected applyArgsToFormat( + format: string, + args: Record, + ): string; + // (undocumented) + protected discovery: PluginEndpointDiscovery; + // (undocumented) + execute(): Promise; + // (undocumented) + protected locationTemplate: string; + // (undocumented) + readonly type: string; +} + +// Warning: (ae-missing-release-tag) "TechDocsDocument" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface TechDocsDocument extends IndexableDocument { + // (undocumented) + kind: string; + // (undocumented) + lifecycle: string; + // (undocumented) + name: string; + // (undocumented) + namespace: string; + // (undocumented) + owner: string; +} + export * from '@backstage/techdocs-common'; // (No @packageDocumentation comment for this package) diff --git a/plugins/techdocs-backend/config.d.ts b/plugins/techdocs-backend/config.d.ts index b19e7af175..7f2fdf8b26 100644 --- a/plugins/techdocs-backend/config.d.ts +++ b/plugins/techdocs-backend/config.d.ts @@ -29,7 +29,31 @@ export interface Config { /** * Techdocs generator information */ + generator?: { + /** + * Where to run the techdocs (mkdocs) generator + */ + runIn: 'local' | 'docker'; + + /** + * Override the default techdocs docker image + */ + dockerImage?: string; + + /** + * Pull the latest docker image + */ + pullImage?: boolean; + }; + + /** + * Techdocs generator information + * @deprecated Replaced with techdocs.generator + */ generators?: { + /** + * @deprecated Use techdocs.generator.runIn + */ techdocs: 'local' | 'docker'; }; diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 714f3ad442..a6a4fbe46a 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.8.6", + "version": "0.9.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.5", + "@backstage/backend-common": "^0.8.7", + "@backstage/catalog-client": "^0.3.17", "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/techdocs-common": "^0.6.7", + "@backstage/integration": "^0.5.8", + "@backstage/search-common": "^0.1.2", + "@backstage/techdocs-common": "^0.7.0", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "dockerode": "^3.2.1", @@ -42,11 +45,15 @@ "express-promise-router": "^4.1.0", "fs-extra": "9.1.0", "knex": "^0.95.1", + "lodash": "^4.17.21", + "p-limit": "^3.1.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.7.3", + "@backstage/cli": "^0.7.5", + "@backstage/test-utils": "^0.1.15", "@types/dockerode": "^3.2.1", + "msw": "^0.29.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/techdocs-backend/src/DocsBuilder/builder.ts b/plugins/techdocs-backend/src/DocsBuilder/builder.ts index e9df0b8400..5327062233 100644 --- a/plugins/techdocs-backend/src/DocsBuilder/builder.ts +++ b/plugins/techdocs-backend/src/DocsBuilder/builder.ts @@ -18,7 +18,9 @@ import { ENTITY_DEFAULT_NAMESPACE, stringifyEntityRef, } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; import { NotModifiedError } from '@backstage/errors'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { GeneratorBase, GeneratorBuilder, @@ -31,8 +33,8 @@ import { import fs from 'fs-extra'; import os from 'os'; import path from 'path'; +import { Writable } from 'stream'; import { Logger } from 'winston'; -import { Config } from '@backstage/config'; import { BuildMetadataStorage } from './BuildMetadataStorage'; type DocsBuilderArguments = { @@ -42,6 +44,8 @@ type DocsBuilderArguments = { entity: Entity; logger: Logger; config: Config; + scmIntegrations: ScmIntegrationRegistry; + logStream?: Writable; }; export class DocsBuilder { @@ -51,6 +55,8 @@ export class DocsBuilder { private entity: Entity; private logger: Logger; private config: Config; + private scmIntegrations: ScmIntegrationRegistry; + private logStream: Writable | undefined; constructor({ preparers, @@ -59,6 +65,8 @@ export class DocsBuilder { entity, logger, config, + scmIntegrations, + logStream, }: DocsBuilderArguments) { this.preparer = preparers.get(entity); this.generator = generators.get(entity); @@ -66,6 +74,8 @@ export class DocsBuilder { this.entity = entity; this.logger = logger; this.config = config; + this.scmIntegrations = scmIntegrations; + this.logStream = logStream; } /** @@ -115,6 +125,7 @@ export class DocsBuilder { try { const preparerResponse = await this.preparer.prepare(this.entity, { etag: storedEtag, + logger: this.logger, }); preparedDir = preparerResponse.preparedDir; @@ -159,12 +170,18 @@ export class DocsBuilder { const outputDir = await fs.mkdtemp( path.join(tmpdirResolvedPath, 'techdocs-tmp-'), ); - const parsedLocationAnnotation = getLocationForEntity(this.entity); + + const parsedLocationAnnotation = getLocationForEntity( + this.entity, + this.scmIntegrations, + ); await this.generator.run({ inputDir: preparedDir, outputDir, parsedLocationAnnotation, etag: newEtag, + logger: this.logger, + logStream: this.logStream, }); // Remove Prepared directory since it is no longer needed. diff --git a/plugins/techdocs-backend/src/index.ts b/plugins/techdocs-backend/src/index.ts index afbf04aba9..e2a89237db 100644 --- a/plugins/techdocs-backend/src/index.ts +++ b/plugins/techdocs-backend/src/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ -export * from './service/router'; +export { createRouter } from './service/router'; +export * from './search'; export * from '@backstage/techdocs-common'; diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts new file mode 100644 index 0000000000..618bb54d60 --- /dev/null +++ b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts @@ -0,0 +1,149 @@ +/* + * 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. + */ + +import { + PluginEndpointDiscovery, + getVoidLogger, +} from '@backstage/backend-common'; +import { Entity } from '@backstage/catalog-model'; +import { DefaultTechDocsCollator } from './DefaultTechDocsCollator'; +import { msw } from '@backstage/test-utils'; +import { setupServer } from 'msw/node'; +import { rest } from 'msw'; + +const logger = getVoidLogger(); + +const mockSearchDocIndex = { + config: { + lang: ['en'], + min_search_length: 3, + prebuild_index: false, + separator: '[\\s\\-]+', + }, + docs: [ + { + location: '', + text: 'docs docs docs', + title: 'Home', + }, + { + location: 'local-development/', + text: 'Docs for first subtitle', + title: 'Local development', + }, + { + location: 'local-development/#development', + text: 'Docs for sub-subtitle', + title: 'Development', + }, + ], +}; + +const expectedEntities: Entity[] = [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test-entity-with-docs', + description: 'Documented description', + annotations: { + 'backstage.io/techdocs-ref': './', + }, + }, + spec: { + type: 'dog', + lifecycle: 'experimental', + owner: 'someone', + }, + }, + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test-entity', + description: 'The expected description', + }, + spec: { + type: 'some-type', + lifecycle: 'experimental', + }, + }, +]; + +describe('DefaultTechDocsCollator', () => { + let mockDiscoveryApi: jest.Mocked; + let collator: DefaultTechDocsCollator; + + const worker = setupServer(); + msw.setupDefaultHandlers(worker); + beforeEach(() => { + mockDiscoveryApi = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; + collator = new DefaultTechDocsCollator({ + discovery: mockDiscoveryApi, + logger, + }); + + worker.use( + rest.get( + 'http://test-backend/static/docs/default/Component/test-entity-with-docs/search/search_index.json', + (_, res, ctx) => res(ctx.status(200), ctx.json(mockSearchDocIndex)), + ), + rest.get('http://test-backend/entities', (_, res, ctx) => + res(ctx.status(200), ctx.json(expectedEntities)), + ), + ); + }); + + it('fetches from the configured catalog and tech docs services', async () => { + const documents = await collator.execute(); + expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('catalog'); + expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('techdocs'); + expect(documents).toHaveLength(mockSearchDocIndex.docs.length); + }); + + it('should create documents for each tech docs search index', async () => { + const documents = await collator.execute(); + const entity = expectedEntities[0]; + documents.forEach((document, idx) => { + expect(document).toMatchObject({ + title: mockSearchDocIndex.docs[idx].title, + location: `/docs/default/Component/${entity.metadata.name}/${mockSearchDocIndex.docs[idx].location}`, + text: mockSearchDocIndex.docs[idx].text, + namespace: 'default', + componentType: entity!.spec!.type, + lifecycle: entity!.spec!.lifecycle, + owner: '', + }); + }); + }); + + it('maps a returned entity with a custom locationTemplate', async () => { + // Provide an alternate location template. + collator = new DefaultTechDocsCollator({ + discovery: mockDiscoveryApi, + locationTemplate: '/software/:name', + logger, + }); + + const documents = await collator.execute(); + expect(documents[0]).toMatchObject({ + location: '/software/test-entity-with-docs', + }); + }); +}); diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts new file mode 100644 index 0000000000..7c176da9fb --- /dev/null +++ b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts @@ -0,0 +1,149 @@ +/* + * 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. + */ + +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { IndexableDocument, DocumentCollator } from '@backstage/search-common'; +import fetch from 'cross-fetch'; +import unescape from 'lodash/unescape'; +import { Logger } from 'winston'; +import pLimit from 'p-limit'; +import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; + +interface MkSearchIndexDoc { + title: string; + text: string; + location: string; +} + +export interface TechDocsDocument extends IndexableDocument { + kind: string; + namespace: string; + name: string; + lifecycle: string; + owner: string; +} + +export class DefaultTechDocsCollator implements DocumentCollator { + protected discovery: PluginEndpointDiscovery; + protected locationTemplate: string; + private readonly logger: Logger; + private readonly catalogClient: CatalogApi; + private readonly parallelismLimit: number; + public readonly type: string = 'techdocs'; + + constructor({ + discovery, + locationTemplate, + logger, + catalogClient, + parallelismLimit = 10, + }: { + discovery: PluginEndpointDiscovery; + logger: Logger; + locationTemplate?: string; + catalogClient?: CatalogApi; + parallelismLimit?: number; + }) { + this.discovery = discovery; + this.locationTemplate = + locationTemplate || '/docs/:namespace/:kind/:name/:path'; + this.logger = logger; + this.catalogClient = + catalogClient || new CatalogClient({ discoveryApi: discovery }); + this.parallelismLimit = parallelismLimit; + } + + async execute() { + const limit = pLimit(this.parallelismLimit); + const techDocsBaseUrl = await this.discovery.getBaseUrl('techdocs'); + const entities = await this.catalogClient.getEntities({ + fields: [ + 'kind', + 'namespace', + 'metadata.annotations', + 'metadata.name', + 'metadata.namespace', + 'spec.type', + 'spec.lifecycle', + 'relations', + ], + }); + const docPromises = entities.items + .filter(it => it.metadata?.annotations?.['backstage.io/techdocs-ref']) + .map((entity: Entity) => + limit( + async (): Promise => { + const entityInfo = { + kind: entity.kind, + namespace: entity.metadata.namespace || 'default', + name: entity.metadata.name, + }; + + try { + const searchIndexResponse = await fetch( + DefaultTechDocsCollator.constructDocsIndexUrl( + techDocsBaseUrl, + entityInfo, + ), + ); + const searchIndex = await searchIndexResponse.json(); + + return searchIndex.docs.map((doc: MkSearchIndexDoc) => ({ + title: unescape(doc.title), + text: unescape(doc.text || ''), + location: this.applyArgsToFormat(this.locationTemplate, { + ...entityInfo, + path: doc.location, + }), + ...entityInfo, + componentType: entity.spec?.type?.toString() || 'other', + lifecycle: (entity.spec?.lifecycle as string) || '', + owner: + entity.relations?.find(r => r.type === RELATION_OWNED_BY) + ?.target?.name || '', + })); + } catch (e) { + this.logger.warn( + `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`, + e, + ); + return []; + } + }, + ), + ); + return (await Promise.all(docPromises)).flat(); + } + + protected applyArgsToFormat( + format: string, + args: Record, + ): string { + let formatted = format; + for (const [key, value] of Object.entries(args)) { + formatted = formatted.replace(`:${key}`, value); + } + return formatted; + } + + private static constructDocsIndexUrl( + techDocsBaseUrl: string, + entityInfo: { kind: string; namespace: string; name: string }, + ) { + return `${techDocsBaseUrl}/static/docs/${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}/search/search_index.json`; + } +} diff --git a/plugins/techdocs-backend/src/search/index.ts b/plugins/techdocs-backend/src/search/index.ts new file mode 100644 index 0000000000..74f348d769 --- /dev/null +++ b/plugins/techdocs-backend/src/search/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ +export { DefaultTechDocsCollator } from './DefaultTechDocsCollator'; +export type { TechDocsDocument } from './DefaultTechDocsCollator'; diff --git a/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts b/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts new file mode 100644 index 0000000000..ac60f3da3a --- /dev/null +++ b/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts @@ -0,0 +1,196 @@ +/* + * 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. + */ + +import { + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { + GeneratorBuilder, + PreparerBuilder, + PublisherBase, +} from '@backstage/techdocs-common'; +import { DocsBuilder, shouldCheckForUpdate } from '../DocsBuilder'; +import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer'; + +jest.mock('../DocsBuilder'); + +const MockedDocsBuilder = DocsBuilder as jest.MockedClass; + +describe('DocsSynchronizer', () => { + const preparers: jest.Mocked = { + register: jest.fn(), + get: jest.fn(), + }; + const generators: jest.Mocked = { + register: jest.fn(), + get: jest.fn(), + }; + const publisher: jest.Mocked = { + docsRouter: jest.fn(), + fetchTechDocsMetadata: jest.fn(), + getReadiness: jest.fn(), + hasDocsBeenGenerated: jest.fn(), + publish: jest.fn(), + }; + const discovery: jest.Mocked = { + getBaseUrl: jest.fn(), + getExternalBaseUrl: jest.fn(), + }; + + let docsSynchronizer: DocsSynchronizer; + const mockResponseHandler: jest.Mocked = { + log: jest.fn(), + finish: jest.fn(), + error: jest.fn(), + }; + + beforeEach(async () => { + publisher.docsRouter.mockReturnValue(() => {}); + discovery.getBaseUrl.mockImplementation(async type => { + return `http://backstage.local/api/${type}`; + }); + + docsSynchronizer = new DocsSynchronizer({ + publisher, + config: new ConfigReader({}), + logger: getVoidLogger(), + scmIntegrations: ScmIntegrations.fromConfig(new ConfigReader({})), + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('doSync', () => { + it('should execute an update', async () => { + (shouldCheckForUpdate as jest.Mock).mockReturnValue(true); + + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + uid: '0', + name: 'test', + namespace: 'default', + }, + }; + + MockedDocsBuilder.prototype.build.mockImplementation(async () => { + // extract the logStream from the constructor call + const logStream = MockedDocsBuilder.mock.calls[0][0].logStream; + + logStream?.write('Some log'); + logStream?.write('Another log'); + + const logger = MockedDocsBuilder.mock.calls[0][0].logger; + + logger.info('Some more log'); + + return true; + }); + + publisher.hasDocsBeenGenerated.mockResolvedValue(true); + + await docsSynchronizer.doSync({ + responseHandler: mockResponseHandler, + entity, + preparers, + generators, + }); + + expect(mockResponseHandler.log).toBeCalledTimes(3); + expect(mockResponseHandler.log).toBeCalledWith('Some log'); + expect(mockResponseHandler.log).toBeCalledWith('Another log'); + expect(mockResponseHandler.log).toBeCalledWith( + expect.stringMatching(/info.*Some more log/), + ); + + expect(mockResponseHandler.finish).toBeCalledWith({ updated: true }); + + expect(mockResponseHandler.error).toBeCalledTimes(0); + + expect(shouldCheckForUpdate).toBeCalledTimes(1); + expect(DocsBuilder.prototype.build).toBeCalledTimes(1); + }); + + it('should not check for an update too often', async () => { + (shouldCheckForUpdate as jest.Mock).mockReturnValue(false); + + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + uid: '0', + name: 'test', + namespace: 'default', + }, + }; + + await docsSynchronizer.doSync({ + responseHandler: mockResponseHandler, + entity, + preparers, + generators, + }); + + expect(mockResponseHandler.finish).toBeCalledWith({ updated: false }); + + expect(mockResponseHandler.log).toBeCalledTimes(0); + expect(mockResponseHandler.error).toBeCalledTimes(0); + + expect(shouldCheckForUpdate).toBeCalledTimes(1); + expect(DocsBuilder.prototype.build).toBeCalledTimes(0); + }); + + it('should forward build errors', async () => { + (shouldCheckForUpdate as jest.Mock).mockReturnValue(true); + + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + uid: '0', + name: 'test', + namespace: 'default', + }, + }; + + const error = new Error('Some random error'); + MockedDocsBuilder.prototype.build.mockRejectedValue(error); + + await docsSynchronizer.doSync({ + responseHandler: mockResponseHandler, + entity, + preparers, + generators, + }); + + expect(mockResponseHandler.log).toBeCalledTimes(1); + expect(mockResponseHandler.log).toBeCalledWith( + expect.stringMatching( + /error.*: Failed to build the docs page: Some random error/, + ), + ); + expect(mockResponseHandler.finish).toBeCalledTimes(0); + expect(mockResponseHandler.error).toBeCalledTimes(1); + expect(mockResponseHandler.error).toBeCalledWith(error); + }); + }); +}); diff --git a/plugins/techdocs-backend/src/service/DocsSynchronizer.ts b/plugins/techdocs-backend/src/service/DocsSynchronizer.ts new file mode 100644 index 0000000000..463b895564 --- /dev/null +++ b/plugins/techdocs-backend/src/service/DocsSynchronizer.ts @@ -0,0 +1,147 @@ +/* + * 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. + */ + +import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { NotFoundError } from '@backstage/errors'; +import { ScmIntegrationRegistry } from '@backstage/integration'; +import { + GeneratorBuilder, + PreparerBuilder, + PublisherBase, +} from '@backstage/techdocs-common'; +import { PassThrough } from 'stream'; +import * as winston from 'winston'; +import { DocsBuilder, shouldCheckForUpdate } from '../DocsBuilder'; + +export type DocsSynchronizerSyncOpts = { + log: (message: string) => void; + error: (e: Error) => void; + finish: (result: { updated: boolean }) => void; +}; + +export class DocsSynchronizer { + private readonly publisher: PublisherBase; + private readonly logger: winston.Logger; + private readonly config: Config; + private readonly scmIntegrations: ScmIntegrationRegistry; + + constructor({ + publisher, + logger, + config, + scmIntegrations, + }: { + publisher: PublisherBase; + logger: winston.Logger; + config: Config; + scmIntegrations: ScmIntegrationRegistry; + }) { + this.config = config; + this.logger = logger; + this.publisher = publisher; + this.scmIntegrations = scmIntegrations; + } + + async doSync({ + responseHandler: { log, error, finish }, + entity, + preparers, + generators, + }: { + responseHandler: DocsSynchronizerSyncOpts; + entity: Entity; + preparers: PreparerBuilder; + generators: GeneratorBuilder; + }) { + // create a new logger to log data to the caller + const taskLogger = winston.createLogger({ + level: process.env.LOG_LEVEL || 'info', + format: winston.format.combine( + winston.format.colorize(), + winston.format.timestamp(), + winston.format.simple(), + ), + defaultMeta: {}, + }); + + // create an in-memory stream to forward logs to the event-stream + const logStream = new PassThrough(); + logStream.on('data', async data => { + log(data.toString().trim()); + }); + + taskLogger.add(new winston.transports.Stream({ stream: logStream })); + + // check if the last update check was too recent + if (!shouldCheckForUpdate(entity.metadata.uid!)) { + finish({ updated: false }); + return; + } + + let foundDocs = false; + + try { + const docsBuilder = new DocsBuilder({ + preparers, + generators, + publisher: this.publisher, + logger: taskLogger, + entity, + config: this.config, + scmIntegrations: this.scmIntegrations, + logStream, + }); + + const updated = await docsBuilder.build(); + + if (!updated) { + finish({ updated: false }); + return; + } + } catch (e) { + const msg = `Failed to build the docs page: ${e.message}`; + taskLogger.error(msg); + this.logger.error(msg, e); + error(e); + return; + } + + // With a maximum of ~5 seconds wait, check if the files got published and if docs will be fetched + // on the user's page. If not, respond with a message asking them to check back later. + // The delay here is to make sure GCS/AWS/etc. registers newly uploaded files which is usually <1 second + for (let attempt = 0; attempt < 5; attempt++) { + if (await this.publisher.hasDocsBeenGenerated(entity)) { + foundDocs = true; + break; + } + await new Promise(r => setTimeout(r, 1000)); + } + if (!foundDocs) { + this.logger.error( + 'Published files are taking longer to show up in storage. Something went wrong.', + ); + error( + new NotFoundError( + 'Sorry! It took too long for the generated docs to show up in storage. Check back later.', + ), + ); + return; + } + + finish({ updated: true }); + } +} diff --git a/plugins/techdocs-backend/src/service/router.test.ts b/plugins/techdocs-backend/src/service/router.test.ts new file mode 100644 index 0000000000..84ab0c6d88 --- /dev/null +++ b/plugins/techdocs-backend/src/service/router.test.ts @@ -0,0 +1,469 @@ +/* + * 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. + */ + +import { + errorHandler, + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { CatalogClient } from '@backstage/catalog-client'; +import { ConfigReader } from '@backstage/config'; +import { NotModifiedError } from '@backstage/errors'; +import { + GeneratorBuilder, + PreparerBuilder, + PublisherBase, +} from '@backstage/techdocs-common'; +import express, { Response } from 'express'; +import request from 'supertest'; +import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer'; +import { createEventStream, createHttpResponse, createRouter } from './router'; + +jest.mock('@backstage/catalog-client'); +jest.mock('@backstage/config'); +jest.mock('./DocsSynchronizer'); + +const MockedConfigReader = ConfigReader as jest.MockedClass< + typeof ConfigReader +>; +const MockCatalogClient = CatalogClient as jest.MockedClass< + typeof CatalogClient +>; +const MockDocsSynchronizer = DocsSynchronizer as jest.MockedClass< + typeof DocsSynchronizer +>; + +describe('createRouter', () => { + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + uid: '0', + name: 'test', + }, + }; + const entityWithoutMetadata = { + ...entity, + metadata: { + ...entity.metadata, + uid: undefined, + }, + }; + + const preparers: jest.Mocked = { + register: jest.fn(), + get: jest.fn(), + }; + const generators: jest.Mocked = { + register: jest.fn(), + get: jest.fn(), + }; + const publisher: jest.Mocked = { + docsRouter: jest.fn(), + fetchTechDocsMetadata: jest.fn(), + getReadiness: jest.fn(), + hasDocsBeenGenerated: jest.fn(), + publish: jest.fn(), + }; + const discovery: jest.Mocked = { + getBaseUrl: jest.fn(), + getExternalBaseUrl: jest.fn(), + }; + + let app: express.Express; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + beforeEach(async () => { + publisher.docsRouter.mockReturnValue(() => {}); + discovery.getBaseUrl.mockImplementation(async type => { + return `http://backstage.local/api/${type}`; + }); + + const outOfTheBoxRouter = await createRouter({ + preparers, + generators, + publisher, + config: new ConfigReader({}), + logger: getVoidLogger(), + discovery, + }); + const recommendedRouter = await createRouter({ + publisher, + config: new ConfigReader({}), + logger: getVoidLogger(), + discovery, + }); + + app = express(); + app.use(outOfTheBoxRouter); + app.use('/recommended', recommendedRouter); + app.use(errorHandler()); + }); + + describe('GET /sync/:namespace/:kind/:name', () => { + describe('accept application/json', () => { + it('should return not found if entity is not found', async () => { + MockCatalogClient.prototype.getEntityByName.mockResolvedValue( + undefined, + ); + + const response = await request(app) + .get('/sync/default/Component/test') + .send(); + + expect(response.status).toBe(404); + }); + + it('should return not found if entity has no uid', async () => { + MockCatalogClient.prototype.getEntityByName.mockResolvedValue( + entityWithoutMetadata, + ); + + const response = await request(app) + .get('/sync/default/Component/test') + .send(); + + expect(response.status).toBe(404); + }); + + it('should not check for an update without local builder', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('external'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + + const response = await request(app) + .get('/sync/default/Component/test') + .send(); + + expect(response.status).toBe(304); + }); + + it('should error if missing builder', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('local'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + + const response = await request(app) + .get('/recommended/sync/default/Component/test') + .send(); + + expect(response.status).toBe(500); + expect(response.text).toMatch( + /Invalid configuration\. 'techdocs\.builder' was set to 'local' but no 'preparer' was provided to the router initialization/, + ); + + expect(MockDocsSynchronizer.prototype.doSync).toBeCalledTimes(0); + }); + + it('should execute synchronization', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('local'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + MockDocsSynchronizer.prototype.doSync.mockImplementation( + async ({ responseHandler }) => + responseHandler.finish({ updated: true }), + ); + + await request(app).get('/sync/default/Component/test').send(); + + expect(MockDocsSynchronizer.prototype.doSync).toBeCalledTimes(1); + expect(MockDocsSynchronizer.prototype.doSync).toBeCalledWith({ + responseHandler: { + log: expect.any(Function), + error: expect.any(Function), + finish: expect.any(Function), + }, + entity, + generators, + preparers, + }); + }); + + it('should return on updated', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('local'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + MockDocsSynchronizer.prototype.doSync.mockImplementation( + async ({ responseHandler }) => { + const { log, finish } = responseHandler; + + log('Some log'); + + finish({ updated: true }); + }, + ); + + const response = await request(app) + .get('/sync/default/Component/test') + .send(); + + expect(response.status).toBe(201); + expect(response.get('content-type')).toMatch(/application\/json/); + expect(response.text).toEqual( + '{"message":"Docs updated or did not need updating"}', + ); + }); + }); + + describe('accept text/event-stream', () => { + it('should return not found if entity is not found', async () => { + MockCatalogClient.prototype.getEntityByName.mockResolvedValue( + undefined, + ); + + const response = await request(app) + .get('/sync/default/Component/test') + .set('accept', 'text/event-stream') + .send(); + + expect(response.status).toBe(404); + }); + + it('should return not found if entity has no uid', async () => { + MockCatalogClient.prototype.getEntityByName.mockResolvedValue( + entityWithoutMetadata, + ); + + const response = await request(app) + .get('/sync/default/Component/test') + .set('accept', 'text/event-stream') + .send(); + + expect(response.status).toBe(404); + }); + + it('should not check for an update without local builder', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('external'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + + const response = await request(app) + .get('/sync/default/Component/test') + .set('accept', 'text/event-stream') + .send(); + + expect(response.status).toBe(200); + expect(response.get('content-type')).toBe('text/event-stream'); + expect(response.text).toEqual( + `event: finish +data: {"updated":false} + +`, + ); + }); + + it('should error if missing builder', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('local'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + + const response = await request(app) + .get('/recommended/sync/default/Component/test') + .set('accept', 'text/event-stream') + .send(); + + expect(response.status).toBe(200); + expect(response.get('content-type')).toBe('text/event-stream'); + expect(response.text).toEqual( + `event: error +data: "Invalid configuration. 'techdocs.builder' was set to 'local' but no 'preparer' was provided to the router initialization." + +`, + ); + + expect(MockDocsSynchronizer.prototype.doSync).toBeCalledTimes(0); + }); + + it('should execute synchronization', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('local'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + MockDocsSynchronizer.prototype.doSync.mockImplementation( + async ({ responseHandler }) => + responseHandler.finish({ updated: true }), + ); + + await request(app) + .get('/sync/default/Component/test') + .set('accept', 'text/event-stream') + .send(); + + expect(MockDocsSynchronizer.prototype.doSync).toBeCalledTimes(1); + expect(MockDocsSynchronizer.prototype.doSync).toBeCalledWith({ + responseHandler: { + log: expect.any(Function), + error: expect.any(Function), + finish: expect.any(Function), + }, + entity, + generators, + preparers, + }); + }); + + it('should return an event-stream', async () => { + MockedConfigReader.prototype.getString.mockReturnValue('local'); + MockCatalogClient.prototype.getEntityByName.mockResolvedValue(entity); + MockDocsSynchronizer.prototype.doSync.mockImplementation( + async ({ responseHandler }) => { + const { log, finish } = responseHandler; + + log('Some log'); + log('Another log'); + + finish({ updated: true }); + }, + ); + + const response = await request(app) + .get('/sync/default/Component/test') + .set('accept', 'text/event-stream') + .send(); + + expect(response.status).toBe(200); + expect(response.get('content-type')).toBe('text/event-stream'); + expect(response.text).toEqual( + `event: log +data: "Some log" + +event: log +data: "Another log" + +event: finish +data: {"updated":true} + +`, + ); + }); + }); + }); +}); + +describe('createEventStream', () => { + const res: jest.Mocked = { + writeHead: jest.fn(), + write: jest.fn(), + end: jest.fn(), + } as any; + + let handlers: DocsSynchronizerSyncOpts; + + beforeEach(() => { + handlers = createEventStream(res); + }); + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should return correct event stream', async () => { + // called in beforeEach + + expect(res.writeHead).toBeCalledTimes(1); + expect(res.writeHead).toBeCalledWith(200, { + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Content-Type': 'text/event-stream', + }); + }); + + it('should flush after write if defined', async () => { + res.flush = jest.fn(); + + handlers.log('A Message'); + + expect(res.write).toBeCalledTimes(1); + expect(res.write).toBeCalledWith(`event: log +data: "A Message" + +`); + expect(res.flush).toBeCalledTimes(1); + }); + + it('should write log', async () => { + handlers.log('A Message'); + + expect(res.write).toBeCalledTimes(1); + expect(res.write).toBeCalledWith(`event: log +data: "A Message" + +`); + expect(res.end).toBeCalledTimes(0); + }); + + it('should write error and end the connection', async () => { + handlers.error(new Error('Some Error')); + + expect(res.write).toBeCalledTimes(1); + expect(res.write).toBeCalledWith(`event: error +data: "Some Error" + +`); + expect(res.end).toBeCalledTimes(1); + }); + + it('should finish and end the connection', async () => { + handlers.finish({ updated: true }); + + expect(res.write).toBeCalledTimes(1); + expect(res.write).toBeCalledWith(`event: finish +data: {"updated":true} + +`); + + expect(res.end).toBeCalledTimes(1); + }); +}); + +describe('createHttpResponse', () => { + const res: jest.Mocked = { + status: jest.fn(), + json: jest.fn(), + } as any; + + let handlers: DocsSynchronizerSyncOpts; + + beforeEach(() => { + res.status.mockImplementation(() => res); + handlers = createHttpResponse(res); + }); + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should return CREATED if updated', async () => { + handlers.finish({ updated: true }); + + expect(res.status).toBeCalledTimes(1); + expect(res.status).toBeCalledWith(201); + + expect(res.json).toBeCalledTimes(1); + expect(res.json).toBeCalledWith({ + message: 'Docs updated or did not need updating', + }); + }); + + it('should return NOT_MODIFIED if not updated', async () => { + expect(() => handlers.finish({ updated: false })).toThrowError( + NotModifiedError, + ); + }); + + it('should throw custom error', async () => { + expect(() => handlers.error(new Error('Some Error'))).toThrowError( + /Some Error/, + ); + }); + + it('should ignore logs', async () => { + expect(() => handlers.log('Some Message')).not.toThrow(); + }); +}); diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index 7563dfa2f9..e560a895b6 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { CatalogClient } from '@backstage/catalog-client'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { NotFoundError, NotModifiedError } from '@backstage/errors'; @@ -24,12 +25,12 @@ import { PublisherBase, } from '@backstage/techdocs-common'; import fetch from 'cross-fetch'; -import express from 'express'; +import express, { Response } from 'express'; import Router from 'express-promise-router'; import { Knex } from 'knex'; import { Logger } from 'winston'; -import { DocsBuilder } from '../DocsBuilder'; -import { shouldCheckForUpdate } from '../DocsBuilder/BuildMetadataStorage'; +import { ScmIntegrations } from '@backstage/integration'; +import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer'; /** * All of the required dependencies for running TechDocs in the "out-of-the-box" @@ -78,6 +79,14 @@ export async function createRouter( ): Promise { const router = Router(); const { publisher, config, logger, discovery } = options; + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + const scmIntegrations = ScmIntegrations.fromConfig(config); + const docsSynchronizer = new DocsSynchronizer({ + publisher, + logger, + config, + scmIntegrations, + }); router.get('/metadata/techdocs/:namespace/:kind/:name', async (req, res) => { const { kind, namespace, name } = req.params; @@ -120,7 +129,7 @@ export async function createRouter( ) ).json()) as Entity; - const locationMetadata = getLocationForEntity(entity); + const locationMetadata = getLocationForEntity(entity, scmIntegrations); res.json({ ...entity, locationMetadata }); } catch (err) { logger.info( @@ -136,114 +145,58 @@ export async function createRouter( }); // Check if docs are the latest version and trigger rebuilds if not - // Responds with immediate success if rebuild not needed + // Responds with an event-stream that closes after the build finished + // Responds with an immediate success if rebuild not needed // If a build is required, responds with a success when finished router.get('/sync/:namespace/:kind/:name', async (req, res) => { const { kind, namespace, name } = req.params; - const catalogUrl = await discovery.getBaseUrl('catalog'); - const triple = [kind, namespace, name].map(encodeURIComponent).join('/'); - const token = getBearerToken(req.headers.authorization); - const catalogRes = await fetch(`${catalogUrl}/entities/by-name/${triple}`, { - headers: token ? { Authorization: `Bearer ${token}` } : {}, - }); - if (!catalogRes.ok) { - const catalogResText = await catalogRes.text(); - res.status(catalogRes.status); - res.send(catalogResText); - return; - } - const entity: Entity = await catalogRes.json(); + const entity = await catalogClient.getEntityByName( + { kind, namespace, name }, + { token }, + ); - if (!entity.metadata.uid) { + if (!entity?.metadata?.uid) { throw new NotFoundError('Entity metadata UID missing'); } - if (!shouldCheckForUpdate(entity.metadata.uid)) { - res.status(200).json({ - message: `Last check for documentation update is recent, did not retry.`, - }); - return; + + let responseHandler: DocsSynchronizerSyncOpts; + if (req.header('accept') !== 'text/event-stream') { + console.warn( + "The call to /sync/:namespace/:kind/:name wasn't done by an EventSource. This behavior is deprecated and will be removed soon. Make sure to update the @backstage/plugin-techdocs package in the frontend to the latest version.", + ); + responseHandler = createHttpResponse(res); + } else { + responseHandler = createEventStream(res); } - let publisherType = ''; - try { - publisherType = config.getString('techdocs.publisher.type'); - } catch (err) { - throw new Error( - 'Unable to get techdocs.publisher.type in your app config. Set it to either ' + - "'local', 'googleGcs' or other support storage providers. Read more here " + - 'https://backstage.io/docs/features/techdocs/architecture', - ); - } // techdocs-backend will only try to build documentation for an entity if techdocs.builder is set to 'local' // If set to 'external', it will assume that an external process (e.g. CI/CD pipeline // of the repository) is responsible for building and publishing documentation to the storage provider if (config.getString('techdocs.builder') !== 'local') { - res.status(200).json({ - message: - '`techdocs.builder` app config is not set to `local`, so docs will not be generated locally and sync is not required.', + responseHandler.finish({ updated: false }); + return; + } + + // Set the synchronization and build process if "out-of-the-box" configuration is provided. + if (isOutOfTheBoxOption(options)) { + const { preparers, generators } = options; + + await docsSynchronizer.doSync({ + responseHandler, + entity, + preparers, + generators, }); return; } - // Set up a DocsBuilder if "out-of-the-box" configuration is provided. - if (isOutOfTheBoxOption(options)) { - const { preparers, generators } = options; - const docsBuilder = new DocsBuilder({ - preparers, - generators, - publisher, - logger, - entity, - config, - }); - let foundDocs = false; - switch (publisherType) { - case 'local': - case 'awsS3': - case 'azureBlobStorage': - case 'openStackSwift': - case 'googleGcs': { - // This block should be valid for all storage implementations. So no need to duplicate in future, - // add the publisher type in the list here. - const updated = await docsBuilder.build(); - - if (!updated) { - throw new NotModifiedError(); - } - - // With a maximum of ~5 seconds wait, check if the files got published and if docs will be fetched - // on the user's page. If not, respond with a message asking them to check back later. - // The delay here is to make sure GCS/AWS/etc. registers newly uploaded files which is usually <1 second - for (let attempt = 0; attempt < 5; attempt++) { - if (await publisher.hasDocsBeenGenerated(entity)) { - foundDocs = true; - break; - } - await new Promise(r => setTimeout(r, 1000)); - } - if (!foundDocs) { - logger.error( - 'Published files are taking longer to show up in storage. Something went wrong.', - ); - throw new NotFoundError( - 'Sorry! It took too long for the generated docs to show up in storage. Check back later.', - ); - } - - res - .status(201) - .json({ message: 'Docs updated or did not need updating' }); - break; - } - - default: - throw new NotFoundError( - `Publisher type ${publisherType} is not supported by techdocs-backend docs builder.`, - ); - } - } + responseHandler.error( + new Error( + "Invalid configuration. 'techdocs.builder' was set to 'local' but no 'preparer' was provided to the router initialization.", + ), + ); }); // Route middleware which serves files from the storage set in the publisher. @@ -255,3 +208,79 @@ export async function createRouter( function getBearerToken(header?: string): string | undefined { return header?.match(/(?:Bearer)\s+(\S+)/i)?.[1]; } + +/** + * Create an event-stream response that emits the events 'log', 'error', and 'finish'. + * + * @param res the response to write the event-stream to + * @returns A tuple of callbacks to emit messages. A call to 'error' or 'finish' + * will close the event-stream. + */ +export function createEventStream( + res: Response, +): DocsSynchronizerSyncOpts { + // Mandatory headers and http status to keep connection open + res.writeHead(200, { + Connection: 'keep-alive', + 'Cache-Control': 'no-cache', + 'Content-Type': 'text/event-stream', + }); + + // client closes connection + res.socket?.on('close', () => { + res.end(); + }); + + // write the event to the stream + const send = (type: 'error' | 'finish' | 'log', data: any) => { + res.write(`event: ${type}\ndata: ${JSON.stringify(data)}\n\n`); + + // res.flush() is only available with the compression middleware + if (res.flush) { + res.flush(); + } + }; + + return { + log: data => { + send('log', data); + }, + + error: e => { + send('error', e.message); + res.end(); + }, + + finish: result => { + send('finish', result); + res.end(); + }, + }; +} + +/** + * Create a HTTP response. This is used for the legacy non-event-stream implementation of the sync endpoint. + * + * @param res the response to write the event-stream to + * @returns A tuple of callbacks to emit messages. A call to 'error' or 'finish' + * will close the event-stream. + */ +export function createHttpResponse( + res: Response, +): DocsSynchronizerSyncOpts { + return { + log: () => {}, + error: e => { + throw e; + }, + finish: ({ updated }) => { + if (!updated) { + throw new NotModifiedError(); + } + + res + .status(201) + .json({ message: 'Docs updated or did not need updating' }); + }, + }; +} diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index d0419ffd58..89c0ee04fd 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-techdocs +## 0.10.1 + +### Patch Changes + +- 9266b80ab: Add search list item to display tech docs search results +- 03bf17e9b: Improve the responsiveness of the EntityPage UI. With this the Header component should scale with the screen size & wrapping should not cause overflowing/blocking of links. Additionally enforce the Pages using the Grid Layout to use it across all screen sizes & to wrap as intended. + + To benefit from the improved responsive layout, the `EntityPage` in existing Backstage applications should be updated to set the `xs` column size on each grid item in the page, as this does not default. For example: + + ```diff + - + + + ``` + +- 378cc6a54: Only update the `path` when the content is updated. + If content and path are updated independently, the frontend rendering is triggered twice on each navigation: Once for the `path` change (with the old content) and once for the new content. + This might result in a flickering rendering that is caused by the async frontend preprocessing, and the fact that replacing the shadow dom content is expensive. +- 214e7c52d: Refactor the techdocs transformers to return `Promise`s and await all transformations. +- e35b13afa: Handle error responses in `getTechDocsMetadata` and `getEntityMetadata` such that `` doesn't throw errors. +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/plugin-catalog-react@0.3.1 + +## 0.10.0 + +### Minor Changes + +- 94a54dd47: Added a `migrateDocsCase()` method to TechDocs publishers, along with + implementations for AWS, Azure, and GCS. + + This change is in support of a future update to TechDocs that will allow for + case-insensitive entity triplet URL access to documentation pages which will + require a migration of existing documentation objects in external storage + solutions. + + See [#4367](https://github.com/backstage/backstage/issues/4367) for details. + +### Patch Changes + +- 537c37b0f: Fix displaying owned documents list by fetching associated entity relations +- 136a91974: Show a "Refresh" button to if the content is stale. + This removes the need to do a full page-reload to display more recent TechDocs content. +- f1200f44c: Rewrite the `/sync/:namespace/:kind/:name` endpoint to support an event-stream as response. + This change allows the sync process to take longer than a normal HTTP timeout. + The stream also emits log events, so the caller can follow the build process in the frontend. +- 3af126cdd: Provide a Drawer component to follow a running build. + This can be used to debug the rendering and get build logs in case an error occurs. +- 2a4a3b32d: Techdocs: fix sidebars not adjusting position automatically +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.9.9 ### Patch Changes diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index 943831d91c..8364263f42 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -13,9 +13,11 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { IdentityApi } from '@backstage/core-plugin-api'; -import { Location as Location_2 } from '@backstage/catalog-model'; +import { LocationSpec } from '@backstage/catalog-model'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "DocsCardGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DocsCardGrid: ({ entities, @@ -23,6 +25,19 @@ export const DocsCardGrid: ({ entities: Entity[] | undefined; }) => JSX.Element | null; +// Warning: (ae-missing-release-tag) "DocsResultListItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const DocsResultListItem: ({ + result, + lineClamp, +}: { + result: any; + lineClamp?: number | undefined; +}) => JSX.Element; + +// Warning: (ae-missing-release-tag) "DocsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DocsTable: ({ entities, @@ -32,39 +47,66 @@ export const DocsTable: ({ title?: string | undefined; }) => JSX.Element | null; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "EmbeddedDocsRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EmbeddedDocsRouter: (_props: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "EntityTechdocsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityTechdocsContent: (_props: { entity?: Entity | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "PanelType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type PanelType = 'DocsCardGrid' | 'DocsTable'; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Reader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Reader: ({ entityId, onReady }: Props_2) => JSX.Element; +// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: () => JSX.Element; +// Warning: (ae-missing-release-tag) "SyncResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) -export type SyncResult = 'cached' | 'updated' | 'timeout'; +export type SyncResult = 'cached' | 'updated'; +// Warning: (ae-missing-release-tag) "TechDocsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TechDocsApi { // (undocumented) getApiOrigin(): Promise; + // Warning: (ae-forgotten-export) The symbol "TechDocsEntityMetadata" needs to be exported by the entry point index.d.ts + // // (undocumented) getEntityMetadata(entityId: EntityName): Promise; + // Warning: (ae-forgotten-export) The symbol "TechDocsMetadata" needs to be exported by the entry point index.d.ts + // // (undocumented) getTechDocsMetadata(entityId: EntityName): Promise; } +// Warning: (ae-missing-release-tag) "techdocsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const techdocsApiRef: ApiRef; +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-undefined-tag) The TSDoc tag "@property" is not defined in this configuration +// Warning: (ae-missing-release-tag) "TechDocsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class TechDocsClient implements TechDocsApi { constructor({ @@ -82,12 +124,18 @@ export class TechDocsClient implements TechDocsApi { discoveryApi: DiscoveryApi; // (undocumented) getApiOrigin(): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' getEntityMetadata(entityId: EntityName): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' getTechDocsMetadata(entityId: EntityName): Promise; // (undocumented) identityApi: IdentityApi; } +// Warning: (ae-missing-release-tag) "TechDocsCustomHome" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TechDocsCustomHome: ({ tabsConfig, @@ -95,9 +143,13 @@ export const TechDocsCustomHome: ({ tabsConfig: TabsConfig; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "TechdocsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TechdocsPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "techdocsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const techdocsPlugin: BackstagePlugin< { @@ -109,9 +161,13 @@ const techdocsPlugin: BackstagePlugin< export { techdocsPlugin as plugin }; export { techdocsPlugin }; +// Warning: (ae-missing-release-tag) "TechDocsReaderPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const TechDocsReaderPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "TechDocsStorageApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TechDocsStorageApi { // (undocumented) @@ -129,12 +185,22 @@ export interface TechDocsStorageApi { // (undocumented) getStorageUrl(): Promise; // (undocumented) - syncEntityDocs(entityId: EntityName): Promise; + syncEntityDocs( + entityId: EntityName, + logHandler?: (line: string) => void, + ): Promise; } +// Warning: (ae-missing-release-tag) "techdocsStorageApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const techdocsStorageApiRef: ApiRef; +// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag +// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" +// Warning: (tsdoc-undefined-tag) The TSDoc tag "@property" is not defined in this configuration +// Warning: (ae-missing-release-tag) "TechDocsStorageClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public export class TechDocsStorageClient implements TechDocsStorageApi { constructor({ @@ -160,13 +226,36 @@ export class TechDocsStorageClient implements TechDocsStorageApi { ): Promise; // (undocumented) getBuilder(): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" getEntityDocs(entityId: EntityName, path: string): Promise; // (undocumented) getStorageUrl(): Promise; // (undocumented) identityApi: IdentityApi; - syncEntityDocs(entityId: EntityName): Promise; + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen + // Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + // Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag + // Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@" + syncEntityDocs( + entityId: EntityName, + logHandler?: (line: string) => void, + ): Promise; } +// Warnings were encountered during analysis: +// +// src/plugin.d.ts:18:5 - (ae-forgotten-export) The symbol "TabsConfig" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/techdocs/dev/index.tsx b/plugins/techdocs/dev/index.tsx index cdc81caa5a..659a4e2caf 100644 --- a/plugins/techdocs/dev/index.tsx +++ b/plugins/techdocs/dev/index.tsx @@ -17,6 +17,7 @@ import { createDevApp } from '@backstage/dev-utils'; import { NotFoundError } from '@backstage/errors'; import React from 'react'; +import { EntityName } from '@backstage/catalog-model'; import { Reader, SyncResult, @@ -81,9 +82,18 @@ function createPage({ }); } - async syncEntityDocs() { + async syncEntityDocs(_: EntityName, logHandler?: (line: string) => void) { if (syncDocsDelay) { - await new Promise(resolve => setTimeout(resolve, syncDocsDelay)); + for (let i = 0; i < 10; i++) { + setTimeout( + () => logHandler?.call(this, `Log line ${i}`), + ((i + 1) * syncDocsDelay) / 10, + ); + } + + await new Promise(resolve => { + setTimeout(resolve, syncDocsDelay); + }); } return syncDocs(); @@ -138,6 +148,11 @@ createDevApp() {createPage({ + entityDocs: ({ called, content }) => { + return called === 0 + ? content + : content.replace(/World/, 'New World'); + }, syncDocs: () => 'updated', syncDocsDelay: 2000, })} @@ -195,13 +210,6 @@ createDevApp() syncDocsDelay: 2000, })} - - - {createPage({ - syncDocs: () => 'timeout', - syncDocsDelay: 2000, - })} - ), diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 3b348873aa..2621be9cee 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.9.9", + "version": "0.10.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,33 +33,38 @@ "dependencies": { "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.1", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "@material-ui/styles": "^4.10.0", + "dompurify": "^2.2.9", + "eventsource": "^1.1.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-lazylog": "^4.5.2", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4", - "sanitize-html": "^2.3.2" + "react-text-truncate": "^0.16.0" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", "@testing-library/user-event": "^13.1.8", + "@types/dompurify": "^2.2.2", + "@types/eventsource": "^1.1.5", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "@types/react": "^16.9", diff --git a/plugins/techdocs/src/api.ts b/plugins/techdocs/src/api.ts index 739c5fc133..a23d9fc7f0 100644 --- a/plugins/techdocs/src/api.ts +++ b/plugins/techdocs/src/api.ts @@ -28,14 +28,17 @@ export const techdocsApiRef = createApiRef({ description: 'Used to make requests towards techdocs API', }); -export type SyncResult = 'cached' | 'updated' | 'timeout'; +export type SyncResult = 'cached' | 'updated'; export interface TechDocsStorageApi { getApiOrigin(): Promise; getStorageUrl(): Promise; getBuilder(): Promise; getEntityDocs(entityId: EntityName, path: string): Promise; - syncEntityDocs(entityId: EntityName): Promise; + syncEntityDocs( + entityId: EntityName, + logHandler?: (line: string) => void, + ): Promise; getBaseUrl( oldBaseUrl: string, entityId: EntityName, diff --git a/plugins/techdocs/src/client.test.ts b/plugins/techdocs/src/client.test.ts index 329d3cbcb6..982c9496b2 100644 --- a/plugins/techdocs/src/client.test.ts +++ b/plugins/techdocs/src/client.test.ts @@ -13,9 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Config } from '@backstage/config'; -import { TechDocsStorageClient } from './client'; import { UrlPatternDiscovery } from '@backstage/core-app-api'; +import { IdentityApi } from '@backstage/core-plugin-api'; +import { NotFoundError } from '@backstage/errors'; +import EventSource from 'eventsource'; +import { TechDocsStorageClient } from './client'; + +const MockedEventSource: jest.MockedClass< + typeof EventSource +> = EventSource as any; + +jest.mock('eventsource'); const mockEntity = { kind: 'Component', @@ -29,6 +39,16 @@ describe('TechDocsStorageClient', () => { getOptionalString: () => 'http://backstage:9191/api/techdocs', } as Partial; const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); + const identityApi: jest.Mocked = { + getIdToken: jest.fn(), + getProfile: jest.fn(), + getUserId: jest.fn(), + signOut: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); it('should return correct base url based on defined storage', async () => { // @ts-ignore Partial not assignable to Config. @@ -51,4 +71,183 @@ describe('TechDocsStorageClient', () => { `${mockBaseUrl}/static/docs/${mockEntity.namespace}/${mockEntity.kind}/${mockEntity.name}/test/`, ); }); + + describe('syncEntityDocs', () => { + it('should create eventsource without headers', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + MockedEventSource.prototype.addEventListener.mockImplementation( + (type, fn) => { + if (type === 'finish') { + fn({ data: '{"updated": false}' } as any); + } + }, + ); + + await storageApi.syncEntityDocs(mockEntity); + + expect( + MockedEventSource, + ).toBeCalledWith( + 'http://backstage:9191/api/techdocs/sync/default/Component/test-component', + { withCredentials: true, headers: {} }, + ); + }); + + it('should create eventsource with headers', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + MockedEventSource.prototype.addEventListener.mockImplementation( + (type, fn) => { + if (type === 'finish') { + fn({ data: '{"updated": false}' } as any); + } + }, + ); + + identityApi.getIdToken.mockResolvedValue('token'); + + await storageApi.syncEntityDocs(mockEntity); + + expect( + MockedEventSource, + ).toBeCalledWith( + 'http://backstage:9191/api/techdocs/sync/default/Component/test-component', + { withCredentials: true, headers: { Authorization: 'Bearer token' } }, + ); + }); + + it('should resolve to cached', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + MockedEventSource.prototype.addEventListener.mockImplementation( + (type, fn) => { + if (type === 'finish') { + fn({ data: '{"updated": false}' } as any); + } + }, + ); + + await expect(storageApi.syncEntityDocs(mockEntity)).resolves.toEqual( + 'cached', + ); + }); + + it('should resolve to updated', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + MockedEventSource.prototype.addEventListener.mockImplementation( + (type, fn) => { + if (type === 'finish') { + fn({ data: '{"updated": true}' } as any); + } + }, + ); + + await expect(storageApi.syncEntityDocs(mockEntity)).resolves.toEqual( + 'updated', + ); + }); + + it('should log values', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + MockedEventSource.prototype.addEventListener.mockImplementation( + (type, fn) => { + if (type === 'log') { + fn({ data: '"A log message"' } as any); + } + + if (type === 'finish') { + fn({ data: '{"updated": false}' } as any); + } + }, + ); + + const logHandler = jest.fn(); + await expect( + storageApi.syncEntityDocs(mockEntity, logHandler), + ).resolves.toEqual('cached'); + + expect(logHandler).toBeCalledTimes(1); + expect(logHandler).toBeCalledWith('A log message'); + }); + + it('should throw NotFoundError', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + // we await later after we emitted the error + const promise = storageApi.syncEntityDocs(mockEntity).then(); + + // flush the event loop + await new Promise(setImmediate); + + const instance = MockedEventSource.mock + .instances[0] as jest.Mocked; + + instance.onerror({ + status: 404, + message: 'Some not found warning', + } as any); + + await expect(promise).rejects.toThrow(NotFoundError); + await expect(promise).rejects.toThrowError('Some not found warning'); + }); + + it('should throw generic errors', async () => { + const storageApi = new TechDocsStorageClient({ + // @ts-ignore Partial not assignable to Config. + configApi, + discoveryApi, + identityApi, + }); + + // we await later after we emitted the error + const promise = storageApi.syncEntityDocs(mockEntity).then(); + + // flush the event loop + await new Promise(setImmediate); + + const instance = MockedEventSource.mock + .instances[0] as jest.Mocked; + + instance.onerror({ + type: 'error', + data: 'Some other error', + } as any); + + await expect(promise).rejects.toThrow(Error); + await expect(promise).rejects.toThrowError('Some other error'); + }); + }); }); diff --git a/plugins/techdocs/src/client.ts b/plugins/techdocs/src/client.ts index 19fc565a2f..f867ce4011 100644 --- a/plugins/techdocs/src/client.ts +++ b/plugins/techdocs/src/client.ts @@ -16,10 +16,11 @@ import { EntityName } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; -import { NotFoundError } from '@backstage/errors'; +import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; +import { NotFoundError, ResponseError } from '@backstage/errors'; +import EventSource from 'eventsource'; import { SyncResult, TechDocsApi, TechDocsStorageApi } from './api'; import { TechDocsEntityMetadata, TechDocsMetadata } from './types'; -import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; /** * API to talk to techdocs-backend. @@ -71,9 +72,12 @@ export class TechDocsClient implements TechDocsApi { const request = await fetch(`${requestUrl}`, { headers: token ? { Authorization: `Bearer ${token}` } : {}, }); - const res = await request.json(); - return res; + if (!request.ok) { + throw await ResponseError.fromResponse(request); + } + + return await request.json(); } /** @@ -96,9 +100,12 @@ export class TechDocsClient implements TechDocsApi { const request = await fetch(`${requestUrl}`, { headers: token ? { Authorization: `Bearer ${token}` } : {}, }); - const res = await request.json(); - return res; + if (!request.ok) { + throw await ResponseError.fromResponse(request); + } + + return await request.json(); } } @@ -192,44 +199,59 @@ export class TechDocsStorageClient implements TechDocsStorageApi { * Check if docs are on the latest version and trigger rebuild if not * * @param {EntityName} entityId Object containing entity data like name, namespace, etc. + * @param {Function} logHandler Callback to receive log messages from the build process * @returns {SyncResult} Whether documents are currently synchronized to newest version * @throws {Error} Throws error on error from sync endpoint in Techdocs Backend */ - async syncEntityDocs(entityId: EntityName): Promise { + async syncEntityDocs( + entityId: EntityName, + logHandler: (line: string) => void = () => {}, + ): Promise { const { kind, namespace, name } = entityId; const apiOrigin = await this.getApiOrigin(); const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`; const token = await this.identityApi.getIdToken(); - let request; - let attempts: number = 0; - // retry if request times out, up to 5 times - // can happen due to docs taking too long to generate - while (!request || (request.status === 408 && attempts < 5)) { - attempts++; - request = await fetch(url, { + + return new Promise((resolve, reject) => { + const source = new EventSource(url, { + withCredentials: true, headers: token ? { Authorization: `Bearer ${token}` } : {}, }); - } - switch (request.status) { - case 404: - throw new NotFoundError((await request.json()).error); + source.addEventListener('log', (e: any) => { + if (e.data) { + logHandler(JSON.parse(e.data)); + } + }); - case 200: - case 304: - return 'cached'; + source.addEventListener('finish', (e: any) => { + let updated: boolean = false; - case 201: - return 'updated'; + if (e.data) { + ({ updated } = JSON.parse(e.data)); + } - // for timeout and misc errors, handle without error to allow viewing older docs - // if older docs not available, - // Reader will show 404 error coming from getEntityDocs - case 408: - default: - return 'timeout'; - } + resolve(updated ? 'updated' : 'cached'); + }); + + source.onerror = (e: any) => { + source.close(); + + switch (e.status) { + // the endpoint returned a 404 status + case 404: + reject(new NotFoundError(e.message)); + return; + + // also handles the event-stream close. the reject is ignored if the Promise was already + // resolved by a finish event. + default: + reject(new Error(e.data)); + return; + } + }; + }); } async getBaseUrl( diff --git a/plugins/techdocs/src/components/DocsResultListItem/DocsResultListItem.test.tsx b/plugins/techdocs/src/components/DocsResultListItem/DocsResultListItem.test.tsx new file mode 100644 index 0000000000..41de2fefe4 --- /dev/null +++ b/plugins/techdocs/src/components/DocsResultListItem/DocsResultListItem.test.tsx @@ -0,0 +1,50 @@ +/* + * 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. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { DocsResultListItem } from './DocsResultListItem'; + +// Using canvas to render text.. +jest.mock('react-text-truncate', () => { + return ({ text }: { text: string }) => {text}; +}); + +const validResult = { + location: 'https://backstage.io/docs', + title: 'Documentation', + text: + 'Backstage is an open-source developer portal that puts the developer experience first.', + kind: 'library', + namespace: '', + name: 'Backstage', + lifecycle: 'production', +}; + +describe('DocsResultListItem test', () => { + it('should render search doc passed in', async () => { + const { findByText } = render(); + + expect( + await findByText('Documentation | Backstage docs'), + ).toBeInTheDocument(); + expect( + await findByText( + 'Backstage is an open-source developer portal that puts the developer experience first.', + ), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/techdocs/src/components/DocsResultListItem/DocsResultListItem.tsx b/plugins/techdocs/src/components/DocsResultListItem/DocsResultListItem.tsx new file mode 100644 index 0000000000..52aa45a97e --- /dev/null +++ b/plugins/techdocs/src/components/DocsResultListItem/DocsResultListItem.tsx @@ -0,0 +1,60 @@ +/* + * 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. + */ + +import React from 'react'; +import { Divider, ListItem, ListItemText, makeStyles } from '@material-ui/core'; +import { Link } from '@backstage/core-components'; +import TextTruncate from 'react-text-truncate'; + +const useStyles = makeStyles({ + flexContainer: { + flexWrap: 'wrap', + }, + itemText: { + width: '100%', + marginBottom: '1rem', + }, +}); + +export const DocsResultListItem = ({ + result, + lineClamp = 5, +}: { + result: any; + lineClamp?: number; +}) => { + const classes = useStyles(); + return ( + + + + } + /> + + + + ); +}; diff --git a/plugins/techdocs/src/components/DocsResultListItem/index.ts b/plugins/techdocs/src/components/DocsResultListItem/index.ts new file mode 100644 index 0000000000..4e1e511ea1 --- /dev/null +++ b/plugins/techdocs/src/components/DocsResultListItem/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { DocsResultListItem } from './DocsResultListItem'; diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx index 90b968de6c..c5d05f1474 100644 --- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx +++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx @@ -125,7 +125,14 @@ export const TechDocsCustomHome = ({ const { value: entities, loading, error } = useAsync(async () => { const response = await catalogApi.getEntities({ - fields: ['apiVersion', 'kind', 'metadata', 'spec.owner', 'spec.type'], + fields: [ + 'apiVersion', + 'kind', + 'metadata', + 'relations', + 'spec.owner', + 'spec.type', + ], }); return response.items.filter((entity: Entity) => { return !!entity.metadata.annotations?.['backstage.io/techdocs-ref']; diff --git a/plugins/techdocs/src/index.ts b/plugins/techdocs/src/index.ts index d645d128f7..66502a4585 100644 --- a/plugins/techdocs/src/index.ts +++ b/plugins/techdocs/src/index.ts @@ -19,6 +19,7 @@ export { techdocsApiRef, techdocsStorageApiRef } from './api'; export type { TechDocsApi, TechDocsStorageApi } from './api'; export { TechDocsClient, TechDocsStorageClient } from './client'; export type { PanelType } from './home/components/TechDocsCustomHome'; +export * from './components/DocsResultListItem'; export { DocsCardGrid, DocsTable, diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 294f81378f..ce72da4bf6 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -15,9 +15,16 @@ */ import { EntityName } from '@backstage/catalog-model'; +import { Progress } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; -import { useTheme } from '@material-ui/core'; +import { + Button, + CircularProgress, + makeStyles, + useTheme, +} from '@material-ui/core'; import { Alert } from '@material-ui/lab'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; @@ -34,27 +41,38 @@ import { simplifyMkdocsFooter, transform as transformer, } from '../transformers'; +import { TechDocsBuildLogs } from './TechDocsBuildLogs'; import { TechDocsNotFound } from './TechDocsNotFound'; -import TechDocsProgressBar from './TechDocsProgressBar'; import { useReaderState } from './useReaderState'; -import { useApi } from '@backstage/core-plugin-api'; type Props = { entityId: EntityName; onReady?: () => void; }; +const useStyles = makeStyles(() => ({ + message: { + // `word-break: break-word` is deprecated, but gives legacy support to browsers not supporting `overflow-wrap` yet + // https://developer.mozilla.org/en-US/docs/Web/CSS/word-break + wordBreak: 'break-word', + overflowWrap: 'anywhere', + }, +})); + export const Reader = ({ entityId, onReady }: Props) => { const { kind, namespace, name } = entityId; - const { '*': path } = useParams(); const theme = useTheme(); + const classes = useStyles(); - const { state, content: rawPage, errorMessage } = useReaderState( - kind, - namespace, - name, + const { + state, path, - ); + contentReload, + content: rawPage, + contentErrorMessage, + syncErrorMessage, + buildLog, + } = useReaderState(kind, namespace, name, useParams()['*']); const techdocsStorageApi = useApi(techdocsStorageApiRef); const [sidebars, setSidebars] = useState(); @@ -82,40 +100,35 @@ export const Reader = ({ entityId, onReady }: Props) => { useEffect(() => { updateSidebarPosition(); - window.addEventListener('scroll', updateSidebarPosition); + window.addEventListener('scroll', updateSidebarPosition, true); window.addEventListener('resize', updateSidebarPosition); return () => { - window.removeEventListener('scroll', updateSidebarPosition); + window.removeEventListener('scroll', updateSidebarPosition, true); window.removeEventListener('resize', updateSidebarPosition); }; // an update to "state" might lead to an updated UI so we include it as a trigger }, [updateSidebarPosition, state]); - useEffect(() => { - if (!rawPage || !shadowDomRef.current) { - return; - } - if (onReady) { - onReady(); - } - // Pre-render - const transformedElement = transformer(rawPage, [ - sanitizeDOM(), - addBaseUrl({ - techdocsStorageApi, - entityId: { - kind, - name, - namespace, - }, - path, - }), - rewriteDocLinks(), - removeMkdocsHeader(), - simplifyMkdocsFooter(), - addGitFeedbackLink(scmIntegrationsApi), - injectCss({ - css: ` + // a function that performs transformations that are executed prior to adding it to the DOM + const preRender = useCallback( + (rawContent: string, contentPath: string) => + transformer(rawContent, [ + sanitizeDOM(), + addBaseUrl({ + techdocsStorageApi, + entityId: { + kind, + name, + namespace, + }, + path: contentPath, + }), + rewriteDocLinks(), + removeMkdocsHeader(), + simplifyMkdocsFooter(), + addGitFeedbackLink(scmIntegrationsApi), + injectCss({ + css: ` body { font-family: ${theme.typography.fontFamily}; --md-text-color: ${theme.palette.text.primary}; @@ -172,21 +185,21 @@ export const Reader = ({ entityId, onReady }: Props) => { } } `, - }), - injectCss({ - // Disable CSS animations on link colors as they lead to issues in dark - // mode. The dark mode color theme is applied later and theirfore there - // is always an animation from light to dark mode when navigation - // between pages. - css: ` + }), + injectCss({ + // Disable CSS animations on link colors as they lead to issues in dark + // mode. The dark mode color theme is applied later and theirfore there + // is always an animation from light to dark mode when navigation + // between pages. + css: ` .md-nav__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink { transition: none; } `, - }), - injectCss({ - // Properly style code blocks. - css: ` + }), + injectCss({ + // Properly style code blocks. + css: ` .md-typeset pre > code::-webkit-scrollbar-thumb { background-color: hsla(0, 0%, 0%, 0.32); } @@ -194,17 +207,17 @@ export const Reader = ({ entityId, onReady }: Props) => { background-color: hsla(0, 0%, 0%, 0.87); } `, - }), - injectCss({ - // Admonitions and others are using SVG masks to define icons. These - // masks are defined as CSS variables. - // As the MkDocs output is rendered in shadow DOM, the CSS variable - // definitions on the root selector are not applied. Instead, the have - // to be applied on :host. - // As there is no way to transform the served main*.css yet (for - // example in the backend), we have to copy from main*.css and modify - // them. - css: ` + }), + injectCss({ + // Admonitions and others are using SVG masks to define icons. These + // masks are defined as CSS variables. + // As the MkDocs output is rendered in shadow DOM, the CSS variable + // definitions on the root selector are not applied. Instead, the have + // to be applied on :host. + // As there is no way to transform the served main*.css yet (for + // example in the backend), we have to copy from main*.css and modify + // them. + css: ` :host { --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,'); --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,'); @@ -230,128 +243,195 @@ export const Reader = ({ entityId, onReady }: Props) => { --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,'); } `, - }), - ]); + }), + ]), + [ + kind, + name, + namespace, + scmIntegrationsApi, + techdocsStorageApi, + theme.palette.background.default, + theme.palette.background.paper, + theme.palette.primary.main, + theme.palette.text.primary, + theme.typography.fontFamily, + ], + ); - if (!transformedElement) { - return; // An unexpected error occurred + // a function that performs transformations that are executed after adding it to the DOM + const postRender = useCallback( + async (shadowRoot: ShadowRoot) => + transformer(shadowRoot.children[0], [ + dom => { + setTimeout(() => { + // Scoll to the desired anchor on initial navigation + if (window.location.hash) { + const hash = window.location.hash.slice(1); + shadowRoot?.getElementById(hash)?.scrollIntoView(); + } + }, 200); + return dom; + }, + addLinkClickListener({ + baseUrl: window.location.origin, + onClick: (_: MouseEvent, url: string) => { + const parsedUrl = new URL(url); + + if (parsedUrl.hash) { + navigate(`${parsedUrl.pathname}${parsedUrl.hash}`); + + // Scroll to hash if it's on the current page + shadowRoot + ?.getElementById(parsedUrl.hash.slice(1)) + ?.scrollIntoView(); + } else { + navigate(parsedUrl.pathname); + } + }, + }), + onCssReady({ + docStorageUrl: await techdocsStorageApi.getApiOrigin(), + onLoading: (dom: Element) => { + (dom as HTMLElement).style.setProperty('opacity', '0'); + }, + onLoaded: (dom: Element) => { + (dom as HTMLElement).style.removeProperty('opacity'); + // disable MkDocs drawer toggling ('for' attribute => checkbox mechanism) + (dom as HTMLElement) + .querySelector('.md-nav__title') + ?.removeAttribute('for'); + const sideDivs: HTMLElement[] = Array.from( + shadowRoot!.querySelectorAll('.md-sidebar'), + ); + setSidebars(sideDivs); + // set sidebar height so they don't initially render in wrong position + const docTopPosition = (dom as HTMLElement).getBoundingClientRect() + .top; + const mdTabs = dom.querySelector('.md-container > .md-tabs'); + sideDivs!.forEach(sidebar => { + sidebar.style.top = mdTabs + ? `${docTopPosition + mdTabs.getBoundingClientRect().height}px` + : `${docTopPosition}px`; + }); + }, + }), + ]), + [navigate, techdocsStorageApi], + ); + + useEffect(() => { + if (!rawPage || !shadowDomRef.current) { + // clear the shadow dom if no content is available + if (shadowDomRef.current?.shadowRoot) { + shadowDomRef.current.shadowRoot.innerHTML = ''; + } + return () => {}; + } + if (onReady) { + onReady(); } - const shadowDiv: HTMLElement = shadowDomRef.current!; - const shadowRoot = - shadowDiv.shadowRoot || shadowDiv.attachShadow({ mode: 'open' }); - Array.from(shadowRoot.children).forEach(child => - shadowRoot.removeChild(child), - ); - shadowRoot.appendChild(transformedElement); + // if false, there is already a newer execution of this effect + let shouldReplaceContent = true; - // Scroll to top after render - window.scroll({ top: 0 }); + // Pre-render + preRender(rawPage, path).then(async transformedElement => { + if (!transformedElement?.innerHTML) { + return; // An unexpected error occurred + } - // Post-render - transformer(shadowRoot.children[0], [ - dom => { - setTimeout(() => { - // Scoll to the desired anchor on initial navigation - if (window.location.hash) { - const hash = window.location.hash.slice(1); - shadowRoot?.getElementById(hash)?.scrollIntoView(); - } - }, 200); - return dom; - }, - addLinkClickListener({ - baseUrl: window.location.origin, - onClick: (_: MouseEvent, url: string) => { - const parsedUrl = new URL(url); + // don't manipulate the shadow dom if this isn't the latest effect execution + if (!shouldReplaceContent) { + return; + } - if (parsedUrl.hash) { - navigate(`${parsedUrl.pathname}${parsedUrl.hash}`); + const shadowDiv: HTMLElement = shadowDomRef.current!; + const shadowRoot = + shadowDiv.shadowRoot || shadowDiv.attachShadow({ mode: 'open' }); + Array.from(shadowRoot.children).forEach(child => + shadowRoot.removeChild(child), + ); + shadowRoot.appendChild(transformedElement); - // Scroll to hash if it's on the current page - shadowRoot - ?.getElementById(parsedUrl.hash.slice(1)) - ?.scrollIntoView(); - } else { - navigate(parsedUrl.pathname); - } - }, - }), - onCssReady({ - docStorageUrl: techdocsStorageApi.getApiOrigin(), - onLoading: (dom: Element) => { - (dom as HTMLElement).style.setProperty('opacity', '0'); - }, - onLoaded: (dom: Element) => { - (dom as HTMLElement).style.removeProperty('opacity'); - // disable MkDocs drawer toggling ('for' attribute => checkbox mechanism) - (dom as HTMLElement) - .querySelector('.md-nav__title') - ?.removeAttribute('for'); - const sideDivs: HTMLElement[] = Array.from( - shadowRoot!.querySelectorAll('.md-sidebar'), - ); - setSidebars(sideDivs); - // set sidebar height so they don't initially render in wrong position - const docTopPosition = (dom as HTMLElement).getBoundingClientRect() - .top; - const mdTabs = dom.querySelector('.md-container > .md-tabs'); - sideDivs!.forEach(sidebar => { - sidebar.style.top = mdTabs - ? `${docTopPosition + mdTabs.getBoundingClientRect().height}px` - : `${docTopPosition}px`; - }); - }, - }), - ]); - }, [ - path, - kind, - namespace, - name, - rawPage, - navigate, - onReady, - shadowDomRef, - techdocsStorageApi, - theme.typography.fontFamily, - theme.palette.text.primary, - theme.palette.primary.main, - theme.palette.background.paper, - theme.palette.background.default, - scmIntegrationsApi, - ]); + // Scroll to top after render + window.scroll({ top: 0 }); + + // Post-render + await postRender(shadowRoot); + }); + + // cancel this execution + return () => { + shouldReplaceContent = false; + }; + }, [onReady, path, postRender, preRender, rawPage]); return ( <> - {(state === 'CHECKING' || state === 'INITIAL_BUILD') && ( - + {state === 'CHECKING' && } + {state === 'INITIAL_BUILD' && ( + } + action={} + > + Documentation is accessed for the first time and is being prepared. + The subsequent loads are much faster. + )} {state === 'CONTENT_STALE_REFRESHING' && ( - + } + action={} + > A newer version of this documentation is being prepared and will be available shortly. )} {state === 'CONTENT_STALE_READY' && ( - + contentReload()}> + Refresh + + } + > A newer version of this documentation is now available, please refresh to view. )} - {state === 'CONTENT_STALE_TIMEOUT' && ( - - Building a newer version of this documentation took longer than - expected. Please refresh to try again. - - )} {state === 'CONTENT_STALE_ERROR' && ( - - Building a newer version of this documentation failed. {errorMessage} + } + classes={{ message: classes.message }} + > + Building a newer version of this documentation failed.{' '} + {syncErrorMessage} )} {state === 'CONTENT_NOT_FOUND' && ( - + <> + {syncErrorMessage && ( + } + classes={{ message: classes.message }} + > + Building a newer version of this documentation failed.{' '} + {syncErrorMessage} + + )} + + )}
diff --git a/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx new file mode 100644 index 0000000000..d1bb99b76c --- /dev/null +++ b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.test.tsx @@ -0,0 +1,87 @@ +/* + * 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. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { + TechDocsBuildLogs, + TechDocsBuildLogsDrawerContent, +} from './TechDocsBuildLogs'; + +// react-lazylog is based on a react-virtualized component which doesn't +// write the content to the dom, so we mock it. +jest.mock('react-lazylog', () => { + return { + LazyLog: ({ text }: { text: string }) => { + return

{text}

; + }, + }; +}); + +describe('', () => { + it('should render with button', () => { + const rendered = render(); + expect(rendered.getByText(/Show Build Logs/i)).toBeInTheDocument(); + expect(rendered.queryByText(/Build Details/i)).not.toBeInTheDocument(); + }); + + it('should open drawer', () => { + const rendered = render(); + rendered.getByText(/Show Build Logs/i).click(); + expect(rendered.getByText(/Build Details/i)).toBeInTheDocument(); + }); +}); + +describe('', () => { + it('should render with empty log', () => { + const onClose = jest.fn(); + const rendered = render( + , + ); + expect(rendered.getByText(/Build Details/i)).toBeInTheDocument(); + expect(rendered.getByText(/Waiting for logs.../i)).toBeInTheDocument(); + + expect(onClose).toBeCalledTimes(0); + }); + + it('should render with empty logs', () => { + const onClose = jest.fn(); + const rendered = render( + , + ); + expect(rendered.getByText(/Build Details/i)).toBeInTheDocument(); + expect( + rendered.queryByText(/Waiting for logs.../i), + ).not.toBeInTheDocument(); + expect(rendered.getByText(/Line 1/i)).toBeInTheDocument(); + expect(rendered.getByText(/Line 2/i)).toBeInTheDocument(); + + expect(onClose).toBeCalledTimes(0); + }); + + it('should call onClose', () => { + const onClose = jest.fn(); + const rendered = render( + , + ); + rendered.getByRole('button').click(); + + expect(onClose).toBeCalledTimes(1); + }); +}); diff --git a/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx new file mode 100644 index 0000000000..d9da954152 --- /dev/null +++ b/plugins/techdocs/src/reader/components/TechDocsBuildLogs.tsx @@ -0,0 +1,121 @@ +/* + * 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. + */ + +import { + Button, + createStyles, + Drawer, + Grid, + IconButton, + makeStyles, + Theme, + Typography, +} from '@material-ui/core'; +import Close from '@material-ui/icons/Close'; +import * as React from 'react'; +import { useState } from 'react'; +import { LazyLog } from 'react-lazylog'; + +const useDrawerStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + width: '100%', + [theme.breakpoints.up('sm')]: { + width: '75%', + }, + [theme.breakpoints.up('md')]: { + width: '50%', + }, + padding: theme.spacing(2.5), + }, + root: { + height: '100%', + overflow: 'hidden', + }, + }), +); + +export const TechDocsBuildLogsDrawerContent = ({ + buildLog, + onClose, +}: { + buildLog: string[]; + onClose: () => void; +}) => { + const classes = useDrawerStyles(); + return ( + + + Build Details + + + + + + + + ); +}; + +export const TechDocsBuildLogs = ({ buildLog }: { buildLog: string[] }) => { + const classes = useDrawerStyles(); + const [open, setOpen] = useState(false); + + return ( + <> + + setOpen(false)} + > + setOpen(false)} + /> + + + ); +}; diff --git a/plugins/techdocs/src/reader/components/TechDocsPage.tsx b/plugins/techdocs/src/reader/components/TechDocsPage.tsx index 88c53a38a7..19092f47c3 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPage.tsx @@ -30,7 +30,7 @@ export const TechDocsPage = () => { const techdocsApi = useApi(techdocsApiRef); - const techdocsMetadataRequest = useAsync(() => { + const { value: techdocsMetadataValue } = useAsync(() => { if (documentReady) { return techdocsApi.getTechDocsMetadata({ kind, namespace, name }); } @@ -38,7 +38,7 @@ export const TechDocsPage = () => { return Promise.resolve(undefined); }, [kind, namespace, name, techdocsApi, documentReady]); - const entityMetadataRequest = useAsync(() => { + const { value: entityMetadataValue } = useAsync(() => { return techdocsApi.getEntityMetadata({ kind, namespace, name }); }, [kind, namespace, name, techdocsApi]); @@ -49,10 +49,8 @@ export const TechDocsPage = () => { return ( ', () => { name: 'test-name', namespace: 'test-namespace', }} - metadataRequest={{ - entity: { - loading: false, - value: { - locationMetadata: { - type: 'github', - target: 'https://example.com/', - }, - spec: { - owner: 'test', - }, - }, + entityMetadata={{ + locationMetadata: { + type: 'github', + target: 'https://example.com/', }, - techdocs: { - loading: false, - value: { - site_name: 'test-site-name', - site_description: 'test-site-desc', - }, + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'test', }, + spec: { + owner: 'test', + }, + }} + techDocsMetadata={{ + site_name: 'test-site-name', + site_description: 'test-site-desc', }} />, { @@ -75,14 +72,6 @@ describe('', () => { name: 'test-name', namespace: 'test-namespace', }} - metadataRequest={{ - entity: { - loading: false, - }, - techdocs: { - loading: false, - }, - }} />, { mountedRoutes: { @@ -105,17 +94,9 @@ describe('', () => { name: 'test-name', namespace: 'test-namespace', }} - metadataRequest={{ - entity: { - loading: false, - }, - techdocs: { - loading: false, - value: { - site_name: 'test-site-name', - site_description: 'test-site-desc', - }, - }, + techDocsMetadata={{ + site_name: 'test-site-name', + site_description: 'test-site-desc', }} />, { diff --git a/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx b/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx index 38e122e9cb..34d564bdfe 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPageHeader.tsx @@ -15,6 +15,8 @@ */ import { EntityName, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { Header, HeaderLabel } from '@backstage/core-components'; +import { useRouteRef } from '@backstage/core-plugin-api'; import { EntityRefLink, EntityRefLinks, @@ -22,45 +24,30 @@ import { } from '@backstage/plugin-catalog-react'; import CodeIcon from '@material-ui/icons/Code'; import React from 'react'; -import { AsyncState } from 'react-use/lib/useAsync'; import { rootRouteRef } from '../../routes'; -import { TechDocsMetadata } from '../../types'; - -import { Header, HeaderLabel } from '@backstage/core-components'; -import { useRouteRef } from '@backstage/core-plugin-api'; +import { TechDocsEntityMetadata, TechDocsMetadata } from '../../types'; type TechDocsPageHeaderProps = { entityId: EntityName; - metadataRequest: { - entity: AsyncState; - techdocs: AsyncState; - }; + entityMetadata?: TechDocsEntityMetadata; + techDocsMetadata?: TechDocsMetadata; }; export const TechDocsPageHeader = ({ entityId, - metadataRequest, + entityMetadata, + techDocsMetadata, }: TechDocsPageHeaderProps) => { - const { - techdocs: techdocsMetadata, - entity: entityMetadata, - } = metadataRequest; - - const { value: techdocsMetadataValues } = techdocsMetadata; - const { value: entityMetadataValues } = entityMetadata; - const { name } = entityId; const { site_name: siteName, site_description: siteDescription } = - techdocsMetadataValues || {}; + techDocsMetadata || {}; - const { - locationMetadata, - spec: { lifecycle }, - } = entityMetadataValues || { spec: {} }; + const { locationMetadata, spec } = entityMetadata || {}; + const lifecycle = spec?.lifecycle; - const ownedByRelations = entityMetadataValues - ? getEntityRelations(entityMetadataValues, RELATION_OWNED_BY) + const ownedByRelations = entityMetadata + ? getEntityRelations(entityMetadata, RELATION_OWNED_BY) : []; const docsRootLink = useRouteRef(rootRouteRef)(); diff --git a/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx b/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx deleted file mode 100644 index f33ed62b97..0000000000 --- a/plugins/techdocs/src/reader/components/TechDocsProgressBar.test.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 TechDocsProgressBar from './TechDocsProgressBar'; -import React from 'react'; -import { render } from '@testing-library/react'; -import { wrapInTestApp } from '@backstage/test-utils'; -import { act } from 'react-dom/test-utils'; - -jest.useFakeTimers(); - -describe('', () => { - it('should render a message if techdocs page takes more time to load', () => { - const rendered = render(wrapInTestApp()); - act(() => { - jest.advanceTimersByTime(250); - }); - expect(rendered.getByTestId('progress')).toBeInTheDocument(); - expect(rendered.queryByTestId('delay-reason')).toBeNull(); - act(() => { - jest.advanceTimersByTime(5000); - }); - expect(rendered.getByTestId('delay-reason')).toBeInTheDocument(); - }); -}); diff --git a/plugins/techdocs/src/reader/components/TechDocsProgressBar.tsx b/plugins/techdocs/src/reader/components/TechDocsProgressBar.tsx deleted file mode 100644 index 7089f5ae08..0000000000 --- a/plugins/techdocs/src/reader/components/TechDocsProgressBar.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 React, { useState, useEffect } from 'react'; -import { useMountedState } from 'react-use'; -import { Typography } from '@material-ui/core'; -import { Progress } from '@backstage/core-components'; - -const TechDocsProgressBar = () => { - const isMounted = useMountedState(); - const [hasBeenDelayed, setHasBeenDelayed] = useState(false); - - const delayReason = `Docs are still loading...Backstage takes some extra time to load docs - for the first time. The subsequent loads are much faster.`; - - // Allowed time that docs can take to load (in milliseconds) - const allowedDelayTime = 5000; - - useEffect(() => { - setTimeout(() => { - if (isMounted()) { - setHasBeenDelayed(true); - } - }, allowedDelayTime); - }); - - return ( - <> - {hasBeenDelayed ? ( - {delayReason} - ) : null} - - - ); -}; - -export default TechDocsProgressBar; diff --git a/plugins/techdocs/src/reader/components/useReaderState.test.tsx b/plugins/techdocs/src/reader/components/useReaderState.test.tsx index 1edf41af30..b0d4e0326a 100644 --- a/plugins/techdocs/src/reader/components/useReaderState.test.tsx +++ b/plugins/techdocs/src/reader/components/useReaderState.test.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; import { NotFoundError } from '@backstage/errors'; import { act, renderHook } from '@testing-library/react-hooks'; import React from 'react'; @@ -23,7 +24,6 @@ import { reducer, useReaderState, } from './useReaderState'; -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; describe('useReaderState', () => { let Wrapper: React.ComponentType; @@ -55,14 +55,12 @@ describe('useReaderState', () => { ${false} | ${undefined} | ${'BUILDING'} | ${'INITIAL_BUILD'} ${false} | ${undefined} | ${'BUILD_READY'} | ${'CONTENT_NOT_FOUND'} ${false} | ${undefined} | ${'BUILD_READY_RELOAD'} | ${'CHECKING'} - ${false} | ${undefined} | ${'BUILD_TIMED_OUT'} | ${'CONTENT_NOT_FOUND'} ${false} | ${undefined} | ${'UP_TO_DATE'} | ${'CONTENT_NOT_FOUND'} ${false} | ${undefined} | ${'ERROR'} | ${'CONTENT_NOT_FOUND'} ${false} | ${'asdf'} | ${'CHECKING'} | ${'CONTENT_FRESH'} ${false} | ${'asdf'} | ${'BUILDING'} | ${'CONTENT_STALE_REFRESHING'} ${false} | ${'asdf'} | ${'BUILD_READY'} | ${'CONTENT_STALE_READY'} ${false} | ${'asdf'} | ${'BUILD_READY_RELOAD'} | ${'CHECKING'} - ${false} | ${'asdf'} | ${'BUILD_TIMED_OUT'} | ${'CONTENT_STALE_TIMEOUT'} ${false} | ${'asdf'} | ${'UP_TO_DATE'} | ${'CONTENT_FRESH'} ${false} | ${'asdf'} | ${'ERROR'} | ${'CONTENT_STALE_ERROR'} `( @@ -84,30 +82,33 @@ describe('useReaderState', () => { activeSyncState: 'CHECKING', contentLoading: false, path: '', + buildLog: ['1', '2'], }; it('should return a copy of the state', () => { - expect(reducer(oldState, { type: 'navigate', path: '/' })).toEqual({ + expect(reducer(oldState, { type: 'content', path: '/' })).toEqual({ activeSyncState: 'CHECKING', contentLoading: false, path: '/', + buildLog: ['1', '2'], }); expect(oldState).toEqual({ activeSyncState: 'CHECKING', contentLoading: false, path: '', + buildLog: ['1', '2'], }); }); it.each` - type | oldActiveSyncState | newActiveSyncState - ${'content'} | ${'BUILD_READY'} | ${'UP_TO_DATE'} - ${'content'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'} - ${'navigate'} | ${'BUILD_READY'} | ${'UP_TO_DATE'} - ${'navigate'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'} - ${'sync'} | ${'BUILD_READY'} | ${undefined} - ${'sync'} | ${'BUILD_READY_RELOAD'} | ${undefined} + type | oldActiveSyncState | newActiveSyncState + ${'contentLoading'} | ${'BUILD_READY'} | ${'UP_TO_DATE'} + ${'contentLoading'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'} + ${'content'} | ${'BUILD_READY'} | ${'UP_TO_DATE'} + ${'content'} | ${'BUILD_READY_RELOAD'} | ${'UP_TO_DATE'} + ${'sync'} | ${'BUILD_READY'} | ${undefined /* undefined, because we don't set an input */} + ${'sync'} | ${'BUILD_READY_RELOAD'} | ${undefined /* undefined, because we don't set an input */} `( 'should, when type=$type and activeSyncState=$oldActiveSyncState, set activeSyncState=$newActiveSyncState', ({ type, oldActiveSyncState, newActiveSyncState }) => { @@ -123,18 +124,45 @@ describe('useReaderState', () => { }, ); - describe('"content" action', () => { + describe('"contentLoading" action', () => { it('should set loading', () => { + expect( + reducer(oldState, { + type: 'contentLoading', + }), + ).toEqual({ + ...oldState, + contentLoading: true, + }); + }); + + it('should keep content', () => { expect( reducer( { ...oldState, content: 'some-old-content', + }, + { + type: 'contentLoading', + }, + ), + ).toEqual({ + ...oldState, + contentLoading: true, + content: 'some-old-content', + }); + }); + + it('should reset errors', () => { + expect( + reducer( + { + ...oldState, contentError: new Error(), }, { - type: 'content', - contentLoading: true, + type: 'contentLoading', }, ), ).toEqual({ @@ -142,7 +170,9 @@ describe('useReaderState', () => { contentLoading: true, }); }); + }); + describe('"content" action', () => { it('should set content', () => { expect( reducer( @@ -163,6 +193,27 @@ describe('useReaderState', () => { }); }); + it('should set content and update path', () => { + expect( + reducer( + { + ...oldState, + contentLoading: true, + }, + { + type: 'content', + content: 'asdf', + path: '/new-path', + }, + ), + ).toEqual({ + ...oldState, + contentLoading: false, + content: 'asdf', + path: '/new-path', + }); + }); + it('should set error', () => { expect( reducer( @@ -184,20 +235,6 @@ describe('useReaderState', () => { }); }); - describe('"navigate" action', () => { - it('should work', () => { - expect( - reducer(oldState, { - type: 'navigate', - path: '/', - }), - ).toEqual({ - ...oldState, - path: '/', - }); - }); - }); - describe('"sync" action', () => { it('should update state', () => { expect( @@ -210,6 +247,33 @@ describe('useReaderState', () => { activeSyncState: 'BUILDING', }); }); + + it('should clear buildLog on "CHECKING"', () => { + expect( + reducer(oldState, { + type: 'sync', + state: 'CHECKING', + }), + ).toEqual({ + ...oldState, + activeSyncState: 'CHECKING', + buildLog: [], + }); + }); + }); + + describe('"buildLog" action', () => { + it('should work', () => { + expect( + reducer(oldState, { + type: 'buildLog', + log: 'Another Line', + }), + ).toEqual({ + ...oldState, + buildLog: ['1', '2', 'Another Line'], + }); + }); }); }); @@ -228,27 +292,38 @@ describe('useReaderState', () => { expect(result.current).toEqual({ state: 'CHECKING', + path: '/example', content: undefined, - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CONTENT_FRESH', + path: '/example', content: 'my content', - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); expect(techdocsStorageApi.getEntityDocs).toBeCalledWith( { kind: 'Component', namespace: 'default', name: 'backstage' }, '/example', ); - expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith({ - kind: 'Component', - namespace: 'default', - name: 'backstage', - }); + expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith( + { + kind: 'Component', + namespace: 'default', + name: 'backstage', + }, + expect.any(Function), + ); }); }); @@ -259,10 +334,14 @@ describe('useReaderState', () => { await new Promise(resolve => setTimeout(resolve, 500)); return 'my content'; }); - techdocsStorageApi.syncEntityDocs.mockImplementation(async () => { - await new Promise(resolve => setTimeout(resolve, 1100)); - return 'updated'; - }); + techdocsStorageApi.syncEntityDocs.mockImplementation( + async (_, logHandler) => { + logHandler?.call(this, 'Line 1'); + logHandler?.call(this, 'Line 2'); + await new Promise(resolve => setTimeout(resolve, 1100)); + return 'updated'; + }, + ); await act(async () => { const { result, waitForValueToChange } = await renderHook( @@ -272,32 +351,48 @@ describe('useReaderState', () => { expect(result.current).toEqual({ state: 'CHECKING', + path: '/example', content: undefined, - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'INITIAL_BUILD', + path: '/example', content: undefined, - errorMessage: ' Load error: NotFoundError: Page Not Found', + contentErrorMessage: 'NotFoundError: Page Not Found', + syncErrorMessage: undefined, + buildLog: ['Line 1', 'Line 2'], + contentReload: expect.any(Function), }); await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CHECKING', + path: '/example', content: undefined, - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CONTENT_FRESH', + path: '/example', content: 'my content', - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); expect(techdocsStorageApi.getEntityDocs).toBeCalledTimes(2); @@ -306,20 +401,32 @@ describe('useReaderState', () => { '/example', ); expect(techdocsStorageApi.syncEntityDocs).toBeCalledTimes(1); - expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith({ - kind: 'Component', - namespace: 'default', - name: 'backstage', - }); + expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith( + { + kind: 'Component', + namespace: 'default', + name: 'backstage', + }, + expect.any(Function), + ); }); }); it('should handle stale content', async () => { - techdocsStorageApi.getEntityDocs.mockResolvedValue('my content'); - techdocsStorageApi.syncEntityDocs.mockImplementation(async () => { - await new Promise(resolve => setTimeout(resolve, 1100)); - return 'updated'; - }); + techdocsStorageApi.getEntityDocs + .mockResolvedValueOnce('my content') + .mockImplementationOnce(async () => { + await new Promise(resolve => setTimeout(resolve, 1100)); + return 'my new content'; + }); + techdocsStorageApi.syncEntityDocs.mockImplementation( + async (_, logHandler) => { + logHandler?.call(this, 'Line 1'); + logHandler?.call(this, 'Line 2'); + await new Promise(resolve => setTimeout(resolve, 1100)); + return 'updated'; + }, + ); await act(async () => { const { result, waitForValueToChange } = await renderHook( @@ -329,79 +436,187 @@ describe('useReaderState', () => { expect(result.current).toEqual({ state: 'CHECKING', + path: '/example', content: undefined, - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); // the content is returned but the sync is in progress await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CONTENT_FRESH', + path: '/example', content: 'my content', - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: ['Line 1', 'Line 2'], + contentReload: expect.any(Function), }); // the sync takes longer than 1 seconds so the refreshing state starts await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CONTENT_STALE_REFRESHING', + path: '/example', content: 'my content', - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: ['Line 1', 'Line 2'], + contentReload: expect.any(Function), }); - // the content is up-to-date + // the content is updated but not yet displayed await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CONTENT_STALE_READY', + path: '/example', content: 'my content', - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: ['Line 1', 'Line 2'], + contentReload: expect.any(Function), }); + // reload the content + result.current.contentReload(); + + // the new content refresh is triggered + await waitForValueToChange(() => result.current.state); + expect(result.current).toEqual({ + state: 'CHECKING', + path: '/example', + content: 'my content', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + + // the new content is loaded + await waitForValueToChange(() => result.current.state); + expect(result.current).toEqual({ + state: 'CONTENT_FRESH', + path: '/example', + content: 'my new content', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + + expect(techdocsStorageApi.getEntityDocs).toBeCalledTimes(2); expect(techdocsStorageApi.getEntityDocs).toBeCalledWith( { kind: 'Component', namespace: 'default', name: 'backstage' }, '/example', ); - expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith({ - kind: 'Component', - namespace: 'default', - name: 'backstage', - }); + expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith( + { + kind: 'Component', + namespace: 'default', + name: 'backstage', + }, + expect.any(Function), + ); }); }); - it('should handle timed-out refresh', async () => { - techdocsStorageApi.getEntityDocs.mockResolvedValue('my content'); - techdocsStorageApi.syncEntityDocs.mockResolvedValue('timeout'); + it('should handle navigation', async () => { + techdocsStorageApi.getEntityDocs + .mockResolvedValueOnce('my content') + .mockImplementationOnce(async () => { + await new Promise(resolve => setTimeout(resolve, 1100)); + return 'my new content'; + }) + .mockRejectedValueOnce(new NotFoundError('Some error description')); + techdocsStorageApi.syncEntityDocs.mockResolvedValue('cached'); await act(async () => { - const { result, waitForValueToChange } = await renderHook( - () => useReaderState('Component', 'default', 'backstage', '/example'), - { wrapper: Wrapper }, + const { result, waitForValueToChange, rerender } = await renderHook( + ({ path }: { path: string }) => + useReaderState('Component', 'default', 'backstage', path), + { initialProps: { path: '/example' }, wrapper: Wrapper as any }, ); expect(result.current).toEqual({ state: 'CHECKING', + path: '/example', content: undefined, - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); - // the content is returned but the sync is in progress + // show the content await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ - state: 'CONTENT_STALE_TIMEOUT', + state: 'CONTENT_FRESH', + path: '/example', content: 'my content', - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + + // navigate + rerender({ path: '/new' }); + + await waitForValueToChange(() => result.current.state); + expect(result.current).toEqual({ + state: 'CHECKING', + path: '/example', + content: 'my content', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + + await waitForValueToChange(() => result.current.state); + expect(result.current).toEqual({ + state: 'CONTENT_FRESH', + path: '/new', + content: 'my new content', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + + // navigate + rerender({ path: '/missing' }); + + await waitForValueToChange(() => result.current.state); + expect(result.current).toEqual({ + state: 'CONTENT_NOT_FOUND', + path: '/missing', + content: undefined, + contentErrorMessage: 'NotFoundError: Some error description', + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); expect(techdocsStorageApi.getEntityDocs).toBeCalledWith( { kind: 'Component', namespace: 'default', name: 'backstage' }, '/example', ); - expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith({ - kind: 'Component', - namespace: 'default', - name: 'backstage', - }); + expect(techdocsStorageApi.getEntityDocs).toBeCalledWith( + { kind: 'Component', namespace: 'default', name: 'backstage' }, + '/new', + ); + expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith( + { + kind: 'Component', + namespace: 'default', + name: 'backstage', + }, + expect.any(Function), + ); }); }); @@ -419,27 +634,38 @@ describe('useReaderState', () => { expect(result.current).toEqual({ state: 'CHECKING', + path: '/example', content: undefined, - errorMessage: '', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); // the content loading threw an error await waitForValueToChange(() => result.current.state); expect(result.current).toEqual({ state: 'CONTENT_NOT_FOUND', + path: '/example', content: undefined, - errorMessage: ' Load error: NotFoundError: Some error description', + contentErrorMessage: 'NotFoundError: Some error description', + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), }); expect(techdocsStorageApi.getEntityDocs).toBeCalledWith( { kind: 'Component', namespace: 'default', name: 'backstage' }, '/example', ); - expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith({ - kind: 'Component', - namespace: 'default', - name: 'backstage', - }); + expect(techdocsStorageApi.syncEntityDocs).toBeCalledWith( + { + kind: 'Component', + namespace: 'default', + name: 'backstage', + }, + expect.any(Function), + ); }); }); }); diff --git a/plugins/techdocs/src/reader/components/useReaderState.ts b/plugins/techdocs/src/reader/components/useReaderState.ts index ac5145ee49..07bd338de9 100644 --- a/plugins/techdocs/src/reader/components/useReaderState.ts +++ b/plugins/techdocs/src/reader/components/useReaderState.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { useEffect, useMemo, useReducer, useRef } from 'react'; +import { useApi } from '@backstage/core-plugin-api'; +import { useMemo, useReducer, useRef } from 'react'; import { useAsync, useAsyncRetry } from 'react-use'; import { techdocsStorageApiRef } from '../../api'; -import { useApi } from '@backstage/core-plugin-api'; /** * A state representation that is used to configure the UI of @@ -35,9 +35,6 @@ type ContentStateTypes = /** There is content, but after a reload, the content will be different */ | 'CONTENT_STALE_READY' - /** There is content, the backend tried to update it, but it took too long */ - | 'CONTENT_STALE_TIMEOUT' - /** There is content, the backend tried to update it, but failed */ | 'CONTENT_STALE_ERROR' @@ -93,11 +90,6 @@ export function calculateDisplayState({ return 'CONTENT_STALE_READY'; } - // the build timed out, but the content is still stale - if (activeSyncState === 'BUILD_TIMED_OUT') { - return 'CONTENT_STALE_TIMEOUT'; - } - // the build failed, but the content is still stale if (activeSyncState === 'ERROR') { return 'CONTENT_STALE_ERROR'; @@ -127,9 +119,6 @@ type SyncStates = */ | 'BUILD_READY_RELOAD' - /** Building the documentation timed out */ - | 'BUILD_TIMED_OUT' - /** No need for a sync. The content was already up-to-date. */ | 'UP_TO_DATE' @@ -142,13 +131,14 @@ type ReducerActions = state: SyncStates; syncError?: Error; } + | { type: 'contentLoading' } | { type: 'content'; + path?: string; content?: string; - contentLoading?: true; contentError?: Error; } - | { type: 'navigate'; path: string }; + | { type: 'buildLog'; log: string }; type ReducerState = { /** @@ -172,6 +162,11 @@ type ReducerState = { contentError?: Error; syncError?: Error; + + /** + * A list of log messages that were emitted by the build process. + */ + buildLog: string[]; }; export function reducer( @@ -182,30 +177,48 @@ export function reducer( switch (action.type) { case 'sync': + // reset the build log when a new check starts + if (action.state === 'CHECKING') { + newState.buildLog = []; + } + newState.activeSyncState = action.state; newState.syncError = action.syncError; break; + case 'contentLoading': + newState.contentLoading = true; + + // only reset errors but keep the old content until it is replaced by the 'content' action + newState.contentError = undefined; + break; + case 'content': + // only override the path if it is part of the action + if (typeof action.path === 'string') { + newState.path = action.path; + } + + newState.contentLoading = false; newState.content = action.content; - newState.contentLoading = action.contentLoading ?? false; newState.contentError = action.contentError; break; - case 'navigate': - newState.path = action.path; + case 'buildLog': + newState.buildLog = newState.buildLog.concat(action.log); break; default: throw new Error(); } - // a navigation or a content update loads fresh content so the build is updated to being up-to-date + // a content update loads fresh content so the build is updated to being up-to-date if ( ['BUILD_READY', 'BUILD_READY_RELOAD'].includes(newState.activeSyncState) && - ['content', 'navigate'].includes(action.type) + ['contentLoading', 'content'].includes(action.type) ) { newState.activeSyncState = 'UP_TO_DATE'; + newState.buildLog = []; } return newState; @@ -216,23 +229,27 @@ export function useReaderState( namespace: string, name: string, path: string, -): { state: ContentStateTypes; content?: string; errorMessage?: string } { +): { + state: ContentStateTypes; + path: string; + contentReload: () => void; + content?: string; + contentErrorMessage?: string; + syncErrorMessage?: string; + buildLog: string[]; +} { const [state, dispatch] = useReducer(reducer, { activeSyncState: 'CHECKING', path, contentLoading: true, + buildLog: [], }); const techdocsStorageApi = useApi(techdocsStorageApiRef); - // convert all path changes into actions - useEffect(() => { - dispatch({ type: 'navigate', path }); - }, [path]); - // try to load the content. the function will fire events and we don't care for the return values const { retry: contentReload } = useAsyncRetry(async () => { - dispatch({ type: 'content', contentLoading: true }); + dispatch({ type: 'contentLoading' }); try { const entityDocs = await techdocsStorageApi.getEntityDocs( @@ -240,11 +257,12 @@ export function useReaderState( path, ); - dispatch({ type: 'content', content: entityDocs }); + // update content and path at the same time + dispatch({ type: 'content', content: entityDocs, path }); return entityDocs; } catch (e) { - dispatch({ type: 'content', contentError: e }); + dispatch({ type: 'content', contentError: e, path }); } return undefined; @@ -268,24 +286,38 @@ export function useReaderState( }, 1000); try { - const result = await techdocsStorageApi.syncEntityDocs({ - kind, - namespace, - name, - }); + const result = await techdocsStorageApi.syncEntityDocs( + { + kind, + namespace, + name, + }, + log => { + dispatch({ type: 'buildLog', log }); + }, + ); - if (result === 'updated') { - // if there was no content prior to building, retry the loading - if (!contentRef.current.content) { - contentRef.current.reload(); - dispatch({ type: 'sync', state: 'BUILD_READY_RELOAD' }); - } else { - dispatch({ type: 'sync', state: 'BUILD_READY' }); - } - } else if (result === 'cached') { - dispatch({ type: 'sync', state: 'UP_TO_DATE' }); - } else { - dispatch({ type: 'sync', state: 'BUILD_TIMED_OUT' }); + switch (result) { + case 'updated': + // if there was no content prior to building, retry the loading + if (!contentRef.current.content) { + contentRef.current.reload(); + dispatch({ type: 'sync', state: 'BUILD_READY_RELOAD' }); + } else { + dispatch({ type: 'sync', state: 'BUILD_READY' }); + } + break; + case 'cached': + dispatch({ type: 'sync', state: 'UP_TO_DATE' }); + break; + + default: + dispatch({ + type: 'sync', + state: 'ERROR', + syncError: new Error('Unexpected return state'), + }); + break; } } catch (e) { dispatch({ type: 'sync', state: 'ERROR', syncError: e }); @@ -305,19 +337,13 @@ export function useReaderState( [state.activeSyncState, state.content, state.contentLoading], ); - const errorMessage = useMemo(() => { - let errMessage = ''; - if (state.contentError) { - errMessage += ` Load error: ${state.contentError}`; - } - if (state.syncError) errMessage += ` Build error: ${state.syncError}`; - - return errMessage; - }, [state.syncError, state.contentError]); - return { state: displayState, + contentReload, + path: state.path, content: state.content, - errorMessage, + contentErrorMessage: state.contentError?.toString(), + syncErrorMessage: state.syncError?.toString(), + buildLog: state.buildLog, }; } diff --git a/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts b/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts index f04c9963f0..11e8375420 100644 --- a/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts +++ b/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts @@ -15,9 +15,9 @@ */ import { waitFor } from '@testing-library/react'; -import { createTestShadowDom } from '../../test-utils'; -import { addBaseUrl } from '../transformers'; import { TechDocsStorageApi } from '../../api'; +import { createTestShadowDom } from '../../test-utils'; +import { addBaseUrl } from './addBaseUrl'; const DOC_STORAGE_URL = 'https://example-host.storage.googleapis.com'; const API_ORIGIN_URL = 'https://backstage.example.com/api/techdocs'; @@ -62,8 +62,8 @@ describe('addBaseUrl', () => { global.fetch = originalFetch; }); - it('contains relative paths', () => { - createTestShadowDom(fixture, { + it('contains relative paths', async () => { + await createTestShadowDom(fixture, { preTransformers: [ addBaseUrl({ techdocsStorageApi, @@ -110,7 +110,7 @@ describe('addBaseUrl', () => { text: jest.fn().mockResolvedValue(svgContent), }); - const root = createTestShadowDom('', { + const root = await createTestShadowDom('', { preTransformers: [ addBaseUrl({ techdocsStorageApi, @@ -137,7 +137,7 @@ describe('addBaseUrl', () => { text: jest.fn().mockResolvedValue(svgContent), }); - const root = createTestShadowDom( + const root = await createTestShadowDom( ``, { preTransformers: [ @@ -162,16 +162,19 @@ describe('addBaseUrl', () => { it('does not inline external svgs', async () => { const expectedSrc = 'https://example.com/test.svg'; - const root = createTestShadowDom(``, { - preTransformers: [ - addBaseUrl({ - techdocsStorageApi, - entityId: mockEntityId, - path: '', - }), - ], - postTransformers: [], - }); + const root = await createTestShadowDom( + ``, + { + preTransformers: [ + addBaseUrl({ + techdocsStorageApi, + entityId: mockEntityId, + path: '', + }), + ], + postTransformers: [], + }, + ); await new Promise(done => { process.nextTick(() => { diff --git a/plugins/techdocs/src/reader/transformers/addBaseUrl.ts b/plugins/techdocs/src/reader/transformers/addBaseUrl.ts index f21cb8c47a..8f08ec83a6 100644 --- a/plugins/techdocs/src/reader/transformers/addBaseUrl.ts +++ b/plugins/techdocs/src/reader/transformers/addBaseUrl.ts @@ -14,8 +14,8 @@ * limitations under the License. */ import { EntityName } from '@backstage/catalog-model'; -import type { Transformer } from './transformer'; import { TechDocsStorageApi } from '../../api'; +import type { Transformer } from './transformer'; type AddBaseUrlOptions = { techdocsStorageApi: TechDocsStorageApi; @@ -44,14 +44,15 @@ export const addBaseUrl = ({ entityId, path, }: AddBaseUrlOptions): Transformer => { - return dom => { - const updateDom = ( + return async dom => { + const apiOrigin = await techdocsStorageApi.getApiOrigin(); + + const updateDom = async ( list: HTMLCollectionOf | NodeListOf, attributeName: string, - ): void => { - Array.from(list) - .filter(elem => !!elem.getAttribute(attributeName)) - .forEach(async (elem: T) => { + ) => { + for (const elem of list) { + if (elem.hasAttribute(attributeName)) { const elemAttribute = elem.getAttribute(attributeName); if (!elemAttribute) return; @@ -61,7 +62,7 @@ export const addBaseUrl = ({ entityId, path, ); - const apiOrigin = await techdocsStorageApi.getApiOrigin(); + if (isSvgNeedingInlining(attributeName, elemAttribute, apiOrigin)) { try { const svg = await fetch(newValue, { credentials: 'include' }); @@ -76,13 +77,16 @@ export const addBaseUrl = ({ } else { elem.setAttribute(attributeName, newValue); } - }); + } + } }; - updateDom(dom.querySelectorAll('img'), 'src'); - updateDom(dom.querySelectorAll('script'), 'src'); - updateDom(dom.querySelectorAll('link'), 'href'); - updateDom(dom.querySelectorAll('a[download]'), 'href'); + await Promise.all([ + updateDom(dom.querySelectorAll('img'), 'src'), + updateDom(dom.querySelectorAll('script'), 'src'), + updateDom(dom.querySelectorAll('link'), 'href'), + updateDom(dom.querySelectorAll('a[download]'), 'href'), + ]); return dom; }; diff --git a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts index 2165afac98..0b8e431781 100644 --- a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts +++ b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts @@ -28,8 +28,8 @@ const integrations = ScmIntegrations.fromConfig( ); describe('addGitFeedbackLink', () => { - it('adds a feedback link when a Gitlab source edit link is available', () => { - const shadowDom = createTestShadowDom( + it('adds a feedback link when a Gitlab source edit link is available', async () => { + const shadowDom = await createTestShadowDom( ` @@ -53,8 +53,8 @@ describe('addGitFeedbackLink', () => { ); }); - it('adds a feedback link when a Github source edit link is available', () => { - const shadowDom = createTestShadowDom( + it('adds a feedback link when a Github source edit link is available', async () => { + const shadowDom = await createTestShadowDom( ` @@ -78,8 +78,8 @@ describe('addGitFeedbackLink', () => { ); }); - it('does not add a feedback link when no source edit link is available', () => { - const shadowDom = createTestShadowDom( + it('does not add a feedback link when no source edit link is available', async () => { + const shadowDom = await createTestShadowDom( ` @@ -97,8 +97,8 @@ describe('addGitFeedbackLink', () => { expect(shadowDom.querySelector('#git-feedback-link')).toBeFalsy(); }); - it('does not add a feedback link when a Gitlab or Github source edit link is not available', () => { - const shadowDom = createTestShadowDom( + it('does not add a feedback link when a Gitlab or Github source edit link is not available', async () => { + const shadowDom = await createTestShadowDom( ` @@ -117,8 +117,8 @@ describe('addGitFeedbackLink', () => { expect(shadowDom.querySelector('#git-feedback-link')).toBeFalsy(); }); - it('adds a feedback link when a Gitlab or Github source edit link is not available but hostname matches an integrations host', () => { - const shadowDom = createTestShadowDom( + it('adds a feedback link when a Gitlab or Github source edit link is not available but hostname matches an integrations host', async () => { + const shadowDom = await createTestShadowDom( ` diff --git a/plugins/techdocs/src/reader/transformers/addLinkClickListener.test.ts b/plugins/techdocs/src/reader/transformers/addLinkClickListener.test.ts index 2d2d54a31c..fe3a6e557c 100644 --- a/plugins/techdocs/src/reader/transformers/addLinkClickListener.test.ts +++ b/plugins/techdocs/src/reader/transformers/addLinkClickListener.test.ts @@ -18,9 +18,9 @@ import { createTestShadowDom } from '../../test-utils'; import { addLinkClickListener } from './addLinkClickListener'; describe('addLinkClickListener', () => { - it('calls onClick when a link has been clicked', () => { + it('calls onClick when a link has been clicked', async () => { const fn = jest.fn(); - const shadowDom = createTestShadowDom( + const shadowDom = await createTestShadowDom( ` @@ -45,9 +45,9 @@ describe('addLinkClickListener', () => { expect(fn).toHaveBeenCalledTimes(1); }); - it('does not call onClick when a link links to another baseUrl', () => { + it('does not call onClick when a link links to another baseUrl', async () => { const fn = jest.fn(); - const shadowDom = createTestShadowDom( + const shadowDom = await createTestShadowDom( ` diff --git a/plugins/techdocs/src/reader/transformers/index.test.ts b/plugins/techdocs/src/reader/transformers/index.test.ts index 30607aedcd..16b42266df 100644 --- a/plugins/techdocs/src/reader/transformers/index.test.ts +++ b/plugins/techdocs/src/reader/transformers/index.test.ts @@ -17,14 +17,14 @@ import { Transformer, transform } from './transformer'; describe('transform', () => { - it('calls the transformers', () => { + it('calls the transformers', async () => { const fn = jest.fn(); const mockTransformer = (): Transformer => (dom: Element) => { fn(dom); return dom; }; - transform('', [mockTransformer()]); + await transform('', [mockTransformer()]); expect(fn).toHaveBeenCalledTimes(1); expect(fn).toHaveBeenCalledWith(expect.any(Element)); diff --git a/plugins/techdocs/src/reader/transformers/injectCss.test.ts b/plugins/techdocs/src/reader/transformers/injectCss.test.ts index 368d077b43..6d0eb8daa9 100644 --- a/plugins/techdocs/src/reader/transformers/injectCss.test.ts +++ b/plugins/techdocs/src/reader/transformers/injectCss.test.ts @@ -15,10 +15,10 @@ */ import { createTestShadowDom } from '../../test-utils'; -import { injectCss } from '../transformers'; +import { injectCss } from './injectCss'; describe('injectCss', () => { - it('should inject style with passed css in head', () => { + it('should inject style with passed css in head', async () => { const html = ` @@ -27,7 +27,7 @@ describe('injectCss', () => { `; const injectedCss = '* {background-color: #fff}'; - const shadowDom = createTestShadowDom(html, { + const shadowDom = await createTestShadowDom(html, { preTransformers: [injectCss({ css: injectedCss })], postTransformers: [], }); diff --git a/plugins/techdocs/src/reader/transformers/onCssReady.test.ts b/plugins/techdocs/src/reader/transformers/onCssReady.test.ts index 3174e23f5c..b2a5aa9760 100644 --- a/plugins/techdocs/src/reader/transformers/onCssReady.test.ts +++ b/plugins/techdocs/src/reader/transformers/onCssReady.test.ts @@ -15,16 +15,15 @@ */ import { - createTestShadowDom, - mockStylesheetEventListener, - executeStylesheetEventListeners, clearStylesheetEventListeners, + createTestShadowDom, + executeStylesheetEventListeners, + mockStylesheetEventListener, } from '../../test-utils'; -import { onCssReady } from '../transformers'; +import { onCssReady } from './onCssReady'; -const docStorageUrl: Promise = Promise.resolve( - 'https://techdocs-mock-sites.storage.googleapis.com', -); +const docStorageUrl: string = + 'https://techdocs-mock-sites.storage.googleapis.com'; const fixture = ` @@ -48,11 +47,11 @@ describe('onCssReady', () => { clearStylesheetEventListeners(); }); - it('does not call onLoading and onLoaded without the onCssReady transformer', () => { + it('does not call onLoading and onLoaded without the onCssReady transformer', async () => { const onLoading = jest.fn(); const onLoaded = jest.fn(); - createTestShadowDom(fixture, { + await createTestShadowDom(fixture, { preTransformers: [], postTransformers: [], }); @@ -62,11 +61,11 @@ describe('onCssReady', () => { expect(onLoaded).not.toHaveBeenCalled(); }); - it('calls the onLoading and onLoaded correctly', () => { + it('calls the onLoading and onLoaded correctly', async () => { const onLoading = jest.fn(); const onLoaded = jest.fn(); - createTestShadowDom(fixture, { + await createTestShadowDom(fixture, { preTransformers: [], postTransformers: [ onCssReady({ diff --git a/plugins/techdocs/src/reader/transformers/onCssReady.ts b/plugins/techdocs/src/reader/transformers/onCssReady.ts index 936c4306a7..e9406ba632 100644 --- a/plugins/techdocs/src/reader/transformers/onCssReady.ts +++ b/plugins/techdocs/src/reader/transformers/onCssReady.ts @@ -17,7 +17,7 @@ import type { Transformer } from './transformer'; type OnCssReadyOptions = { - docStorageUrl: Promise; + docStorageUrl: string; onLoading: (dom: Element) => void; onLoaded: (dom: Element) => void; }; @@ -30,9 +30,7 @@ export const onCssReady = ({ return dom => { const cssPages = Array.from( dom.querySelectorAll('head > link[rel="stylesheet"]'), - ).filter(async elem => - elem.getAttribute('href')?.startsWith(await docStorageUrl), - ); + ).filter(elem => elem.getAttribute('href')?.startsWith(docStorageUrl)); let count = cssPages.length; diff --git a/plugins/techdocs/src/reader/transformers/removeMkdocsHeader.test.ts b/plugins/techdocs/src/reader/transformers/removeMkdocsHeader.test.ts index 70d6b2fa4a..60f0b9ca30 100644 --- a/plugins/techdocs/src/reader/transformers/removeMkdocsHeader.test.ts +++ b/plugins/techdocs/src/reader/transformers/removeMkdocsHeader.test.ts @@ -18,20 +18,26 @@ import { createTestShadowDom, FIXTURES } from '../../test-utils'; import { removeMkdocsHeader } from '../transformers'; describe('removeMkdocsHeader', () => { - it('does not remove mkdocs header', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [], - postTransformers: [], - }); + it('does not remove mkdocs header', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [], + postTransformers: [], + }, + ); expect(shadowDom.querySelector('.md-header')).toBeTruthy(); }); - it('does remove mkdocs header', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [removeMkdocsHeader()], - postTransformers: [], - }); + it('does remove mkdocs header', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [removeMkdocsHeader()], + postTransformers: [], + }, + ); expect(shadowDom.querySelector('.md-header')).toBeFalsy(); }); diff --git a/plugins/techdocs/src/reader/transformers/rewriteDocLinks.test.ts b/plugins/techdocs/src/reader/transformers/rewriteDocLinks.test.ts index 62b80cd514..66fc5f29c1 100644 --- a/plugins/techdocs/src/reader/transformers/rewriteDocLinks.test.ts +++ b/plugins/techdocs/src/reader/transformers/rewriteDocLinks.test.ts @@ -19,8 +19,8 @@ import { rewriteDocLinks } from '../transformers'; import { normalizeUrl } from './rewriteDocLinks'; describe('rewriteDocLinks', () => { - it('should not do anything', () => { - const shadowDom = createTestShadowDom(` + it('should not do anything', async () => { + const shadowDom = await createTestShadowDom(`
Test Test Test @@ -35,8 +35,8 @@ describe('rewriteDocLinks', () => { ]); }); - it('should transform a href with localhost as baseUrl', () => { - const shadowDom = createTestShadowDom( + it('should transform a href with localhost as baseUrl', async () => { + const shadowDom = await createTestShadowDom( ` Test Test @@ -57,9 +57,9 @@ describe('rewriteDocLinks', () => { ]); }); - it('should rewrite non-parseable URLs as text', () => { + it('should rewrite non-parseable URLs as text', async () => { const expectedText = `www.my-internet.[top-level-domain]/pathname/[URLkey]`; - const shadowDom = createTestShadowDom( + const shadowDom = await createTestShadowDom( `${expectedText}`, { preTransformers: [rewriteDocLinks()], diff --git a/plugins/techdocs/src/reader/transformers/sanitizeDOM/index.test.ts b/plugins/techdocs/src/reader/transformers/sanitizeDOM.test.ts similarity index 60% rename from plugins/techdocs/src/reader/transformers/sanitizeDOM/index.test.ts rename to plugins/techdocs/src/reader/transformers/sanitizeDOM.test.ts index 8456cf7c23..8e5b245941 100644 --- a/plugins/techdocs/src/reader/transformers/sanitizeDOM/index.test.ts +++ b/plugins/techdocs/src/reader/transformers/sanitizeDOM.test.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { createTestShadowDom, FIXTURES } from '../../../test-utils'; -import { Transformer } from '../index'; -import { sanitizeDOM } from '../sanitizeDOM'; +import { createTestShadowDom, FIXTURES } from '../../test-utils'; +import { Transformer } from './index'; +import { sanitizeDOM } from './sanitizeDOM'; const injectMaliciousLink = (): Transformer => dom => { const link = document.createElement('a'); @@ -27,55 +27,64 @@ const injectMaliciousLink = (): Transformer => dom => { }; describe('sanitizeDOM', () => { - it('contains a script tag', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE); + it('contains a script tag', async () => { + const shadowDom = await createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE); expect(shadowDom.querySelectorAll('script').length).toBeGreaterThan(0); }); - it('does not contain a script tag', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }); + it('does not contain a script tag', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [sanitizeDOM()], + postTransformers: [], + }, + ); expect(shadowDom.querySelectorAll('script').length).toBe(0); }); - it('contains link with a onClick attribute', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [injectMaliciousLink()], - postTransformers: [], - }); + it('contains link with a onClick attribute', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [injectMaliciousLink()], + postTransformers: [], + }, + ); expect( shadowDom.querySelector('#test-malicious-link')?.hasAttribute('onclick'), ).toBeTruthy(); }); - it('does not contain link with a onClick attribute', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }); + it('does not contain link with a onClick attribute', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [sanitizeDOM()], + postTransformers: [], + }, + ); expect( shadowDom.querySelector('#test-malicious-link')?.hasAttribute('onclick'), ).toBeFalsy(); }); - it('removes style tags', () => { + it('removes style tags', async () => { const html = ` - `; - const shadowDom = createTestShadowDom(html, { + const shadowDom = await createTestShadowDom(html, { preTransformers: [sanitizeDOM()], postTransformers: [], }); @@ -83,7 +92,7 @@ describe('sanitizeDOM', () => { expect(shadowDom.querySelectorAll('style').length).toEqual(0); }); - it('does not remove link tags', () => { + it('does not remove link tags', async () => { const html = ` @@ -94,7 +103,7 @@ describe('sanitizeDOM', () => { `; - const shadowDom = createTestShadowDom(html, { + const shadowDom = await createTestShadowDom(html, { preTransformers: [sanitizeDOM()], postTransformers: [], }); diff --git a/plugins/techdocs/src/reader/transformers/sanitizeDOM/index.ts b/plugins/techdocs/src/reader/transformers/sanitizeDOM.ts similarity index 54% rename from plugins/techdocs/src/reader/transformers/sanitizeDOM/index.ts rename to plugins/techdocs/src/reader/transformers/sanitizeDOM.ts index 65db98ae01..84dea567a2 100644 --- a/plugins/techdocs/src/reader/transformers/sanitizeDOM/index.ts +++ b/plugins/techdocs/src/reader/transformers/sanitizeDOM.ts @@ -14,23 +14,16 @@ * limitations under the License. */ -// @ts-ignore -import sanitizeHtml from 'sanitize-html'; -import type { Transformer } from '../transformer'; -import { TECHDOCS_ALLOWED_TAGS } from './tags'; -import { TECHDOCS_ALLOWED_ATTRIBUTES } from './attributes'; - -// TODO(freben): move all of this out of index +import DOMPurify from 'dompurify'; +import type { Transformer } from './transformer'; export const sanitizeDOM = (): Transformer => { return dom => { - const sanitizedHtml = sanitizeHtml(dom.innerHTML, { - allowedTags: TECHDOCS_ALLOWED_TAGS, - allowedAttributes: TECHDOCS_ALLOWED_ATTRIBUTES, - allowedSchemes: ['http', 'https', 'ftp', 'mailto', 'data', 'blob'], + return DOMPurify.sanitize(dom.innerHTML, { + ADD_TAGS: ['link'], + FORBID_TAGS: ['style'], + WHOLE_DOCUMENT: true, + RETURN_DOM: true, }); - - return new DOMParser().parseFromString(sanitizedHtml, 'text/html') - .documentElement; }; }; diff --git a/plugins/techdocs/src/reader/transformers/sanitizeDOM/attributes.ts b/plugins/techdocs/src/reader/transformers/sanitizeDOM/attributes.ts deleted file mode 100644 index 39fb528da9..0000000000 --- a/plugins/techdocs/src/reader/transformers/sanitizeDOM/attributes.ts +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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. - */ - -/** - * This is the source of truth of what attributes we support in TechDocs. - */ - -export const html = [ - 'accept', - 'action', - 'align', - 'alt', - 'autocapitalize', - 'autocomplete', - 'autopictureinpicture', - 'autoplay', - 'background', - 'bgcolor', - 'border', - 'capture', - 'cellpadding', - 'cellspacing', - 'checked', - 'cite', - 'class', - 'clear', - 'color', - 'cols', - 'colspan', - 'controls', - 'controlslist', - 'coords', - 'crossorigin', - 'datetime', - 'decoding', - 'default', - 'dir', - 'disabled', - 'disablepictureinpicture', - 'disableremoteplayback', - 'download', - 'draggable', - 'enctype', - 'enterkeyhint', - 'face', - 'for', - 'headers', - 'height', - 'hidden', - 'high', - 'href', - 'hreflang', - 'id', - 'inputmode', - 'integrity', - 'ismap', - 'kind', - 'label', - 'lang', - 'list', - 'loading', - 'loop', - 'low', - 'max', - 'maxlength', - 'media', - 'method', - 'min', - 'minlength', - 'multiple', - 'muted', - 'name', - 'noshade', - 'novalidate', - 'nowrap', - 'open', - 'optimum', - 'pattern', - 'placeholder', - 'playsinline', - 'poster', - 'preload', - 'pubdate', - 'radiogroup', - 'readonly', - 'rel', - 'required', - 'rev', - 'reversed', - 'role', - 'rows', - 'rowspan', - 'spellcheck', - 'scope', - 'selected', - 'shape', - 'size', - 'sizes', - 'span', - 'srclang', - 'start', - 'src', - 'srcset', - 'step', - 'style', - 'summary', - 'tabindex', - 'title', - 'translate', - 'type', - 'usemap', - 'valign', - 'value', - 'width', - 'xmlns', -]; - -export const svg = [ - 'accent-height', - 'accumulate', - 'additive', - 'alignment-baseline', - 'ascent', - 'attributename', - 'attributetype', - 'azimuth', - 'basefrequency', - 'baseline-shift', - 'begin', - 'bias', - 'by', - 'class', - 'clip', - 'clip-path', - 'clip-rule', - 'color', - 'color-interpolation', - 'color-interpolation-filters', - 'color-profile', - 'color-rendering', - 'cx', - 'cy', - 'd', - 'dx', - 'dy', - 'data', - 'diffuseconstant', - 'direction', - 'display', - 'divisor', - 'dur', - 'edgemode', - 'elevation', - 'end', - 'fill', - 'fill-opacity', - 'fill-rule', - 'filter', - 'filterunits', - 'flood-color', - 'flood-opacity', - 'font-family', - 'font-size', - 'font-size-adjust', - 'font-stretch', - 'font-style', - 'font-variant', - 'font-weight', - 'fx', - 'fy', - 'g1', - 'g2', - 'glyph-name', - 'glyphref', - 'gradientunits', - 'gradienttransform', - 'height', - 'href', - 'id', - 'image-rendering', - 'in', - 'in2', - 'k', - 'k1', - 'k2', - 'k3', - 'k4', - 'kerning', - 'keypoints', - 'keysplines', - 'keytimes', - 'lang', - 'lengthadjust', - 'letter-spacing', - 'kernelmatrix', - 'kernelunitlength', - 'lighting-color', - 'local', - 'marker-end', - 'marker-mid', - 'marker-start', - 'markerheight', - 'markerunits', - 'markerwidth', - 'maskcontentunits', - 'maskunits', - 'max', - 'mask', - 'media', - 'method', - 'mode', - 'min', - 'name', - 'numoctaves', - 'offset', - 'operator', - 'opacity', - 'order', - 'orient', - 'orientation', - 'origin', - 'overflow', - 'paint-order', - 'path', - 'pathlength', - 'patterncontentunits', - 'patterntransform', - 'patternunits', - 'points', - 'preservealpha', - 'preserveaspectratio', - 'primitiveunits', - 'r', - 'rx', - 'ry', - 'radius', - 'refx', - 'refy', - 'repeatcount', - 'repeatdur', - 'restart', - 'result', - 'rotate', - 'scale', - 'seed', - 'shape-rendering', - 'specularconstant', - 'specularexponent', - 'spreadmethod', - 'startoffset', - 'stddeviation', - 'stitchtiles', - 'stop-color', - 'stop-opacity', - 'stroke-dasharray', - 'stroke-dashoffset', - 'stroke-linecap', - 'stroke-linejoin', - 'stroke-miterlimit', - 'stroke-opacity', - 'stroke', - 'stroke-width', - 'style', - 'surfacescale', - 'tabindex', - 'targetx', - 'targety', - 'transform', - 'text-anchor', - 'text-decoration', - 'text-rendering', - 'textlength', - 'type', - 'u1', - 'u2', - 'unicode', - 'values', - 'viewbox', - 'visibility', - 'version', - 'vert-adv-y', - 'vert-origin-x', - 'vert-origin-y', - 'width', - 'word-spacing', - 'wrap', - 'writing-mode', - 'xchannelselector', - 'ychannelselector', - 'x', - 'x1', - 'x2', - 'xmlns', - 'y', - 'y1', - 'y2', - 'z', - 'zoomandpan', -]; - -export const mathMl = [ - 'accent', - 'accentunder', - 'align', - 'bevelled', - 'close', - 'columnsalign', - 'columnlines', - 'columnspan', - 'denomalign', - 'depth', - 'dir', - 'display', - 'displaystyle', - 'encoding', - 'fence', - 'frame', - 'height', - 'href', - 'id', - 'largeop', - 'length', - 'linethickness', - 'lspace', - 'lquote', - 'mathbackground', - 'mathcolor', - 'mathsize', - 'mathvariant', - 'maxsize', - 'minsize', - 'movablelimits', - 'notation', - 'numalign', - 'open', - 'rowalign', - 'rowlines', - 'rowspacing', - 'rowspan', - 'rspace', - 'rquote', - 'scriptlevel', - 'scriptminsize', - 'scriptsizemultiplier', - 'selection', - 'separator', - 'separators', - 'stretchy', - 'subscriptshift', - 'supscriptshift', - 'symmetric', - 'voffset', - 'width', - 'xmlns', -]; - -export const xml = [ - 'xlink:href', - 'xml:id', - 'xlink:title', - 'xml:space', - 'xmlns:xlink', -]; - -/** - * Anything in here will be supported as HTML attributes from TechDocs. - * - * @note Be conscious about what you add here. It can affect the TechDocs experience. For - * some review before making a PR, reach out in the #docs-like-code channel in Discord first. - */ -export const TECHDOCS_ALLOWED_ATTRIBUTES = { - '*': [...html, ...svg, ...mathMl, ...xml, 'data-*'], -}; diff --git a/plugins/techdocs/src/reader/transformers/sanitizeDOM/tags.ts b/plugins/techdocs/src/reader/transformers/sanitizeDOM/tags.ts deleted file mode 100644 index 780a4c01f7..0000000000 --- a/plugins/techdocs/src/reader/transformers/sanitizeDOM/tags.ts +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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. - */ - -/** - * This is the source of truth for what HTML tags we support in TechDocs. - */ - -// prettier-ignore -export const html = [ - 'a', 'abbr', 'acronym', - 'address', 'area', 'article', - 'aside', 'audio', 'b', 'bdi', - 'bdo', 'big', - 'blink', 'blockquote', - 'body', - 'br', 'button', - 'canvas', 'caption', 'center', 'cite', 'code', - 'col', 'colgroup', - 'content', - 'data', - 'datalist', - 'dd', - 'decorator', - 'del', - 'details', - 'dfn', - 'dir', - 'div', - 'dl', - 'dt', - 'element', - 'em', - 'fieldset', - 'figcaption', - 'figure', - 'font', - 'footer', - 'form', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'head', - 'header', - 'hgroup', - 'hr', - 'html', - 'i', - 'img', - 'input', - 'ins', - 'kbd', - 'label', - 'legend', - 'li', - 'link', - 'main', - 'map', - 'mark', - 'marquee', - 'menu', - 'menuitem', - 'meter', - 'nav', - 'nobr', - 'ol', - 'optgroup', - 'option', - 'output', - 'p', - 'picture', - 'pre', - 'progress', - 'q', - 'rp', - 'rt', - 'ruby', - 's', - 'samp', - 'section', - 'select', - 'shadow', - 'small', - 'source', - 'spacer', - 'span', - 'strike', - 'strong', - 'sub', - 'summary', - 'sup', - 'table', - 'tbody', - 'td', - 'template', - 'textarea', - 'tfoot', - 'th', - 'thead', - 'time', - 'tr', - 'track', - 'tt', - 'u', - 'ul', - 'var', - 'video', - 'wbr', -]; - -// SVG -export const svg = [ - 'svg', - 'a', - 'altglyph', - 'altglyphdef', - 'altglyphitem', - 'animatecolor', - 'animatemotion', - 'animatetransform', - 'audio', - 'canvas', - 'circle', - 'clippath', - 'defs', - 'desc', - 'ellipse', - 'filter', - 'font', - 'g', - 'glyph', - 'glyphref', - 'hkern', - 'image', - 'line', - 'lineargradient', - 'marker', - 'mask', - 'metadata', - 'mpath', - 'path', - 'pattern', - 'polygon', - 'polyline', - 'radialgradient', - 'rect', - 'stop', - 'switch', - 'symbol', - 'text', - 'textpath', - 'title', - 'tref', - 'tspan', - 'video', - 'view', - 'vkern', -]; - -export const svgFilters = [ - 'feBlend', - 'feColorMatrix', - 'feComponentTransfer', - 'feComposite', - 'feConvolveMatrix', - 'feDiffuseLighting', - 'feDisplacementMap', - 'feDistantLight', - 'feFlood', - 'feFuncA', - 'feFuncB', - 'feFuncG', - 'feFuncR', - 'feGaussianBlur', - 'feMerge', - 'feMergeNode', - 'feMorphology', - 'feOffset', - 'fePointLight', - 'feSpecularLighting', - 'feSpotLight', - 'feTile', - 'feTurbulence', -]; - -export const mathMl = [ - 'math', - 'menclose', - 'merror', - 'mfenced', - 'mfrac', - 'mglyph', - 'mi', - 'mlabeledtr', - 'mmultiscripts', - 'mn', - 'mo', - 'mover', - 'mpadded', - 'mphantom', - 'mroot', - 'mrow', - 'ms', - 'mspace', - 'msqrt', - 'mstyle', - 'msub', - 'msup', - 'msubsup', - 'mtable', - 'mtd', - 'mtext', - 'mtr', - 'munder', - 'munderover', -]; - -export const text = ['#text']; - -/** - * Anything in here will be executed as HTML tags from TechDocs. - * - * @note Be conscious about what you add here. It can affect the TechDocs experience. For - * some review before making a PR, reach out in the #docs-like-code channel in Discord first. - */ -export const TECHDOCS_ALLOWED_TAGS = [ - ...html, - ...svg, - ...svgFilters, - ...mathMl, - ...text, - 'iframe', -]; diff --git a/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts b/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts index dbc3761e80..020befd522 100644 --- a/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts +++ b/plugins/techdocs/src/reader/transformers/simplifyMkdocsFooter.test.ts @@ -18,20 +18,26 @@ import { createTestShadowDom, FIXTURES } from '../../test-utils'; import { simplifyMkdocsFooter } from './simplifyMkdocsFooter'; describe('simplifyMkdocsFooter', () => { - it('does not remove mkdocs copyright', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [], - postTransformers: [], - }); + it('does not remove mkdocs copyright', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [], + postTransformers: [], + }, + ); expect(shadowDom.querySelector('.md-footer-copyright')).toBeTruthy(); }); - it('does remove mkdocs copyright', () => { - const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [simplifyMkdocsFooter()], - postTransformers: [], - }); + it('does remove mkdocs copyright', async () => { + const shadowDom = await createTestShadowDom( + FIXTURES.FIXTURE_STANDARD_PAGE, + { + preTransformers: [simplifyMkdocsFooter()], + postTransformers: [], + }, + ); expect(shadowDom.querySelector('.md-footer-copyright')).toBeFalsy(); }); diff --git a/plugins/techdocs/src/reader/transformers/transformer.ts b/plugins/techdocs/src/reader/transformers/transformer.ts index 7b440befbf..fc52f42b5c 100644 --- a/plugins/techdocs/src/reader/transformers/transformer.ts +++ b/plugins/techdocs/src/reader/transformers/transformer.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -export type Transformer = (dom: Element) => Element; +export type Transformer = (dom: Element) => Element | Promise; -export const transform = ( +export const transform = async ( html: string | Element, transformers: Transformer[], -): Element => { +): Promise => { let dom: Element; if (typeof html === 'string') { @@ -30,9 +30,9 @@ export const transform = ( throw new Error('dom is not a recognized type'); } - transformers.forEach(transformer => { - dom = transformer(dom); - }); + for (const transformer of transformers) { + dom = await transformer(dom); + } return dom; }; diff --git a/plugins/techdocs/src/test-utils/shadowDom.ts b/plugins/techdocs/src/test-utils/shadowDom.ts index 61055a87bd..f8289bf6d9 100644 --- a/plugins/techdocs/src/test-utils/shadowDom.ts +++ b/plugins/techdocs/src/test-utils/shadowDom.ts @@ -22,13 +22,13 @@ export type CreateTestShadowDomOptions = { postTransformers: Transformer[]; }; -export const createTestShadowDom = ( +export const createTestShadowDom = async ( fixture: string, opts: CreateTestShadowDomOptions = { preTransformers: [], postTransformers: [], }, -): ShadowRoot => { +): Promise => { const divElement = document.createElement('div'); divElement.attachShadow({ mode: 'open' }); document.body.appendChild(divElement); @@ -39,7 +39,7 @@ export const createTestShadowDom = ( 'text/html', ).documentElement; if (opts.preTransformers) { - dom = transformer(dom, opts.preTransformers); + dom = await transformer(dom, opts.preTransformers); } // Mount the UI @@ -47,7 +47,7 @@ export const createTestShadowDom = ( // Transformers after the UI is rendered if (opts.postTransformers) { - transformer(dom, opts.postTransformers); + await transformer(dom, opts.postTransformers); } return divElement.shadowRoot!; diff --git a/plugins/techdocs/src/types.ts b/plugins/techdocs/src/types.ts index f6d849b426..dd35e255b6 100644 --- a/plugins/techdocs/src/types.ts +++ b/plugins/techdocs/src/types.ts @@ -14,11 +14,13 @@ * limitations under the License. */ -import { Entity, Location } from '@backstage/catalog-model'; +import { Entity, LocationSpec } from '@backstage/catalog-model'; export type TechDocsMetadata = { site_name: string; site_description: string; }; -export type TechDocsEntityMetadata = Entity & { locationMetadata?: Location }; +export type TechDocsEntityMetadata = Entity & { + locationMetadata?: LocationSpec; +}; diff --git a/plugins/todo-backend/api-report.md b/plugins/todo-backend/api-report.md index 92e133a481..592a912c11 100644 --- a/plugins/todo-backend/api-report.md +++ b/plugins/todo-backend/api-report.md @@ -11,12 +11,21 @@ import { Logger as Logger_2 } from 'winston'; import { ScmIntegrations } from '@backstage/integration'; import { UrlReader } from '@backstage/backend-common'; +// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-forgotten-export) The symbol "TodoParserOptions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "TodoParser" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createTodoParser" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export function createTodoParser(options?: TodoParserOptions): TodoParser; +// Warning: (ae-missing-release-tag) "ListTodosRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ListTodosRequest = { entity?: EntityName; @@ -32,6 +41,8 @@ export type ListTodosRequest = { }[]; }; +// Warning: (ae-missing-release-tag) "ListTodosResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ListTodosResponse = { items: TodoItem[]; @@ -40,16 +51,22 @@ export type ListTodosResponse = { limit: number; }; +// Warning: (ae-missing-release-tag) "ReadTodosOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ReadTodosOptions = { url: string; }; +// Warning: (ae-missing-release-tag) "ReadTodosResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type ReadTodosResult = { items: TodoItem[]; }; +// Warning: (ae-missing-release-tag) "TodoItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type TodoItem = { text: string; @@ -60,13 +77,18 @@ export type TodoItem = { repoFilePath?: string; }; +// Warning: (ae-missing-release-tag) "TodoReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TodoReader { readTodos(options: ReadTodosOptions): Promise; } +// Warning: (ae-missing-release-tag) "TodoReaderService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class TodoReaderService implements TodoService { + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts constructor(options: Options_2); // (undocumented) listTodos( @@ -77,9 +99,13 @@ export class TodoReaderService implements TodoService { ): Promise; } +// Warning: (ae-missing-release-tag) "TodoScmReader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export class TodoScmReader implements TodoReader { constructor(options: Options); + // Warning: (ae-forgotten-export) The symbol "Options" needs to be exported by the entry point index.d.ts + // // (undocumented) static fromConfig( config: Config, @@ -89,6 +115,8 @@ export class TodoScmReader implements TodoReader { readTodos({ url }: ReadTodosOptions): Promise; } +// Warning: (ae-missing-release-tag) "TodoService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export interface TodoService { // (undocumented) @@ -100,5 +128,9 @@ export interface TodoService { ): Promise; } +// Warnings were encountered during analysis: +// +// src/service/types.d.ts:9:9 - (ae-forgotten-export) The symbol "Fields" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index e8a079880d..ef5ad352f4 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-todo +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/todo/api-report.md b/plugins/todo/api-report.md index 1fc8e8f99f..548d55accb 100644 --- a/plugins/todo/api-report.md +++ b/plugins/todo/api-report.md @@ -9,12 +9,19 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +// Warning: (ae-missing-release-tag) "EntityTodoContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const EntityTodoContent: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "TodoApi" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "todoApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const todoApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "todoPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const todoPlugin: BackstagePlugin<{}, {}>; diff --git a/plugins/todo/package.json b/plugins/todo/package.json index df719ef10e..ece2509882 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-todo", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,10 +27,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.0", - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.3.0", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,10 +40,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 95344dd0a5..94324eed10 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-user-settings +## 0.3.1 + +### Patch Changes + +- b5953c1df: Aligns switch left and allows clicking on rows +- Updated dependencies + - @backstage/core-components@0.1.6 + +## 0.3.0 + +### Minor Changes + +- 71afed7f3: Exported and renamed components from the `@backstage/plugin-user-settings` plugin , to be able to use it in the consumer side and customize the `SettingPage` + ## 0.2.12 ### Patch Changes diff --git a/plugins/user-settings/api-report.md b/plugins/user-settings/api-report.md index 2f3b9b2fed..af542e22d9 100644 --- a/plugins/user-settings/api-report.md +++ b/plugins/user-settings/api-report.md @@ -12,11 +12,17 @@ import { ProfileInfo } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; import { SessionApi } from '@backstage/core-plugin-api'; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "DefaultProviderSettings" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const DefaultProviderSettings: ({ configuredProviders, }: Props_3) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ProviderSettingsItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const ProviderSettingsItem: ({ title, @@ -25,29 +31,47 @@ export const ProviderSettingsItem: ({ apiRef, }: Props_4) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SettingsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Router: ({ providerSettings }: Props) => JSX.Element; +// Warning: (ae-missing-release-tag) "Settings" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const Settings: () => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsAppearanceCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsAppearanceCard: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "UserSettingsAuthProviders" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsAuthProviders: ({ providerSettings, }: Props_2) => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsFeatureFlags" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsFeatureFlags: () => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsGeneral" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsGeneral: () => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsMenu" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsMenu: () => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsPage: ({ providerSettings, @@ -55,9 +79,13 @@ export const UserSettingsPage: ({ providerSettings?: JSX.Element | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsPinToggle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsPinToggle: () => JSX.Element; +// Warning: (ae-missing-release-tag) "userSettingsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const userSettingsPlugin: BackstagePlugin< { @@ -68,15 +96,24 @@ const userSettingsPlugin: BackstagePlugin< export { userSettingsPlugin as plugin }; export { userSettingsPlugin }; +// Warning: (ae-missing-release-tag) "UserSettingsProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsProfileCard: () => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "UserSettingsSignInAvatar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsSignInAvatar: ({ size }: Props_5) => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsThemeToggle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const UserSettingsThemeToggle: () => JSX.Element; +// Warning: (ae-missing-release-tag) "useUserProfile" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const useUserProfile: () => { profile: ProfileInfo; diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 941116b0bb..7a450a188e 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.2.12", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -42,11 +42,11 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx b/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx index 62861d99a9..e97b33cd92 100644 --- a/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx +++ b/plugins/user-settings/src/components/FeatureFlags/FeatureFlagsItem.tsx @@ -17,8 +17,8 @@ import React from 'react'; import { ListItem, - ListItemSecondaryAction, ListItemText, + ListItemIcon, Switch, Tooltip, } from '@material-ui/core'; @@ -31,20 +31,15 @@ type Props = { }; export const FlagItem = ({ flag, enabled, toggleHandler }: Props) => ( - + toggleHandler(flag.name)}> + + + + + - - - toggleHandler(flag.name)} - name={flag.name} - /> - - ); diff --git a/plugins/welcome/api-report.md b/plugins/welcome/api-report.md index 49ccd46994..f3096482de 100644 --- a/plugins/welcome/api-report.md +++ b/plugins/welcome/api-report.md @@ -7,9 +7,13 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "WelcomePage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const WelcomePage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "welcomePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) const welcomePlugin: BackstagePlugin<{}, {}>; export { welcomePlugin as plugin }; diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index a04d3cf9dd..68cb992e40 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -42,10 +42,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/plugins/xcmetrics/api-report.md b/plugins/xcmetrics/api-report.md index 3509fec523..9eb9a16f8b 100644 --- a/plugins/xcmetrics/api-report.md +++ b/plugins/xcmetrics/api-report.md @@ -8,9 +8,13 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; +// Warning: (ae-missing-release-tag) "XcmetricsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const XcmetricsPage: () => JSX.Element; +// Warning: (ae-missing-release-tag) "xcmetricsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export const xcmetricsPlugin: BackstagePlugin< { diff --git a/plugins/xcmetrics/docs/XCMetrics-overview.png b/plugins/xcmetrics/docs/XCMetrics-overview.png index b0b7dc6153..69b77f87df 100644 Binary files a/plugins/xcmetrics/docs/XCMetrics-overview.png and b/plugins/xcmetrics/docs/XCMetrics-overview.png differ diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index e617d12e85..b939c3e5bf 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.5", + "@backstage/core-components": "^0.1.6", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", "@backstage/theme": "^0.2.8", @@ -33,10 +33,10 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.7.3", - "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.1", - "@backstage/test-utils": "^0.1.14", + "@backstage/cli": "^0.7.5", + "@backstage/core-app-api": "^0.1.5", + "@backstage/dev-utils": "^0.2.3", + "@backstage/test-utils": "^0.1.15", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.8", diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index f88b30d1fd..21c61599a4 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -150,16 +150,11 @@ async function runApiExtraction({ for (const packageDir of packageDirs) { console.log(`## Processing ${packageDir}`); const projectFolder = resolvePath(__dirname, '..', packageDir); - const packagePath = resolvePath(__dirname, `../${packageDir}/package.json`); + const packageFolder = resolvePath(__dirname, '../dist-types', packageDir); const extractorConfig = ExtractorConfig.prepare({ configObject: { - mainEntryPointFilePath: resolvePath( - __dirname, - '../dist-types', - packageDir, - 'src/index.d.ts', - ), + mainEntryPointFilePath: resolvePath(packageFolder, 'src/index.d.ts'), bundledPackages: [], compiler: { @@ -190,7 +185,7 @@ async function runApiExtraction({ }, messages: { - // Silence warnings, as these will prevent the CI build to work + // Silence compiler warnings, as these will prevent the CI build to work compilerMessageReporting: { default: { logLevel: 'none' as ExtractorLogLevel.None, @@ -200,14 +195,14 @@ async function runApiExtraction({ }, extractorMessageReporting: { default: { - logLevel: 'none' as ExtractorLogLevel.Warning, - // addToApiReportFile: true, + logLevel: 'warning' as ExtractorLogLevel.Warning, + addToApiReportFile: true, }, }, tsdocMessageReporting: { default: { - logLevel: 'none' as ExtractorLogLevel.Warning, - // addToApiReportFile: true, + logLevel: 'warning' as ExtractorLogLevel.Warning, + addToApiReportFile: true, }, }, }, @@ -217,9 +212,16 @@ async function runApiExtraction({ projectFolder, }, configObjectFullPath: projectFolder, - packageJsonFullPath: packagePath, + packageJsonFullPath: resolvePath(projectFolder, 'package.json'), }); + // The `packageFolder` needs to point to the location within `dist-types` in order for relative + // paths to be logged. Unfortunately the `prepare` method above derives it from the `packageJsonFullPath`, + // which needs to point to the actual file, so we override `packageFolder` afterwards. + (extractorConfig as { + packageFolder: string; + }).packageFolder = packageFolder; + if (!compilerState) { compilerState = CompilerState.create(extractorConfig, { additionalEntryPoints: entryPoints, diff --git a/yarn.lock b/yarn.lock index a8e6f4a167..c2dde8f4c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -129,6 +129,26 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.0.0" +"@azure/core-auth@^1.3.0": + version "1.3.2" + resolved "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz#6a2c248576c26df365f6c7881ca04b7f6d08e3d0" + integrity sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + +"@azure/core-client@^1.0.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@azure/core-client/-/core-client-1.2.2.tgz#29c781e5ccd4da968cea89d3b909ab573a3a7bec" + integrity sha512-VYFR2qiczjBrSfpQSbo5s8FJhXaJFz2tP01MOrpNJaOqnSNEKcY35I79b1Ty7s8qHGvc5/YMJ745l3B7abncFQ== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-asynciterator-polyfill" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-rest-pipeline" "^1.1.0" + "@azure/core-tracing" "1.0.0-preview.12" + tslib "^2.2.0" + "@azure/core-http@^1.2.0": version "1.2.2" resolved "https://registry.npmjs.org/@azure/core-http/-/core-http-1.2.2.tgz#a6f7717184fd2657d3acabd1d64dfdc0bd531ce3" @@ -167,6 +187,38 @@ dependencies: "@azure/core-asynciterator-polyfill" "^1.0.0" +"@azure/core-rest-pipeline@^1.1.0": + version "1.1.1" + resolved "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.1.1.tgz#53b4278f5c417c3da9f133511064375362b68a12" + integrity sha512-ObF8iTEDXIG7/NlL28ni9bR3XLJwgm2S3GWO4aNW6CsTCFVoY9HMdbBtN7xOB+pUQwifehifXNnootbzzuwJnw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-tracing" "1.0.0-preview.12" + "@azure/logger" "^1.0.0" + form-data "^3.0.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + tslib "^2.2.0" + uuid "^8.3.0" + +"@azure/core-tracing@1.0.0-preview.10": + version "1.0.0-preview.10" + resolved "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.10.tgz#e7060272145dddad4486765030d1b037cd52a8ea" + integrity sha512-iIwjtMwQnsxB7cYkugMx+s4W1nfy3+pT/ceo+uW1fv4YDgYe84nh+QP0fEC9IH/3UATLSWbIBemdMHzk2APUrw== + dependencies: + "@opencensus/web-types" "0.0.7" + "@opentelemetry/api" "^0.10.2" + tslib "^2.0.0" + +"@azure/core-tracing@1.0.0-preview.12": + version "1.0.0-preview.12" + resolved "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.12.tgz#f53ff452c0742ad981c244f97d93d37ca2b5e139" + integrity sha512-nvo2Wc4EKZGN6eFu9n3U7OXmASmL8VxoPIH7xaD6OlQqi44bouF0YIi9ID5rEsKLiAU59IYx6M297nqWVMWPDg== + dependencies: + "@opentelemetry/api" "^1.0.0" + tslib "^2.2.0" + "@azure/core-tracing@1.0.0-preview.9": version "1.0.0-preview.9" resolved "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz#84f3b85572013f9d9b85e1e5d89787aa180787eb" @@ -176,26 +228,29 @@ "@opentelemetry/api" "^0.10.2" tslib "^2.0.0" -"@azure/identity@^1.2.2": - version "1.2.2" - resolved "https://registry.npmjs.org/@azure/identity/-/identity-1.2.2.tgz#00d673c8881778c55777fcc76e822b42466d3fc7" - integrity sha512-aYkeNXl52aEHW1iOZQJb3SC7Vvbu87f01iNT+pSVHwj09LpN9+gP/Lb9uoWy36Fgv9WlukM55LbjLSbb1Renqw== +"@azure/identity@^1.5.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@azure/identity/-/identity-1.5.0.tgz#0ac832b95adaac00b4718d92b43b2c9c5ab42d2d" + integrity sha512-djgywuWtX6720seqNOPmGM1hY54oHnjRT0MLIOzacMARTZuEtAIaFFvMPBlUIMQdtSGhdjH+/MS1/9PE8j83eA== dependencies: - "@azure/core-http" "^1.2.0" - "@azure/core-tracing" "1.0.0-preview.9" + "@azure/core-auth" "^1.3.0" + "@azure/core-client" "^1.0.0" + "@azure/core-rest-pipeline" "^1.1.0" + "@azure/core-tracing" "1.0.0-preview.12" "@azure/logger" "^1.0.0" - "@azure/msal-node" "1.0.0-beta.3" - "@opentelemetry/api" "^0.10.2" + "@azure/msal-node" "1.0.0-beta.6" + "@types/stoppable" "^1.1.0" axios "^0.21.1" events "^3.0.0" jws "^4.0.0" msal "^1.0.2" open "^7.0.0" qs "^6.7.0" + stoppable "^1.1.0" tslib "^2.0.0" uuid "^8.3.0" optionalDependencies: - keytar "^5.4.0" + keytar "^7.3.0" "@azure/logger@^1.0.0": version "1.0.1" @@ -204,50 +259,43 @@ dependencies: tslib "^2.0.0" -"@azure/msal-common@^2.1.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-2.1.0.tgz#a4bc17e254d6ec524016f13267947dd4ff4a624d" - integrity sha512-Y1Id+jG59S3eY2ZQQtUA/lxwbRcgjcWaiib9YX+SwV3zeRauKfEiZT7l3z+lwV+T+Sst20F6l1mJsfQcfE7CEQ== +"@azure/msal-common@^4.0.0", "@azure/msal-common@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-4.4.0.tgz#818526042f78838ebc332fb735e7de64d8bccb45" + integrity sha512-Qrs33Ctt2KM7NxArFPIUKc8UbIcm7zYxJFdJeQ9k7HKBhVk3e88CUz1Mw33cS/Jr+YA1H02OAzHg++bJ+4SFyQ== dependencies: debug "^4.1.1" -"@azure/msal-common@^4.3.0": - version "4.3.0" - resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-4.3.0.tgz#b540e92748656724088bf77192e59943a93135bc" - integrity sha512-jFqUWe83wVb6O8cNGGBFg2QlKvqM1ezUgJTEV7kIsAPX0RXhGFE4B1DLNt6hCnkTXDbw+KGW0zgxOEr4MJQwLw== +"@azure/msal-node@1.0.0-beta.6": + version "1.0.0-beta.6" + resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.0.0-beta.6.tgz#da6bc3a3a861057c85586055960e069f162548ee" + integrity sha512-ZQI11Uz1j0HJohb9JZLRD8z0moVcPks1AFW4Q/Gcl67+QvH4aKEJti7fjCcipEEZYb/qzLSO8U6IZgPYytsiJQ== dependencies: - debug "^4.1.1" - -"@azure/msal-node@1.0.0-beta.3": - version "1.0.0-beta.3" - resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.0.0-beta.3.tgz#c84c7948028b39e48b901f5fac35bdedcbc8772e" - integrity sha512-/KfYRfrsOIrZONvo/0Vi5umuqbPBtCWNtmRvkse64uI0C4CP/W4WXwRD42VMws/8LtKvr1I5rYlYgFzt5zDz/A== - dependencies: - "@azure/msal-common" "^2.1.0" + "@azure/msal-common" "^4.0.0" axios "^0.21.1" jsonwebtoken "^8.5.1" uuid "^8.3.0" "@azure/msal-node@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.1.0.tgz#e472cfadead169f8832066ae6c2d6b8eef4e89e4" - integrity sha512-gMO9aZdWOzufp1PcdD5ID25DdS9eInxgeCqx4Tk8PVU6Z7RxJQhoMzS64cJhGdpYgeIQwKljtF0CLCcPFxew/w== + version "1.2.0" + resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.2.0.tgz#d08a5fb3391436715cb37c6eb44816013bdfa11e" + integrity sha512-79o5n483vslc7Qegh9+0BsxODRmlk6YYjVdl9jvwmAuF+i+oylq57e7RVhTVocKCbLCIMOKARI14JyKdDbW0WA== dependencies: - "@azure/msal-common" "^4.3.0" + "@azure/msal-common" "^4.4.0" axios "^0.21.1" jsonwebtoken "^8.5.1" uuid "^8.3.0" -"@azure/storage-blob@^12.4.0": - version "12.4.0" - resolved "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.4.0.tgz#7127ddd9f413105e2c3688691bc4c6245d0806b3" - integrity sha512-OnhVSoKD1HzBB79/rFzPbC4w9TdzFXeoOwkX+aIu3rb8qvN0VaqvUqZXSrBCyG2LcLyVkY4MPCJQBrmEUm9kvw== +"@azure/storage-blob@^12.5.0": + version "12.5.0" + resolved "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.5.0.tgz#1ddd8837d9a15ebe355e795375d13b406f2cb496" + integrity sha512-DgoefgODst2IPkkQsNdhtYdyJgSsAZC1pEujO6aD5y7uFy5GnzhYliobSrp204jYRyK5XeJ9iiePmy/SPtTbLA== dependencies: "@azure/abort-controller" "^1.0.0" "@azure/core-http" "^1.2.0" "@azure/core-lro" "^1.0.2" "@azure/core-paging" "^1.1.1" - "@azure/core-tracing" "1.0.0-preview.9" + "@azure/core-tracing" "1.0.0-preview.10" "@azure/logger" "^1.0.0" "@opentelemetry/api" "^0.10.2" events "^3.0.0" @@ -1348,19 +1396,6 @@ "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" -"@backstage/catalog-model@^0.8.4": - version "0.9.0" - dependencies: - "@backstage/config" "^0.1.5" - "@backstage/errors" "^0.1.1" - "@types/json-schema" "^7.0.5" - "@types/yup" "^0.29.8" - ajv "^7.0.3" - json-schema "^0.3.0" - lodash "^4.17.15" - uuid "^8.0.0" - yup "^0.29.3" - "@backstage/core-api@^0.2.23": version "0.2.23" resolved "https://registry.npmjs.org/@backstage/core-api/-/core-api-0.2.23.tgz#c0ec13407ff7c78d376eb18e9d8e1490d54d995b" @@ -2417,6 +2452,20 @@ prop-types "^15.6.2" scheduler "^0.19.0" +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" + integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + "@iarna/toml@^2.2.5": version "2.2.5" resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -2659,10 +2708,10 @@ resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== -"@kubernetes/client-node@^0.14.0": - version "0.14.3" - resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.14.3.tgz#5ed9b88873419080547f22cb74eb502bf6671fca" - integrity sha512-9hHGDNm2JEFQcRTpDxVoAVr0fowU+JH/l5atCXY9VXwvFM18pW5wr2LzLP+Q2Rh+uQv7Moz4gEjEKSCgVKykEQ== +"@kubernetes/client-node@^0.15.0": + version "0.15.0" + resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.15.0.tgz#aa5cfcfa9ba3055fe0b510c430d19bbda715d8e7" + integrity sha512-AnEcsWWadl5IWOzzvO/gWpTnJb1d1CzA/rbV/qK1c0fD1SOxTDPj6jFllyQ9icGDfCgNw3TafZftmuepm6z9JA== dependencies: "@types/js-yaml" "^3.12.1" "@types/node" "^10.12.0" @@ -2672,14 +2721,14 @@ "@types/underscore" "^1.8.9" "@types/ws" "^6.0.1" byline "^5.0.0" - execa "1.0.0" + execa "5.0.0" isomorphic-ws "^4.0.1" js-yaml "^3.13.1" jsonpath-plus "^0.19.0" openid-client "^4.1.1" request "^2.88.0" rfc4648 "^1.3.0" - shelljs "^0.8.2" + shelljs "^0.8.4" stream-buffers "^3.0.2" tar "^6.0.2" tmp-promise "^3.0.2" @@ -3899,6 +3948,11 @@ dependencies: "@opentelemetry/context-base" "^0.10.2" +"@opentelemetry/api@^1.0.0": + version "1.0.1" + resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.1.tgz#03c72f548431da5820a0c8864d1401e348e7e79f" + integrity sha512-H5Djcc2txGAINgf3TNaq4yFofYSIK3722PM89S/3R8FuI/eqi1UscajlXk7EBkG9s2pxss/q6SHlpturaavXaw== + "@opentelemetry/context-base@^0.10.2": version "0.10.2" resolved "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.10.2.tgz#55bea904b2b91aa8a8675df9eaba5961bddb1def" @@ -4009,16 +4063,16 @@ resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-3.0.0.tgz#69ece2cb549f0e860b5f89898db90edcc95b15ba" integrity sha512-T2B8QnrDQphbFNxDz7baAa0zTd5TXJmO9soHBPTKKdniRbMEOQ19AJBbZkA3ED2XZa/xrUY/6XjERQLpNACddw== -"@roadiehq/backstage-plugin-buildkite@^1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-1.0.4.tgz#8bd89d235c12b0093dd8c35d5397d8332bb62df0" - integrity sha512-SkPJzkLhVo1vMM76EShJRRQegXFKkY1bf1TMVIqR2Cm3E9QXHyN099+dSimFW8f5uyNioEHuRkSh7Iwm7AZekA== +"@roadiehq/backstage-plugin-buildkite@^1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-1.0.6.tgz#f0ca863d206437d8b14e059a0d04fa37553de5f7" + integrity sha512-poC1yaDnH2Gteim8U3SdvVT1jkk2MtMv7YlCq0QwK08RTKUc5PFO6MaDaz95cJ+lndSjYWYn/yii8UmuqrJMUw== dependencies: - "@backstage/catalog-model" "^0.8.4" + "@backstage/catalog-model" "^0.9.0" "@backstage/core-app-api" "^0.1.3" "@backstage/core-components" "^0.1.3" "@backstage/core-plugin-api" "^0.1.3" - "@backstage/plugin-catalog-react" "^0.2.4" + "@backstage/plugin-catalog-react" "^0.3.0" "@backstage/theme" "^0.2.6" "@material-ui/core" "^4.11.3" "@material-ui/icons" "^4.11.2" @@ -4032,16 +4086,16 @@ react-router-dom "6.0.0-beta.0" react-use "^17.2.4" -"@roadiehq/backstage-plugin-github-insights@^1.1.15": - version "1.1.15" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-1.1.15.tgz#ef033f85c54f5203a2bc161290f46e861dc6feca" - integrity sha512-UH8lvDZd1OVP6fX+mF0eTrxqo/QmGWiCWvxumxkyU61sF3XkXzEqhQDKlWWEtpGLPa++yf98p00wfSeXRfjRWA== +"@roadiehq/backstage-plugin-github-insights@^1.1.20": + version "1.1.20" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-1.1.20.tgz#5e58124b4da2a60c0d4e6563cd94cdd7702fa341" + integrity sha512-bhYe9o1MpIGxSqXa2HK1E8P/uzTL7zJvUleLTp9C5q4CmPCroNTE/Ce8Apzlrb9o9baSQEhQLoPqe0xs+chS7A== dependencies: - "@backstage/catalog-model" "^0.8.4" + "@backstage/catalog-model" "^0.9.0" "@backstage/core-app-api" "^0.1.3" "@backstage/core-components" "^0.1.3" "@backstage/core-plugin-api" "^0.1.3" - "@backstage/plugin-catalog-react" "^0.2.0" + "@backstage/plugin-catalog-react" "^0.3.0" "@backstage/theme" "^0.2.7" "@date-io/core" "2.10.7" "@material-ui/core" "^4.11.0" @@ -4055,16 +4109,16 @@ react-router "^6.0.0-beta.0" react-use "^17.2.4" -"@roadiehq/backstage-plugin-github-pull-requests@^1.0.8": - version "1.0.8" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-1.0.8.tgz#7d6202ec60808156058bd2d335f7df332934ae56" - integrity sha512-g1CudaKX1NYW0wfbtjcZxFWmDMfw1IvpJD4ymmwZmsogBOHFknlowVmwwSL4W4LwK6IPypIZkOe95Vd2PrjO+Q== +"@roadiehq/backstage-plugin-github-pull-requests@^1.0.10": + version "1.0.10" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-1.0.10.tgz#8c921393d25a4d498c196fcab7d6138c5e2998ba" + integrity sha512-8mIhWpxIDZBUrfK72+Pc6g8e/nNo+IA0wbDqWbGO53gw8J2dWSFIU06IRpatN672pzkqrrfzt1qhQujzQslRkg== dependencies: - "@backstage/catalog-model" "^0.8.4" + "@backstage/catalog-model" "^0.9.0" "@backstage/core-app-api" "^0.1.3" "@backstage/core-components" "^0.1.3" "@backstage/core-plugin-api" "^0.1.3" - "@backstage/plugin-catalog-react" "^0.2.0" + "@backstage/plugin-catalog-react" "^0.3.0" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@octokit/rest" "^18.5.3" @@ -4078,16 +4132,16 @@ react-router "6.0.0-beta.0" react-use "^17.2.4" -"@roadiehq/backstage-plugin-travis-ci@^1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-1.0.4.tgz#224d17749b6bb33fbc5bf50489d7e7a87b06aa3a" - integrity sha512-rSMB+ZuI9xXIwgqJv7NW/wt3ulZa9rSLsCzkMO30/6/TMbenmsIyfmIP9XI32D9mPew4dVX9spi6KsgEo41WHw== +"@roadiehq/backstage-plugin-travis-ci@^1.0.8": + version "1.0.8" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-1.0.8.tgz#33c51ed676191190ba3e2d00e5e9f75b6612b767" + integrity sha512-9iLUnjpF4aCyaD1+0B8OBI3k0nSAzWWqnOSM/4NUwoFDuqhKLlMeN+/HM9L38pp6x1wcwks8sMZh8GufFYl2nw== dependencies: - "@backstage/catalog-model" "^0.8.4" + "@backstage/catalog-model" "^0.9.0" "@backstage/core-app-api" "^0.1.3" "@backstage/core-components" "^0.1.3" "@backstage/core-plugin-api" "^0.1.3" - "@backstage/plugin-catalog-react" "^0.2.4" + "@backstage/plugin-catalog-react" "^0.3.0" "@backstage/theme" "^0.2.6" "@material-ui/core" "^4.11.3" "@material-ui/icons" "^4.11.2" @@ -4227,6 +4281,13 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== +"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.8.3": + version "1.8.3" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + "@sinonjs/commons@^1.7.0": version "1.7.1" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" @@ -4241,6 +4302,27 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sinonjs/fake-timers@^7.0.4", "@sinonjs/fake-timers@^7.1.0": + version "7.1.2" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" + integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/samsam@^6.0.2": + version "6.0.2" + resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz#a0117d823260f282c04bff5f8704bdc2ac6910bb" + integrity sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + "@spotify/eslint-config-base@^9.0.0": version "9.0.2" resolved "https://registry.npmjs.org/@spotify/eslint-config-base/-/eslint-config-base-9.0.2.tgz#a4830f610f40de935de795d3def486c3e5064ee4" @@ -4285,20 +4367,20 @@ uuid-browser "^3.1.0" "@storybook/addon-links@^6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.1.11.tgz#84b20b0c13bbfc8270cdf52389ce52f98c415b6a" - integrity sha512-OIBeOdTHNr/egIuyOUEtEBtZJXVp9PEPQ4MDxvKsCgpnXruPhjenPNW+mQKxm4SefpePW7zkx6k1ubpYVJUeag== + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.3.4.tgz#2ec5b7532e67303b7ec693ecc22a6c4feac93cb3" + integrity sha512-qIey6kNcrg43vsmMmEGXGMbO1Wjqc6hbcMaLstUn3xN29vZnjUcv7PVPFlJsBBYIxW7gXBtRmqjdxN5UD0bvZA== dependencies: - "@storybook/addons" "6.1.11" - "@storybook/client-logger" "6.1.11" - "@storybook/core-events" "6.1.11" + "@storybook/addons" "6.3.4" + "@storybook/client-logger" "6.3.4" + "@storybook/core-events" "6.3.4" "@storybook/csf" "0.0.1" - "@storybook/router" "6.1.11" - "@types/qs" "^6.9.0" - core-js "^3.0.1" - global "^4.3.2" + "@storybook/router" "6.3.4" + "@types/qs" "^6.9.5" + core-js "^3.8.2" + global "^4.4.0" prop-types "^15.7.2" - qs "^6.6.0" + qs "^6.10.0" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" @@ -4322,21 +4404,6 @@ react-syntax-highlighter "^13.5.3" regenerator-runtime "^0.13.7" -"@storybook/addons@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.11.tgz#cb4578411ca00ccb206b484df5a171ccaca34719" - integrity sha512-OZXsdmn60dVe482l9zWxzOqqJApD2jggk/8QJKn3/Ub9posmqdqg712bW6v71BBe0UXXG/QfkZA7gcyiyEENbw== - dependencies: - "@storybook/api" "6.1.11" - "@storybook/channels" "6.1.11" - "@storybook/client-logger" "6.1.11" - "@storybook/core-events" "6.1.11" - "@storybook/router" "6.1.11" - "@storybook/theming" "6.1.11" - core-js "^3.0.1" - global "^4.3.2" - regenerator-runtime "^0.13.7" - "@storybook/addons@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.15.tgz#09eb8d962f58bd20b4ac2f83b515831c83226352" @@ -4367,7 +4434,7 @@ global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/addons@6.3.0", "@storybook/addons@^6.1.11": +"@storybook/addons@6.3.0": version "6.3.0" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.0.tgz#a86849f46a654d2d78b91fad0088264a32d4e58e" integrity sha512-/dcq20HtdSw5+cG8znR59Y/uv2zCR2VjRK3N52IkGWk162b/UbSjjL0PhNnnQFGpH9Fruft6mqvjTAKT41kmJw== @@ -4382,30 +4449,20 @@ global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/api@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/api/-/api-6.1.11.tgz#1e0b798203df823ac21184386258cf8b5f17f440" - integrity sha512-/p4QW/p3uWO0AKVveNezX3I/CotyBKaJ5ui8PuvSPsl7yvqcsK41qI4evKOw7GMQn6oP+2enRbzHpGuCUgQSjA== +"@storybook/addons@6.3.4", "@storybook/addons@^6.1.11": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.3.4.tgz#016c5c3e36c78a320eb8b022cf7fe556d81577c2" + integrity sha512-rf8K8X3JrB43gq5nw5SYgfucQkFg2QgUMWdByf7dQ4MyIl5zet+2MYiSXJ9lfbhGKJZ8orc81rmMtiocW4oBjg== dependencies: - "@reach/router" "^1.3.3" - "@storybook/channels" "6.1.11" - "@storybook/client-logger" "6.1.11" - "@storybook/core-events" "6.1.11" - "@storybook/csf" "0.0.1" - "@storybook/router" "6.1.11" - "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.1.11" - "@types/reach__router" "^1.3.5" - core-js "^3.0.1" - fast-deep-equal "^3.1.1" - global "^4.3.2" - lodash "^4.17.15" - memoizerific "^1.11.3" + "@storybook/api" "6.3.4" + "@storybook/channels" "6.3.4" + "@storybook/client-logger" "6.3.4" + "@storybook/core-events" "6.3.4" + "@storybook/router" "6.3.4" + "@storybook/theming" "6.3.4" + core-js "^3.8.2" + global "^4.4.0" regenerator-runtime "^0.13.7" - store2 "^2.7.1" - telejson "^5.0.2" - ts-dedent "^2.0.0" - util-deprecate "^1.0.2" "@storybook/api@6.1.15": version "6.1.15" @@ -4484,6 +4541,32 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/api@6.3.4": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.3.4.tgz#25b8b842104693000b018b3f64986e95fa032b45" + integrity sha512-12q6dvSR4AtyuZbKAy3Xt+ZHzZ4ePPRV1q20xtgYBoiFEgB9vbh4XKEeeZD0yIeTamQ2x1Hn87R79Rs1GIdKRQ== + dependencies: + "@reach/router" "^1.3.4" + "@storybook/channels" "6.3.4" + "@storybook/client-logger" "6.3.4" + "@storybook/core-events" "6.3.4" + "@storybook/csf" "0.0.1" + "@storybook/router" "6.3.4" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.3.4" + "@types/reach__router" "^1.3.7" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^5.3.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/channel-postmessage@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.1.15.tgz#80ea2346d18496f9710dd7f87fd2a9eca46ef36f" @@ -4510,15 +4593,6 @@ qs "^6.10.0" telejson "^5.3.2" -"@storybook/channels@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.1.11.tgz#a93a83746ad78dd40e1c056029f6d93b17bb66bc" - integrity sha512-NvjWzQ95TSV1e18jaQBCOGoe+vptKH2NOKZ7QRQ7I0O5OoHKr47IXoh+MQ5C8CRD9FTdLE/xWdn1sVVEPRyHEw== - dependencies: - core-js "^3.0.1" - ts-dedent "^2.0.0" - util-deprecate "^1.0.2" - "@storybook/channels@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.1.15.tgz#22bb06a671a5ae09d2537bcf63aaf90d7f6b9f6b" @@ -4546,6 +4620,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/channels@6.3.4": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.3.4.tgz#425b31a67e42ac66ccb03465e4ba2e2ef9c8344b" + integrity sha512-zdZzBbIu9JHEe+uw8FqKsNUiFY+iqI9QdHH/pM3DTTQpBN/JM1Xwfo3CkqA8c5PkhSGqpW0YjXoPash4lawr1Q== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-api@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.1.15.tgz#8f8ead111459b94621571bdb2276f8a0aace17b1" @@ -4594,14 +4677,6 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.11.tgz#5dd092e4293e5f58f7e89ddbc6eb2511b7d60954" - integrity sha512-dSc+VKLW1UaiMPMhlZYRqhynrrHdHFiBEgU28+8LcmoZ1yhZBwLkcKdSD4YTT0CbMJAG1/+NUW5kRI8Geeg+rA== - dependencies: - core-js "^3.0.1" - global "^4.3.2" - "@storybook/client-logger@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.15.tgz#b558d6ecbee82c038d684717d8c598eaa4a9324d" @@ -4626,6 +4701,14 @@ core-js "^3.8.2" global "^4.4.0" +"@storybook/client-logger@6.3.4": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.3.4.tgz#c7ee70463c48bb3af704165d5456351ebb667fc2" + integrity sha512-Gu4M5bBHHQznsdoj8uzYymeojwWq+CRNsUUH41BQIND/RJYSX1IYGIj0yNBP449nv2pjHcTGlN8NJDd+PcELCQ== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + "@storybook/components@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/components/-/components-6.1.15.tgz#b4a2af23ee6b9cba4c255191eae3d3463e29bfb7" @@ -4712,13 +4795,6 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/core-events@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.1.11.tgz#d50e8ec90490f9a7180a8c8a83afb6dcfe47ed66" - integrity sha512-hTib81W8PxepM7iXVvl3pBXSaGpChl5LTzaLCoIRO9sSB8dy0/x2DLAHzbQvShk/l1wqUc3TtOLIxq+eC9l3wg== - dependencies: - core-js "^3.0.1" - "@storybook/core-events@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.1.15.tgz#f66e30cbed8afdb8df2254d2aa47fe139e641c60" @@ -4740,6 +4816,13 @@ dependencies: core-js "^3.8.2" +"@storybook/core-events@6.3.4": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.3.4.tgz#f841b8659a8729d334acd9a6dcfc470c88a2be8f" + integrity sha512-6qI5bU5VcAoRfxkvpdRqO16eYrX5M0P2E3TakqUUDcgDo5Rfcwd1wTTcwiXslMIh7oiVGiisA+msKTlfzyKf9Q== + dependencies: + core-js "^3.8.2" + "@storybook/core@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/core/-/core-6.1.15.tgz#7ff8c314d3857497bf2e26c69a1fa93ef37301aa" @@ -4892,18 +4975,6 @@ ts-dedent "^2.0.0" webpack "^4.44.2" -"@storybook/router@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/router/-/router-6.1.11.tgz#d58e0c8324d8b16d71e09c017a4e3c844b1a4139" - integrity sha512-YEYOoKMo/WI13MZCkdqI9X3H1G0Oj5OUxi7So4qd3khX3zcCjSr3LjiMDBcmIVZpFo5VAvzjhIY4KqpgvzTG0A== - dependencies: - "@reach/router" "^1.3.3" - "@types/reach__router" "^1.3.5" - core-js "^3.0.1" - global "^4.3.2" - memoizerific "^1.11.3" - qs "^6.6.0" - "@storybook/router@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/router/-/router-6.1.15.tgz#e0cd7440a2ddc9b265e506b1cb590d3eeab56476" @@ -4948,6 +5019,22 @@ qs "^6.10.0" ts-dedent "^2.0.0" +"@storybook/router@6.3.4": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.3.4.tgz#f38ec8064a9d1811a68558390727c30220fe7d72" + integrity sha512-cNG2bT0BBfqJyaW6xKUnEB/XXSdMkYeI9ShwJ2gh/2Bnidm7eZ/RKUOZ4q5equMm+SxxyZgpBulqnFN+TqPbOA== + dependencies: + "@reach/router" "^1.3.4" + "@storybook/client-logger" "6.3.4" + "@types/reach__router" "^1.3.7" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.20" + memoizerific "^1.11.3" + qs "^6.10.0" + ts-dedent "^2.0.0" + "@storybook/semver@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" @@ -4972,24 +5059,6 @@ prettier "~2.2.1" regenerator-runtime "^0.13.7" -"@storybook/theming@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.1.11.tgz#0e757e0562aedefc96bd1d12714d4c8e92823467" - integrity sha512-zRChacVgKoU2BmpvwK1ntiF3KIpc8QblJT7IGiKfP/BNpy9gNeXbLPLk3g/tkHszOvVYtkaZhEXni4Od8tqy1A== - dependencies: - "@emotion/core" "^10.1.1" - "@emotion/is-prop-valid" "^0.8.6" - "@emotion/styled" "^10.0.23" - "@storybook/client-logger" "6.1.11" - core-js "^3.0.1" - deep-object-diff "^1.1.0" - emotion-theming "^10.0.19" - global "^4.3.2" - memoizerific "^1.11.3" - polished "^3.4.4" - resolve-from "^5.0.0" - ts-dedent "^2.0.0" - "@storybook/theming@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.1.15.tgz#01083ab89904dd959429b0b3fd1c76bd0ecc59ef" @@ -5044,6 +5113,24 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" +"@storybook/theming@6.3.4": + version "6.3.4" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.3.4.tgz#69d3f912c74a7b6ba78c1c95fac3315356468bdd" + integrity sha512-L0lJcwUi7mse+U7EBAv5NVt81mH1MtUzk9paik8hMAc68vDtR/X0Cq4+zPsgykCROOTtEGrQ/JUUrpcEqeprTQ== + dependencies: + "@emotion/core" "^10.1.1" + "@emotion/is-prop-valid" "^0.8.6" + "@emotion/styled" "^10.0.27" + "@storybook/client-logger" "6.3.4" + core-js "^3.8.2" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.27" + global "^4.4.0" + memoizerific "^1.11.3" + polished "^4.0.5" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + "@storybook/ui@6.1.15": version "6.1.15" resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.1.15.tgz#a0f6c49fcf81cf172cd2de4c8dba2be1296891f6" @@ -5257,9 +5344,9 @@ pretty-format "^26.6.2" "@testing-library/jest-dom@^5.10.1": - version "5.11.9" - resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz#e6b3cd687021f89f261bd53cbe367041fbd3e975" - integrity sha512-Mn2gnA9d1wStlAIT2NU8J15LNob0YFBVjs2aEQ3j8rsfRQo+lAs7/ui1i2TGaJjapLmuNPLTsrm+nPjmZDwpcQ== + version "5.14.1" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.14.1.tgz#8501e16f1e55a55d675fe73eecee32cdaddb9766" + integrity sha512-dfB7HVIgTNCxH22M1+KU6viG5of2ldoA5ly8Ar8xkezKHKXjRvznCdbMbqjYGgO2xjRbwnR+rR8MLUIqF3kKbQ== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" @@ -5267,6 +5354,7 @@ chalk "^3.0.0" css "^3.0.0" css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" lodash "^4.17.15" redent "^3.0.0" @@ -5558,6 +5646,11 @@ dependencies: "@types/d3-color" "*" +"@types/d3-path@*": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz#939e3a784ae4f80b1fde8098b91af1776ff1312b" + integrity sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg== + "@types/d3-path@^1": version "1.0.9" resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c" @@ -5575,12 +5668,12 @@ dependencies: "@types/d3-path" "^1" -"@types/d3-shape@^2.0.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.0.tgz#cc7bbc9fc2c25f092bd457887a3224a21a55ca55" - integrity sha512-xTMEs8eITRksXclcVxMHIONRdyjj2TjDIwO4XFOPTVBNK9/oC4ZOhUbvTz1IpcsEsS/mClwuulP+OoawSAbSGA== +"@types/d3-shape@^3.0.1": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.0.1.tgz#8cebf5f3d56dd81674e1822279db58a38848a250" + integrity sha512-HnpwE2zl45cOaXLLo0zR4OfRQM9u9sI/ESb0w41PrrsQfkibj8pVIS8VYMSem2Wf5RuWxcgy3/8Kw2/FcIqAEQ== dependencies: - "@types/d3-path" "^1" + "@types/d3-path" "*" "@types/d3-zoom@^2.0.0": version "2.0.1" @@ -5607,6 +5700,13 @@ dependencies: "@types/node" "*" +"@types/dompurify@^2.2.2": + version "2.2.3" + resolved "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.2.3.tgz#6e89677a07902ac1b6821c345f34bd85da239b08" + integrity sha512-CLtc2mZK8+axmrz1JqtpklO/Kvn38arGc8o1l3UVopZaXXuer9ONdZwJ/9f226GrhRLtUmLr9WrvZsRSNpS8og== + dependencies: + "@types/trusted-types" "*" + "@types/eslint@*": version "6.1.8" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-6.1.8.tgz#7e868f89bc1e520323d405940e49cb912ede5bba" @@ -5625,10 +5725,15 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/eventsource@^1.1.5": + version "1.1.5" + resolved "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.1.5.tgz#408e9b45efb176c8bea672ab58c81e7ab00d24bc" + integrity sha512-BA9q9uC2PAMkUS7DunHTxWZZaVpeNzDG8lkBxcKwzKJClfDQ4Z59/Csx7HSH/SIqFN2JWh0tAKAM6k/wRR0OZg== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.5": - version "4.17.21" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz#a427278e106bca77b83ad85221eae709a3414d42" - integrity sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA== + version "4.17.24" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" + integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== dependencies: "@types/node" "*" "@types/qs" "*" @@ -5828,6 +5933,13 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jenkins@^0.23.1": + version "0.23.2" + resolved "https://registry.npmjs.org/@types/jenkins/-/jenkins-0.23.2.tgz#96736a2be4904efdfe7fe2650569479fd77dc48e" + integrity sha512-BELmIZ6brxwGFqBHfLjLTjYRWAUqcT1d2BydH1CcRTZEjCYw3DRVfZkXU7BVlyIsKXm2ZMIKVkEjKKtVDvPiXA== + dependencies: + "@types/node" "*" + "@types/jest-when@^2.7.2": version "2.7.2" resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-2.7.2.tgz#619fbc5f623bcd0b29efde0e4993c7f0d50d026d" @@ -5946,10 +6058,10 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/ldapjs@^1.0.10": - version "1.0.10" - resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.10.tgz#bac705c9e154b97d69496b5213cc28dbe9715a37" - integrity sha512-AMkMxkK/wjYtWebNH2O+rARfo7scBpW3T23g6zmGCwDgbyDbR79XWpcSqhPWdU+fChaF+I3dVyl9X2dT1CyI9w== +"@types/ldapjs@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.0.tgz#43ff78668c420b69a61b93e0acde71ddc0c7766d" + integrity sha512-L6ObSryRGOw0qYmN9B4w4s8UkoyRbxoWh7C1vc/Z6O/VXJQJqGBSyZPXRDmgKF+TZVGq/F7H37t0Cxrm+hLiRA== dependencies: "@types/node" "*" @@ -6055,10 +6167,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "14.17.0" - resolved "https://registry.npmjs.org/@types/node/-/node-14.17.0.tgz#3ba770047723b3eeb8dc9fca02cce8a7fb6378da" - integrity sha512-w8VZUN/f7SSbvVReb9SWp6cJFevxb4/nkG65yLAya//98WgocKm5PLDAtSs5CtJJJM+kHmJjO/6mmYW4MHShZA== +"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^14.14.31", "@types/node@^14.14.32", "@types/node@^14.14.33": + version "14.17.3" + resolved "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d" + integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw== "@types/node@10.17.13": version "10.17.13" @@ -6075,11 +6187,6 @@ resolved "https://registry.npmjs.org/@types/node/-/node-12.12.58.tgz#46dae9b2b9ee5992818c8f7cee01ff4ce03ab44c" integrity sha512-Be46CNIHWAagEfINOjmriSxuv7IVcqbGe+sDSg2SYCEz/0CRBy7LRASGfRbD8KZkqoePU73Wsx3UvOSFcq/9hA== -"@types/node@^14.14.31", "@types/node@^14.14.32", "@types/node@^14.14.33": - version "14.17.3" - resolved "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d" - integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw== - "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -6091,9 +6198,9 @@ integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA== "@types/nunjucks@^3.1.4": - version "3.1.4" - resolved "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.4.tgz#2f80e2fa5e7982b2131201d0b4474ab4d03d9de1" - integrity sha512-cR65PLlHKW/qxxj840dbNb3ICO+iAVQzaNKJ8TcKOVKFi+QcAkhw9SCY8VFAyU41SmJMs+2nrIN2JGhX+jYb7A== + version "3.1.5" + resolved "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.5.tgz#e0782333b75b4ff9ac9213aa160ca8c5132eaf57" + integrity sha512-0zEdmQNNvQ+xyV9kqQvAV93UVroTwhE78toVUDT0GBnGcW2jQBZnB4al9qq2LqI5qHOqROy/DvvAY/UwrbvV1A== "@types/oauth@*": version "0.9.1" @@ -6163,9 +6270,9 @@ "@types/passport" "*" "@types/passport@*", "@types/passport@^1.0.3": - version "1.0.6" - resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.6.tgz#72343e49d65efa98cb328163cff5f127981947d7" - integrity sha512-9oKfrJXuAxvyxdrtMCxKkHgmd6DMO8NDOLvMJ1LvIWd6/xP+i81PAkpTaEca7VhJX9S009RciwZL/j6dsLsHrA== + version "1.0.7" + resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz#85892f14932168158c86aecafd06b12f5439467a" + integrity sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw== dependencies: "@types/express" "*" @@ -6211,7 +6318,7 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/reach__router@^1.3.5", "@types/reach__router@^1.3.7": +"@types/reach__router@^1.3.7": version "1.3.7" resolved "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.7.tgz#de8ab374259ae7f7499fc1373b9697a5f3cd6428" integrity sha512-cyBEb8Ef3SJNH5NYEIDGPoMMmYUxROatuxbICusVRQIqZUB85UCt6R2Ok60tKS/TABJsJYaHyNTW3kqbpxlMjg== @@ -6558,6 +6665,11 @@ resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== +"@types/trusted-types@*": + version "2.0.2" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" + integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== + "@types/tunnel@^0.0.1": version "0.0.1" resolved "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" @@ -6705,74 +6817,73 @@ resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== -"@typescript-eslint/eslint-plugin@^v4.27.0": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.27.0.tgz#0b7fc974e8bc9b2b5eb98ed51427b0be529b4ad0" - integrity sha512-DsLqxeUfLVNp3AO7PC3JyaddmEHTtI9qTSAs+RB6ja27QvIM0TA8Cizn1qcS6vOu+WDLFJzkwkgweiyFhssDdQ== +"@typescript-eslint/eslint-plugin@^v4.28.3": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz#36cdcd9ca6f9e5cb49b9f61b970b1976708d084b" + integrity sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg== dependencies: - "@typescript-eslint/experimental-utils" "4.27.0" - "@typescript-eslint/scope-manager" "4.27.0" + "@typescript-eslint/experimental-utils" "4.28.3" + "@typescript-eslint/scope-manager" "4.28.3" debug "^4.3.1" functional-red-black-tree "^1.0.1" - lodash "^4.17.21" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.27.0", "@typescript-eslint/experimental-utils@^4.0.1": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.27.0.tgz#78192a616472d199f084eab8f10f962c0757cd1c" - integrity sha512-n5NlbnmzT2MXlyT+Y0Jf0gsmAQzCnQSWXKy4RGSXVStjDvS5we9IWbh7qRVKdGcxT0WYlgcCYUK/HRg7xFhvjQ== +"@typescript-eslint/experimental-utils@4.28.3", "@typescript-eslint/experimental-utils@^4.0.1": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz#976f8c1191b37105fd06658ed57ddfee4be361ca" + integrity sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.27.0" - "@typescript-eslint/types" "4.27.0" - "@typescript-eslint/typescript-estree" "4.27.0" + "@typescript-eslint/scope-manager" "4.28.3" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/typescript-estree" "4.28.3" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@^v4.27.0": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.27.0.tgz#85447e573364bce4c46c7f64abaa4985aadf5a94" - integrity sha512-XpbxL+M+gClmJcJ5kHnUpBGmlGdgNvy6cehgR6ufyxkEJMGP25tZKCaKyC0W/JVpuhU3VU1RBn7SYUPKSMqQvQ== +"@typescript-eslint/parser@^v4.28.3": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.3.tgz#95f1d475c08268edffdcb2779993c488b6434b44" + integrity sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ== dependencies: - "@typescript-eslint/scope-manager" "4.27.0" - "@typescript-eslint/types" "4.27.0" - "@typescript-eslint/typescript-estree" "4.27.0" + "@typescript-eslint/scope-manager" "4.28.3" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/typescript-estree" "4.28.3" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.27.0": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.27.0.tgz#b0b1de2b35aaf7f532e89c8e81d0fa298cae327d" - integrity sha512-DY73jK6SEH6UDdzc6maF19AHQJBFVRf6fgAXHPXCGEmpqD4vYgPEzqpFz1lf/daSbOcMpPPj9tyXXDPW2XReAw== +"@typescript-eslint/scope-manager@4.28.3": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz#c32ad4491b3726db1ba34030b59ea922c214e371" + integrity sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ== dependencies: - "@typescript-eslint/types" "4.27.0" - "@typescript-eslint/visitor-keys" "4.27.0" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/visitor-keys" "4.28.3" -"@typescript-eslint/types@4.27.0": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.27.0.tgz#712b408519ed699baff69086bc59cd2fc13df8d8" - integrity sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA== +"@typescript-eslint/types@4.28.3": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz#8fffd436a3bada422c2c1da56060a0566a9506c7" + integrity sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA== -"@typescript-eslint/typescript-estree@4.27.0": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.27.0.tgz#189a7b9f1d0717d5cccdcc17247692dedf7a09da" - integrity sha512-KH03GUsUj41sRLLEy2JHstnezgpS5VNhrJouRdmh6yNdQ+yl8w5LrSwBkExM+jWwCJa7Ct2c8yl8NdtNRyQO6g== +"@typescript-eslint/typescript-estree@4.28.3": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz#253d7088100b2a38aefe3c8dd7bd1f8232ec46fb" + integrity sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w== dependencies: - "@typescript-eslint/types" "4.27.0" - "@typescript-eslint/visitor-keys" "4.27.0" + "@typescript-eslint/types" "4.28.3" + "@typescript-eslint/visitor-keys" "4.28.3" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.27.0": - version "4.27.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.27.0.tgz#f56138b993ec822793e7ebcfac6ffdce0a60cb81" - integrity sha512-es0GRYNZp0ieckZ938cEANfEhsfHrzuLrePukLKtY3/KPXcq1Xd555Mno9/GOgXhKzn0QfkDLVgqWO3dGY80bg== +"@typescript-eslint/visitor-keys@4.28.3": + version "4.28.3" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz#26ac91e84b23529968361045829da80a4e5251c4" + integrity sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg== dependencies: - "@typescript-eslint/types" "4.27.0" + "@typescript-eslint/types" "4.28.3" eslint-visitor-keys "^2.0.0" "@webassemblyjs/ast@1.9.0": @@ -7807,6 +7918,15 @@ autoprefixer@^9.7.2: postcss "^7.0.32" postcss-value-parser "^4.1.0" +aws-sdk-mock@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/aws-sdk-mock/-/aws-sdk-mock-5.2.1.tgz#126d4d5362c96b7d1d0bd87708a99d626c19ffd4" + integrity sha512-dY7zA1p/lX335V4/aOJ2L8ggXC3a5zokTJFZlZVW3uU+Zej7u+V7WrEcN5TVaJAnk4auT263T6EK/OHW4WjKhw== + dependencies: + aws-sdk "^2.928.0" + sinon "^11.1.1" + traverse "^0.6.6" + aws-sdk@^2.382.0, aws-sdk@^2.840.0: version "2.922.0" resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.922.0.tgz#4568be067dceaaeda5d2d5a7e2f22666687f0b32" @@ -7822,6 +7942,21 @@ aws-sdk@^2.382.0, aws-sdk@^2.840.0: uuid "3.3.2" xml2js "0.4.19" +aws-sdk@^2.928.0: + version "2.951.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.951.0.tgz#3cd8c0a4610407623ae8f1724f431fd328441033" + integrity sha512-YPqhdESUzd4+pSuGJcfMnG1qNVbmZjnmsa85Z9jofR1ilIpuV31onIiFHv8iubM59ETok/+zy3QOmxRSLYzFmQ== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -8362,6 +8497,11 @@ bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: dependencies: tweetnacl "^0.14.3" +bdd-lazy-var@^2.6.0: + version "2.6.1" + resolved "https://registry.npmjs.org/bdd-lazy-var/-/bdd-lazy-var-2.6.1.tgz#ca03fb36d68c5a507c0ba9a4d53160b899e6b7cb" + integrity sha512-X3ADwcFji/IHIrYJhTTpaiWhoOx4pl4whdAx1dmvdeUPsMUb7fVYFvf/Q33VEAEAVkEwi5rgNSZ0Y9oOVeQV+A== + before-after-hook@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" @@ -11324,10 +11464,10 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz#b06d059cdd4a4ad9a79275f9d414a5c126241166" - integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ== +dom-accessibility-api@^0.5.4, dom-accessibility-api@^0.5.6: + version "0.5.6" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9" + integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw== dom-converter@^0.2: version "0.2.0" @@ -11428,6 +11568,11 @@ dompurify@^2.0.12, dompurify@^2.1.1, dompurify@^2.2.8: resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.2.8.tgz#ce88e395f6d00b6dc53f80d6b2a6fdf5446873c6" integrity sha512-9H0UL59EkDLgY3dUFjLV6IEUaHm5qp3mxSqWw7Yyx4Zhk2Jn2cmLe+CNPP3xy13zl8Bqg+0NehQzkdMoVhGRww== +dompurify@^2.2.9: + version "2.3.0" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2" + integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw== + domutils@1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -11444,7 +11589,7 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.0.0, domutils@^2.4.4: +domutils@^2.0.0: version "2.4.4" resolved "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== @@ -11709,10 +11854,10 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.4" -enhanced-resolve@^4.0.0, enhanced-resolve@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" - integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== +enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" @@ -12121,13 +12266,14 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.1.0: - version "7.28.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz#435aa17a0b82c13bb2be9d51408b617e49c1e820" - integrity sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g== +eslint@^7.30.0: + version "7.30.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz#6d34ab51aaa56112fd97166226c9a97f505474f8" + integrity sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.2" + "@humanwhocodes/config-array" "^0.5.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -12302,6 +12448,13 @@ eventsource@^1.0.5, eventsource@^1.0.7: dependencies: original "^1.0.0" +eventsource@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" + integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== + dependencies: + original "^1.0.0" + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -12315,19 +12468,6 @@ exec-sh@^0.3.2: resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== -execa@1.0.0, execa@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" @@ -12359,20 +12499,7 @@ execa@4.1.0, execa@^4.0.0, execa@^4.0.1: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^5.0.0: +execa@5.0.0, execa@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== @@ -12387,6 +12514,32 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + executable@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -14435,16 +14588,6 @@ htmlparser2@^4.0: domutils "^2.0.0" entities "^2.0.0" -htmlparser2@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01" - integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.4.4" - entities "^2.0.0" - http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -14719,9 +14862,9 @@ immer@8.0.1: integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== immer@^9.0.1: - version "9.0.1" - resolved "https://registry.npmjs.org/immer/-/immer-9.0.1.tgz#1116368e051f9a0fd188c5136b6efb74ed69c57f" - integrity sha512-7CCw1DSgr8kKYXTYOI1qMM/f5qxT5vIVMeGLDCDX8CSxsggr1Sjdoha4OhsP0AZ1UvWbyZlILHvLjaynuu02Mg== + version "9.0.5" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.5.tgz#a7154f34fe7064f15f00554cc94c66cc0bf453ec" + integrity sha512-2WuIehr2y4lmYz9gaQzetPR2ECniCifk4ORaQbU3g5EalLt+0IVTosEPJ5BoYl/75ky2mivzdRzV8wWgQGOSYQ== immutable@>=3.8.2, immutable@^3.8.1, immutable@^3.8.2, immutable@^3.x.x: version "3.8.2" @@ -15610,6 +15753,11 @@ is-yarn-global@^0.3.0: resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -15663,9 +15811,9 @@ isomorphic-form-data@~2.0.0: form-data "^2.3.2" isomorphic-git@^1.8.0: - version "1.8.0" - resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.8.0.tgz#50440650a64706a321cbea1af955c1cf1110b238" - integrity sha512-TWJvQh+++eFrEG0IFS/jLhMwsBoCOX1/Dsw9q8no59Mp1K0jEjSHXFWv2P04PwkxcIpePkXVBI5YFcFT2nkuQg== + version "1.9.2" + resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.9.2.tgz#0e492dbcd9873070b2a57eef257a45b90020ed72" + integrity sha512-puCXcGgtkDXdMYLZlAEGbpkbmHn/Q4Lsl2uMFwMLOKmmr8Qe7Fe3+c6k2+aHW3rMdJYg9xTv95BJ+PRzR8Ydww== dependencies: async-lock "^1.1.0" clean-git-ref "^2.0.1" @@ -15766,7 +15914,7 @@ jake@^10.6.1: filelist "^1.0.1" minimatch "^3.0.4" -jenkins@^0.28.0: +jenkins@^0.28.1: version "0.28.1" resolved "https://registry.npmjs.org/jenkins/-/jenkins-0.28.1.tgz#f7951798ee5d2bb501a831979b6b5ecc1a922a64" integrity sha512-gcC4QUrP4VzdqOMHoVzh36XlJprxJkI2HGLQSY7w84KoCTVNDcR/8O00tYyXp9vrZOx4wl5zCXLVKMgH2IoyJQ== @@ -16246,9 +16394,9 @@ joycon@^2.2.5: integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ== js-base64@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.6.0.tgz#773e1de628f4f298d65a7e9842c50244751f5756" - integrity sha512-wVdUBYQeY2gY73RIlPrysvpYx+2vheGo8Y1SNQv/BzHToWpAZzJU7Z6uheKMAe+GLSBig5/Ps2nxg/8tRB73xg== + version "3.6.1" + resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz#555aae398b74694b4037af1f8a5a6209d170efbe" + integrity sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ== js-cookie@^2.2.1: version "2.2.1" @@ -16739,6 +16887,11 @@ junk@^3.1.0: resolved "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +just-extend@^4.0.2: + version "4.2.1" + resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" + integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== + jwa@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -16779,17 +16932,17 @@ jwt-decode@*, jwt-decode@^3.1.0: integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== kafkajs@^1.16.0-beta.6: - version "1.16.0-beta.6" - resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-1.16.0-beta.6.tgz#650f4d16abd60516aa0c375be613368391216df1" - integrity sha512-R4DT3s7oCAoxdq3tN8w2WkUxLXcZgLQSjQVSmEZpEmT6hVdZ5AUZWqyLYr1IRbQowsJK4Z7eWJAk68J0JiRlUw== + version "1.16.0-beta.21" + resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-1.16.0-beta.21.tgz#5736bcef7b505714642a82d6dc0d1507fc0ae817" + integrity sha512-6iarOOnKTaei0EK+a+K2V/bBA7YgvpA69tZwnVF85PxGlvoG/wqKpfRNh2Mb04uiNTEwBYNEIO7hAFElEM6/AA== -keytar@^5.4.0: - version "5.6.0" - resolved "https://registry.npmjs.org/keytar/-/keytar-5.6.0.tgz#7b5d4bd043d17211163640be6c4a27a49b12bb39" - integrity sha512-ueulhshHSGoryfRXaIvTj0BV1yB0KddBGhGoqCxSN9LR1Ks1GKuuCdVhF+2/YOs5fMl6MlTI9On1a4DHDXoTow== +keytar@^7.3.0: + version "7.7.0" + resolved "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz#3002b106c01631aa79b1aa9ee0493b94179bbbd2" + integrity sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A== dependencies: - nan "2.14.1" - prebuild-install "5.3.3" + node-addon-api "^3.0.0" + prebuild-install "^6.0.0" keyv-memcache@^1.2.5: version "1.2.5" @@ -16861,11 +17014,6 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -klona@^2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" - integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== - knex@^0.95.1: version "0.95.6" resolved "https://registry.npmjs.org/knex/-/knex-0.95.6.tgz#5fc60ffc2935567bf122925526b1b06b8dbca785" @@ -17337,7 +17485,7 @@ lodash.flattendeep@^4.0.0: resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= -lodash.get@^4, lodash.get@^4.0.0: +lodash.get@^4, lodash.get@^4.0.0, lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= @@ -18370,6 +18518,11 @@ mkdirp-classic@^0.5.2: resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b" integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g== +mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp-infer-owner@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" @@ -18577,7 +18730,7 @@ named-placeholders@^1.1.2: dependencies: lru-cache "^4.1.3" -nan@2.14.1, nan@^2.12.1, nan@^2.14.0: +nan@^2.12.1, nan@^2.14.0: version "2.14.1" resolved "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== @@ -18679,6 +18832,17 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nise@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz#713ef3ed138252daef20ec035ab62b7a28be645c" + integrity sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^7.0.4" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + no-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -18687,10 +18851,10 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-abi@^2.7.0: - version "2.19.3" - resolved "https://registry.npmjs.org/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" - integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== +node-abi@^2.21.0: + version "2.30.0" + resolved "https://registry.npmjs.org/node-abi/-/node-abi-2.30.0.tgz#8be53bf3e7945a34eea10e0fc9a5982776cf550b" + integrity sha512-g6bZh3YCKQRdwuO/tSZZYJAw622SjsRfJ2X0Iy4sSOHZ34/sPPdVBn8fev2tj7njzLwuqPw9uMtGsGkO5kIQvg== dependencies: semver "^5.4.1" @@ -18873,9 +19037,9 @@ node-releases@^1.1.52, node-releases@^1.1.61, node-releases@^1.1.71: integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== nodemon@^2.0.2: - version "2.0.7" - resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz#6f030a0a0ebe3ea1ba2a38f71bf9bab4841ced32" - integrity sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA== + version "2.0.12" + resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5" + integrity sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA== dependencies: chokidar "^3.2.2" debug "^3.2.6" @@ -18888,11 +19052,6 @@ nodemon@^2.0.2: undefsafe "^2.0.3" update-notifier "^4.1.0" -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= - "nopt@2 || 3": version "3.0.6" resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -19762,11 +19921,6 @@ parse-path@^4.0.0: is-ssh "^1.3.0" protocols "^1.4.0" -parse-srcset@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" - integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= - parse-url@^5.0.0: version "5.0.1" resolved "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz#99c4084fc11be14141efa41b3d117a96fcb9527f" @@ -19992,6 +20146,13 @@ path-to-regexp@0.1.7: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + path-type@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -20711,7 +20872,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^ source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.0.2, postcss@^8.1.0, postcss@^8.2.15: +postcss@^8.1.0, postcss@^8.2.15: version "8.3.5" resolved "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== @@ -20747,26 +20908,24 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@5.3.3: - version "5.3.3" - resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" - integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== +prebuild-install@^6.0.0: + version "6.1.3" + resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.3.tgz#8ea1f9d7386a0b30f7ef20247e36f8b2b82825a2" + integrity sha512-iqqSR84tNYQUQHRXalSKdIaM8Ov1QxOVuBNWI7+BzZWv6Ih9k75wOnH1rGQ9WWTaaLkTpxWKIciOF0KyfM74+Q== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" github-from-package "0.0.0" - minimist "^1.2.0" - mkdirp "^0.5.1" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" napi-build-utils "^1.0.1" - node-abi "^2.7.0" - noop-logger "^0.1.1" + node-abi "^2.21.0" npmlog "^4.0.1" pump "^3.0.0" rc "^1.2.7" simple-get "^3.0.3" tar-fs "^2.0.0" tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" precond@0.2: version "0.2.3" @@ -22783,19 +22942,6 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sanitize-html@^2.3.2: - version "2.4.0" - resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.4.0.tgz#8da7524332eb210d968971621b068b53f17ab5a3" - integrity sha512-Y1OgkUiTPMqwZNRLPERSEi39iOebn2XJLbeiGOBhaJD/yLqtLGu6GE5w7evx177LeGgSE+4p4e107LMiydOf6A== - dependencies: - deepmerge "^4.2.2" - escape-string-regexp "^4.0.0" - htmlparser2 "^6.0.0" - is-plain-object "^5.0.0" - klona "^2.0.3" - parse-srcset "^1.0.2" - postcss "^8.0.2" - sax@1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -22878,9 +23024,9 @@ select@^1.1.2: integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= selfsigned@^1.10.7: - version "1.10.8" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" - integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== + version "1.10.11" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9" + integrity sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA== dependencies: node-forge "^0.10.0" @@ -23118,7 +23264,7 @@ shell-quote@1.7.2: resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shelljs@^0.8.2, shelljs@^0.8.3, shelljs@^0.8.4: +shelljs@^0.8.3, shelljs@^0.8.4: version "0.8.4" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== @@ -23185,6 +23331,18 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +sinon@^11.1.1: + version "11.1.1" + resolved "https://registry.npmjs.org/sinon/-/sinon-11.1.1.tgz#99a295a8b6f0fadbbb7e004076f3ae54fc6eab91" + integrity sha512-ZSSmlkSyhUWbkF01Z9tEbxZLF/5tRC9eojCdFh33gtQaP7ITQVaMWQHGuFM7Cuf/KEfihuh1tTl3/ABju3AQMg== + dependencies: + "@sinonjs/commons" "^1.8.3" + "@sinonjs/fake-timers" "^7.1.0" + "@sinonjs/samsam" "^6.0.2" + diff "^5.0.0" + nise "^5.1.0" + supports-color "^7.2.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -24169,6 +24327,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + supports-color@^8.1.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -24832,7 +24997,7 @@ tr46@^2.0.2: resolved "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= -traverse@~0.6.6: +traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= @@ -24973,7 +25138,7 @@ tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@~2.3.0: +tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@~2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== @@ -25048,7 +25213,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -25952,9 +26117,9 @@ webpack-virtual-modules@^0.2.2: debug "^3.0.0" webpack@^4.41.6, webpack@^4.44.2: - version "4.44.2" - resolved "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" - integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== + version "4.46.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -25964,7 +26129,7 @@ webpack@^4.41.6, webpack@^4.44.2: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.3.0" + enhanced-resolve "^4.5.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -26071,11 +26236,6 @@ which-module@^2.0.0: resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-pm-runs@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" - integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= - which-pm@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae"