diff --git a/.changeset/afraid-garlics-glow.md b/.changeset/afraid-garlics-glow.md deleted file mode 100644 index dfe2596869..0000000000 --- a/.changeset/afraid-garlics-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -The `create-plugin` and `create` commands have both been deprecated in favor of a new `new` command. The `new` command is functionally identical to `create`, but the new naming makes it possible to use as yarn script, since `yarn create` is reserved. diff --git a/.changeset/afraid-horses-lick.md b/.changeset/afraid-horses-lick.md deleted file mode 100644 index f2dfcf4ed6..0000000000 --- a/.changeset/afraid-horses-lick.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -'@backstage/app-defaults': patch -'@backstage/core-app-api': patch -'@backstage/core-components': patch -'@backstage/core-plugin-api': patch -'@backstage/dev-utils': patch -'@backstage/test-utils': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-airbrake': patch -'@backstage/plugin-allure': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-bazaar': patch -'@backstage/plugin-catalog-graph': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-catalog-react': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-codescene': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-graphiql': patch -'@backstage/plugin-home': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-org': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-permission-react': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-search-react': patch -'@backstage/plugin-search': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-techdocs-addons-test-utils': patch -'@backstage/plugin-techdocs-react': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-todo': patch -'@backstage/plugin-user-settings': patch ---- - -Updated React Router dependencies to be peer dependencies. diff --git a/.changeset/afraid-radios-boil.md b/.changeset/afraid-radios-boil.md deleted file mode 100644 index a9533d55c0..0000000000 --- a/.changeset/afraid-radios-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-msgraph': patch ---- - -Fixed a bug in the `MicrosoftGraphEntityProvider` that ignored the `userExpand` and `groupExpand` configuration parameters diff --git a/.changeset/beige-coins-type.md b/.changeset/beige-coins-type.md deleted file mode 100644 index 340170a83e..0000000000 --- a/.changeset/beige-coins-type.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-app-api': patch -'@backstage/backend-plugin-api': patch ---- - -Made `ApiRef.defaultFactory` internal. diff --git a/.changeset/beige-monkeys-add.md b/.changeset/beige-monkeys-add.md deleted file mode 100644 index ee54e4d090..0000000000 --- a/.changeset/beige-monkeys-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Workaround support for `swc` instead of `sucrase` diff --git a/.changeset/beige-pumas-tap.md b/.changeset/beige-pumas-tap.md deleted file mode 100644 index 8ed8aab1f4..0000000000 --- a/.changeset/beige-pumas-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Add the option for a homepage when using the `github:publish` action diff --git a/.changeset/big-dolls-reflect.md b/.changeset/big-dolls-reflect.md deleted file mode 100644 index e57fb74e09..0000000000 --- a/.changeset/big-dolls-reflect.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-auth-node': patch -'@backstage/plugin-permission-node': patch ---- - -Minor update to tests diff --git a/.changeset/big-spies-check.md b/.changeset/big-spies-check.md deleted file mode 100644 index c704c7d00e..0000000000 --- a/.changeset/big-spies-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-node': patch ---- - -Fix Techdocs S3 and GCS publisher to include bucketRootPath in requests diff --git a/.changeset/big-teachers-tell.md b/.changeset/big-teachers-tell.md deleted file mode 100644 index 180a974e5e..0000000000 --- a/.changeset/big-teachers-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/test-utils': minor ---- - -Elements rendered in a test app are no longer wrapped in a `Routes` and `Route` element, as this is not compatible with React Router v6 stable. diff --git a/.changeset/blue-snakes-compete.md b/.changeset/blue-snakes-compete.md deleted file mode 100644 index 73054554b1..0000000000 --- a/.changeset/blue-snakes-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-adr-backend': patch ---- - -Continue processing subsequent entities when an error occurs in collator diff --git a/.changeset/brave-steaks-lay.md b/.changeset/brave-steaks-lay.md deleted file mode 100644 index a36fa5c80b..0000000000 --- a/.changeset/brave-steaks-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-backend': patch ---- - -chore: Exporting KubernetesClientProvider and everything in kubernetes-auth-translator as requested in issue #10457 diff --git a/.changeset/breezy-pots-worry.md b/.changeset/breezy-pots-worry.md new file mode 100644 index 0000000000..48d5ae6c80 --- /dev/null +++ b/.changeset/breezy-pots-worry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +consistently show parent and child relations in group profile card diff --git a/.changeset/bright-rules-shout.md b/.changeset/bright-rules-shout.md new file mode 100644 index 0000000000..f7cdae3b4f --- /dev/null +++ b/.changeset/bright-rules-shout.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': patch +--- + +Defer `ajv` compilation of schema validators to improve module-import performance diff --git a/.changeset/brown-kings-greet.md b/.changeset/brown-kings-greet.md deleted file mode 100644 index 8271b40bb4..0000000000 --- a/.changeset/brown-kings-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-actions': patch ---- - -Modify RecentWorkflowRunsCard use constructed route instead of hardcoded route. diff --git a/.changeset/chatty-cars-kick.md b/.changeset/chatty-cars-kick.md deleted file mode 100644 index fcda65d0f7..0000000000 --- a/.changeset/chatty-cars-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-node': patch ---- - -Adds experimental `catalogServiceRef` for obtaining a `CatalogClient` in the new backend system. diff --git a/.changeset/clean-camels-sneeze.md b/.changeset/clean-camels-sneeze.md new file mode 100644 index 0000000000..0fccb52e0b --- /dev/null +++ b/.changeset/clean-camels-sneeze.md @@ -0,0 +1,12 @@ +--- +'@backstage/create-app': patch +--- + +Newly created Backstage repositories now use the stable version 6 of +`react-router`, just like the main repo does. Please let us know if you find any +issues with this. + +Migrating to the stable version of `react-router` is optional for the time +being. But if you want to do the same for your existing repository, please +follow [this +guide](https://backstage.io/docs/tutorials/react-router-stable-migration). diff --git a/.changeset/clever-beans-turn.md b/.changeset/clever-beans-turn.md deleted file mode 100644 index 69846a2516..0000000000 --- a/.changeset/clever-beans-turn.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-kafka': patch -'@backstage/plugin-kafka-backend': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-proxy-backend': patch ---- - -Minor API signatures cleanup diff --git a/.changeset/clever-turtles-work.md b/.changeset/clever-turtles-work.md deleted file mode 100644 index 9ddad4d1d2..0000000000 --- a/.changeset/clever-turtles-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Deprecated the `plugin:diff` command. If you wish to keep running similar checks in your project we recommend using bespoke scripts. A useful utility for such scripts is `@manypkg/get-packages`, which helps you enumerate all packages in a monorepo. diff --git a/.changeset/cold-frogs-kiss.md b/.changeset/cold-frogs-kiss.md deleted file mode 100644 index 7af9468ca1..0000000000 --- a/.changeset/cold-frogs-kiss.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Fixed a bug in plugin-scaffolder-backend where it ignores the skip migration database options. - -To use this new implementation you need to create the instance of `DatabaseTaskStore` using the `PluginDatabaseManager` instead of `Knex`; - -``` -import { DatabaseManager, getRootLogger, loadBackendConfig } from '@backstage/backend-common'; -import { DatabaseTaskStore } from '@backstage/plugin-scaffolder-backend'; - -const config = await loadBackendConfig({ argv: process.argv, logger: getRootLogger() }); -const databaseManager = DatabaseManager.fromConfig(config, { migrations: { skip: true } }); -const databaseTaskStore = await DatabaseTaskStore.create(databaseManager); -``` diff --git a/.changeset/cool-cameras-count.md b/.changeset/cool-cameras-count.md deleted file mode 100644 index 02c8ffc2bd..0000000000 --- a/.changeset/cool-cameras-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -When using React Router v6 stable, it is now possible for components within the `Route` element tree to have `path` props, although they will be ignored. diff --git a/.changeset/cuddly-clocks-dance.md b/.changeset/cuddly-clocks-dance.md deleted file mode 100644 index 9d33535a62..0000000000 --- a/.changeset/cuddly-clocks-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-msgraph': patch ---- - -Added $select attribute to user query diff --git a/.changeset/cuddly-needles-marry.md b/.changeset/cuddly-needles-marry.md deleted file mode 100644 index 84963a7fe5..0000000000 --- a/.changeset/cuddly-needles-marry.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/integration': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-techdocs-node': patch ---- - -Bump `git-url-parse` version to `^13.0.0` diff --git a/.changeset/curly-hounds-wait.md b/.changeset/curly-hounds-wait.md deleted file mode 100644 index 58ebfe89a3..0000000000 --- a/.changeset/curly-hounds-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-stack-overflow-backend': patch ---- - -Now requests all questions available using pagination. Default max page is set to 100, with a configurable `maxPage` option on the collator. diff --git a/.changeset/curvy-kiwis-fold.md b/.changeset/curvy-kiwis-fold.md new file mode 100644 index 0000000000..8d94f38343 --- /dev/null +++ b/.changeset/curvy-kiwis-fold.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Update versions of packages used in the create-app template, to match those in the main repo diff --git a/.changeset/dry-games-end.md b/.changeset/dry-games-end.md deleted file mode 100644 index dc2f86ee10..0000000000 --- a/.changeset/dry-games-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Fix bug with empty strings in `EntityPicker` diff --git a/.changeset/dry-tips-build.md b/.changeset/dry-tips-build.md deleted file mode 100644 index b800161fc8..0000000000 --- a/.changeset/dry-tips-build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-node': patch ---- - -`IdentityClient` is now deprecated. Please migrate to `IdentityApi` and `DefaultIdentityClient` instead. The authenticate function on `DefaultIdentityClient` is also deprecated. Please use `getIdentity` instead. diff --git a/.changeset/dull-spoons-doubt.md b/.changeset/dull-spoons-doubt.md deleted file mode 100644 index 533a1ea38f..0000000000 --- a/.changeset/dull-spoons-doubt.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Removed usage of the deprecated `diff` command in the root `package.json`. - -To make this change in an existing app, make the following change in the root `package.json`: - -```diff -- "diff": "lerna run diff --", -``` diff --git a/.changeset/eight-shrimps-call.md b/.changeset/eight-shrimps-call.md deleted file mode 100644 index 2e19681f7a..0000000000 --- a/.changeset/eight-shrimps-call.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -humanizeEntityRef function can now be forced to include default namespace diff --git a/.changeset/eight-spies-protect.md b/.changeset/eight-spies-protect.md deleted file mode 100644 index 0011dece72..0000000000 --- a/.changeset/eight-spies-protect.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-azure-devops-common': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-fossa': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-newrelic-dashboard': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-search-backend-node': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-sonarqube': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-tech-insights-node': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-vault-backend': patch ---- - -Minor API signatures cleanup diff --git a/.changeset/eleven-bees-marry.md b/.changeset/eleven-bees-marry.md deleted file mode 100644 index fb0a6fb32e..0000000000 --- a/.changeset/eleven-bees-marry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Added a keep-alive refresh loop to the `DatabaseManager`, keeping the connection -pool up. diff --git a/.changeset/empty-colts-whisper.md b/.changeset/empty-colts-whisper.md deleted file mode 100644 index 1eabe8c58c..0000000000 --- a/.changeset/empty-colts-whisper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/config-loader': patch ---- - -No longer log when reloading remote config. diff --git a/.changeset/famous-cups-roll.md b/.changeset/famous-cups-roll.md deleted file mode 100644 index fdd5559c72..0000000000 --- a/.changeset/famous-cups-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -Updated `createBackendPlugin` and `createBackendModule` to properly forward lack of options. diff --git a/.changeset/famous-hounds-sit.md b/.changeset/famous-hounds-sit.md deleted file mode 100644 index d6cc852ff0..0000000000 --- a/.changeset/famous-hounds-sit.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -The options part of `DatabaseManager.fromConfig` now accepts an optional logger -field. diff --git a/.changeset/famous-pianos-kick.md b/.changeset/famous-pianos-kick.md deleted file mode 100644 index 51688077f9..0000000000 --- a/.changeset/famous-pianos-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -The `versions:bump` command will now update dependency ranges in `package.json`, even if the new version is within the current range. diff --git a/.changeset/famous-wombats-happen.md b/.changeset/famous-wombats-happen.md deleted file mode 100644 index 9d8a2f0b0e..0000000000 --- a/.changeset/famous-wombats-happen.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-techdocs': patch ---- - -Internal refactor to use more type safe code when dealing with route parameters. diff --git a/.changeset/fast-paws-press.md b/.changeset/fast-paws-press.md deleted file mode 100644 index 8ddb0bc49e..0000000000 --- a/.changeset/fast-paws-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added support for custom certificate for webpack dev server. diff --git a/.changeset/five-carrots-pay.md b/.changeset/five-carrots-pay.md deleted file mode 100644 index 60fa91c163..0000000000 --- a/.changeset/five-carrots-pay.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-tasks': patch -'@backstage/plugin-catalog-backend': patch ---- - -Added support to mysql on some raw queries diff --git a/.changeset/flat-crabs-love.md b/.changeset/flat-crabs-love.md deleted file mode 100644 index 96f566d712..0000000000 --- a/.changeset/flat-crabs-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-node': minor ---- - -Added refresh function to the `EntityProviderConnection` to be able to schedule refreshes from entity providers. diff --git a/.changeset/flat-humans-dance.md b/.changeset/flat-humans-dance.md deleted file mode 100644 index ea7571f5bc..0000000000 --- a/.changeset/flat-humans-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -Service are now scoped to either `'plugin'` or `'root'` scope. Service factories have been updated to provide dependency instances directly rather than factory functions. diff --git a/.changeset/flat-plums-shout.md b/.changeset/flat-plums-shout.md deleted file mode 100644 index 250d6a1b35..0000000000 --- a/.changeset/flat-plums-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Allow unknown typed location from being registered via the location service by configuration settings diff --git a/.changeset/flat-walls-kiss.md b/.changeset/flat-walls-kiss.md deleted file mode 100644 index ea2d2556ed..0000000000 --- a/.changeset/flat-walls-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -The Backstage packages and plugins have all been updated to support React Router v6 stable. The `create-app` template has not been migrated yet, but if you want to migrate your own app or plugins, check out the [migration guide](https://backstage.io/docs/tutorials/react-router-stable-migration). diff --git a/.changeset/fresh-rabbits-juggle.md b/.changeset/fresh-rabbits-juggle.md deleted file mode 100644 index 804ae70853..0000000000 --- a/.changeset/fresh-rabbits-juggle.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-dynatrace': minor ---- - -New features: - -- Some visual improvements to the table that displays Problems -- Added support for viewing recent Synthetics results using -- Added some additional linking to the configured Dynatrace instance diff --git a/.changeset/funny-hounds-obey.md b/.changeset/funny-hounds-obey.md deleted file mode 100644 index 065af245c7..0000000000 --- a/.changeset/funny-hounds-obey.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Fixing bug when the workspace would not be automatically saved when using `allowedOwners` diff --git a/.changeset/funny-years-speak.md b/.changeset/funny-years-speak.md deleted file mode 100644 index b82c10a59f..0000000000 --- a/.changeset/funny-years-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -Removed the previously deprecated class `AtlassianAuthProvider`. Please use `providers.atlassian.create(...)` instead. diff --git a/.changeset/fuzzy-jars-compare.md b/.changeset/fuzzy-jars-compare.md deleted file mode 100644 index b8c404c001..0000000000 --- a/.changeset/fuzzy-jars-compare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-backend': patch ---- - -Adds skipMetricsLookup to the kubernetes-backend schema diff --git a/.changeset/gold-fishes-yawn.md b/.changeset/gold-fishes-yawn.md deleted file mode 100644 index 4b9ef0f605..0000000000 --- a/.changeset/gold-fishes-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Avoid validating the backend configuration schema when loading static configuration for building the frontend. diff --git a/.changeset/gold-hounds-vanish.md b/.changeset/gold-hounds-vanish.md deleted file mode 100644 index d221f0f5e9..0000000000 --- a/.changeset/gold-hounds-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -The search index now does retain fields that have a very long value, but in the form of just a null. This makes it possible to at least filter for their existence. diff --git a/.changeset/gold-laws-attend.md b/.changeset/gold-laws-attend.md deleted file mode 100644 index 08f1250a7d..0000000000 --- a/.changeset/gold-laws-attend.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': minor -'@backstage/plugin-techdocs-node': minor ---- - -Add `projectId` config option to GCP Cloud Storage techdocs publisher. This will allow users to override the project ID, instead of implicitly using the same one as found in a credentials bundle. diff --git a/.changeset/good-avocados-grow.md b/.changeset/good-avocados-grow.md deleted file mode 100644 index dcc685c36d..0000000000 --- a/.changeset/good-avocados-grow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': minor ---- - -Updated the routing system to be compatible with React Router v6 stable. diff --git a/.changeset/good-papayas-dress.md b/.changeset/good-papayas-dress.md deleted file mode 100644 index 26062c8da8..0000000000 --- a/.changeset/good-papayas-dress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Added `projectId` for gitlab projects to be displayed in the `gitlab:publish` output diff --git a/.changeset/gorgeous-boats-prove.md b/.changeset/gorgeous-boats-prove.md deleted file mode 100644 index 46e72db735..0000000000 --- a/.changeset/gorgeous-boats-prove.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-bazaar': patch ---- - -Fixed broken routing by removing the wrapping `Router` from the `RoutedTabs` children. diff --git a/.changeset/gorgeous-pillows-retire.md b/.changeset/gorgeous-pillows-retire.md deleted file mode 100644 index d6d4009ac2..0000000000 --- a/.changeset/gorgeous-pillows-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Support showing counts in option labels of the `EntityTagPicker`. You can enable this by adding the `showCounts` property diff --git a/.changeset/gorgeous-swans-kiss.md b/.changeset/gorgeous-swans-kiss.md deleted file mode 100644 index c33eb49d73..0000000000 --- a/.changeset/gorgeous-swans-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-github': patch ---- - -New experimental alpha exports for use with the upcoming backend system. diff --git a/.changeset/great-cats-sit.md b/.changeset/great-cats-sit.md deleted file mode 100644 index abdf9cfb39..0000000000 --- a/.changeset/great-cats-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Fixes a bug to be able to utilize refresh keys after the entity is loaded from cache diff --git a/.changeset/great-rules-march.md b/.changeset/great-rules-march.md deleted file mode 100644 index eb8d901910..0000000000 --- a/.changeset/great-rules-march.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/cli': patch ---- - -Remove use of deprecated trimLeft/trimRight diff --git a/.changeset/grumpy-kiwis-juggle.md b/.changeset/grumpy-kiwis-juggle.md deleted file mode 100644 index e303f8f674..0000000000 --- a/.changeset/grumpy-kiwis-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added a new `migrate react-router-deps` command to aid in the migration to React Router v6 stable. diff --git a/.changeset/happy-kiwis-look.md b/.changeset/happy-kiwis-look.md deleted file mode 100644 index ff0da8943b..0000000000 --- a/.changeset/happy-kiwis-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Fix typo in the documentation diff --git a/.changeset/heavy-ligers-laugh.md b/.changeset/heavy-ligers-laugh.md deleted file mode 100644 index 71f8b99b12..0000000000 --- a/.changeset/heavy-ligers-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-node': patch ---- - -Fixed bug in LunrSearchEngine where stopwords in title is causing in-doc search not working diff --git a/.changeset/hot-brooms-drive.md b/.changeset/hot-brooms-drive.md deleted file mode 100644 index 3b020acdc8..0000000000 --- a/.changeset/hot-brooms-drive.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -'@backstage/app-defaults': patch -'@backstage/catalog-client': patch -'@backstage/catalog-model': patch -'@backstage/cli-common': patch -'@backstage/config': patch -'@backstage/config-loader': patch -'@backstage/core-app-api': patch -'@backstage/core-components': patch -'@backstage/core-plugin-api': patch -'@backstage/create-app': patch -'@backstage/dev-utils': patch -'@backstage/errors': patch -'@backstage/integration': patch -'@backstage/integration-react': patch -'@backstage/release-manifests': patch -'@techdocs/cli': patch -'@backstage/test-utils': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-airbrake': patch -'@backstage/plugin-allure': patch -'@backstage/plugin-analytics-module-ga': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-apollo-explorer': patch -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-bitrise': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-catalog-graph': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-catalog-react': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-codescene': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-explore-react': patch -'@backstage/plugin-firehydrant': patch -'@backstage/plugin-fossa': patch -'@backstage/plugin-gcalendar': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-github-deployments': patch -'@backstage/plugin-github-issues': patch -'@backstage/plugin-github-pull-requests-board': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-gocd': patch -'@backstage/plugin-graphiql': patch -'@backstage/plugin-home': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-newrelic': patch -'@backstage/plugin-org': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-periskop': patch -'@backstage/plugin-permission-common': patch -'@backstage/plugin-permission-react': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-scaffolder-backend-module-rails': patch -'@backstage/plugin-scaffolder-backend-module-yeoman': patch -'@backstage/plugin-search': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-sonarqube': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-stack-overflow': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-techdocs-addons-test-utils': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-todo': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-vault': patch -'@backstage/plugin-xcmetrics': patch ---- - -Removed dependency on `@types/jest`. diff --git a/.changeset/hot-files-begin.md b/.changeset/hot-files-begin.md deleted file mode 100644 index e9b9076986..0000000000 --- a/.changeset/hot-files-begin.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -The `packages/backend/Dockerfile` received a couple of updates, it now looks as follows: - -```Dockerfile -FROM node:16-bullseye-slim - -# Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, -# in which case you should also move better-sqlite3 to "devDependencies" in package.json. -RUN apt-get update && \ - apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ - rm -rf /var/lib/apt/lists/* && \ - yarn config set python /usr/bin/python3 - -# From here on we use the least-privileged `node` user to run the backend. -USER node -WORKDIR /app - -# This switches many Node.js dependencies to production mode. -ENV NODE_ENV production - -# Copy repo skeleton first, to avoid unnecessary docker cache invalidation. -# The skeleton contains the package.json of each package in the monorepo, -# and along with yarn.lock and the root package.json, that's enough to run yarn install. -COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ -RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz - -RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" - -# Then copy the rest of the backend bundle, along with any other files we might want. -COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ -RUN tar xzf bundle.tar.gz && rm bundle.tar.gz - -CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] -``` - -The two notable changes are that a `USER node` instruction has been added and the ordering of instructions has been changed accordingly. This means that the app will now be running using the least-privileged `node` user. In order for this to work we now need to make sure that all app files are owned by the `node` user, which we do by adding the `--chown=node:node` option to the `COPY` instructions. - -The second change is the addition of `ENV NODE_ENV production`, which ensured that all Node.js modules run in production mode. If you apply this change to an existing app, note that one of the more significant changes is that this switches the log formatting to use the default production format, JSON. Rather than your log lines looking like this: - -```log -2022-08-10T11:36:05.478Z catalog info Performing database migration type=plugin -``` - -They will now look like this: - -```log -{"level":"info","message":"Performing database migration","plugin":"catalog","service":"backstage","type":"plugin"} -``` - -If you wish to keep the existing format, you can override this change by applying the following change to `packages/backend/src/index.ts`: - -```diff - getRootLogger, -+ setRootLogger, -+ createRootLogger, -+ coloredFormat, - useHotMemoize, - ... - ServerTokenManager, - } from '@backstage/backend-common'; - - ... - - async function main() { -+ setRootLogger(createRootLogger({ format: coloredFormat })); -+ - const config = await loadBackendConfig({ -``` diff --git a/.changeset/hot-suits-glow.md b/.changeset/hot-suits-glow.md deleted file mode 100644 index e394d3b8c0..0000000000 --- a/.changeset/hot-suits-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -The `CostInsightsHeader`component now uses group names if available diff --git a/.changeset/hungry-dogs-agree.md b/.changeset/hungry-dogs-agree.md deleted file mode 100644 index b111b6e463..0000000000 --- a/.changeset/hungry-dogs-agree.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-app-api': patch -'@backstage/core-plugin-api': patch ---- - -Added `getSystemIcons()` function to the `AppContext` available through `useApp` that will pull a list of all the icons that have been registered in the App. diff --git a/.changeset/hungry-llamas-tie.md b/.changeset/hungry-llamas-tie.md deleted file mode 100644 index 0f3be39c3f..0000000000 --- a/.changeset/hungry-llamas-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Provide information about the user into scaffolder template action's context diff --git a/.changeset/itchy-brooms-end.md b/.changeset/itchy-brooms-end.md deleted file mode 100644 index 632eb33d97..0000000000 --- a/.changeset/itchy-brooms-end.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Fix issue when using `.jsx` files inside tests diff --git a/.changeset/itchy-kiwis-warn.md b/.changeset/itchy-kiwis-warn.md deleted file mode 100644 index 28c4c875ff..0000000000 --- a/.changeset/itchy-kiwis-warn.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Adds `IdentityApi` configuration to `create-app` scaffolding templates. - -To migrate to the new `IdentityApi`, edit the `packages/backend/src/index.ts` adding the following import: - -```typescript -import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; -``` - -Use the factory function to create an `IdentityApi` in the `makeCreateEnv` function and return it from the -function as follows: - -```typescript -function makeCreateEnv(config: Config) { -... - const identity = DefaultIdentityClient.create({ - discovery, - }); -... - - return { - ..., - identity - } -} -``` - -Backend plugins can be upgraded to work with this new `IdentityApi`. - -Add `identity` to the `RouterOptions` type. - -```typescript -export interface RouterOptions { - ... - identity: IdentityApi; -} -``` - -Then you can use the `IdentityApi` from the plugin. - -```typescript -export async function createRouter( - options: RouterOptions, -): Promise { - const { identity } = options; - - router.get('/user', async (req, res) => { - const user = await identity.getIdentity({ request: req }); - ... -``` diff --git a/.changeset/kind-penguins-report.md b/.changeset/kind-penguins-report.md new file mode 100644 index 0000000000..4023ea5976 --- /dev/null +++ b/.changeset/kind-penguins-report.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +CookieConfigurer can optionally return the `SameSite` cookie attribute. +CookieConfigurer now requires an additional argument `appOrigin` - the origin URL of the app - which is used to calculate the `SameSite` attribute. +defaultCookieConfigurer returns the `SameSite` attribute which defaults to `Lax`. In cases where an auth-backend is running on a different domain than the App, `SameSite=None` is used - but only for secure contexts. This is so that cookies can be included in third-party requests. + +OAuthAdapterOptions has been modified to require additional arguments, `baseUrl`, and `cookieConfigurer`. +OAuthAdapter now resolves cookie configuration using its supplied CookieConfigurer for each request to make sure that the proper attributes always are set. diff --git a/.changeset/large-books-deny.md b/.changeset/large-books-deny.md deleted file mode 100644 index c88d391639..0000000000 --- a/.changeset/large-books-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-client': minor ---- - -Adding `validateEntity` method that calls `/validate-entity` endpoint. diff --git a/.changeset/large-eagles-shop.md b/.changeset/large-eagles-shop.md deleted file mode 100644 index bd5bd2e28d..0000000000 --- a/.changeset/large-eagles-shop.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-airbrake-backend': patch -'@backstage/plugin-catalog-backend-module-bitbucket-server': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-periskop-backend': patch -'@backstage/plugin-stack-overflow-backend': patch -'@backstage/plugin-vault-backend': patch ---- - -Switched to using node-fetch instead of cross-fetch as is standard for our backend packages diff --git a/.changeset/large-trainers-float.md b/.changeset/large-trainers-float.md deleted file mode 100644 index b5d1d9be1d..0000000000 --- a/.changeset/large-trainers-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Properly handle free-text entity filtering in the case of empty tag arrays diff --git a/.changeset/late-turtles-listen.md b/.changeset/late-turtles-listen.md deleted file mode 100644 index 117737a064..0000000000 --- a/.changeset/late-turtles-listen.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Implemented KubernetesContainerRunner: a ContainerRunner implementation that leverages Jobs on a kubernetes cluster - -```ts -const kubeConfig = new KubeConfig(); -kubeConfig.loadFromDefault(); - -const options: KubernetesContainerRunnerOptions = { - kubeConfig, - // namespace where Jobs will be created - namespace: 'default', - // Jobs name will be prefixed with this name - name: 'my-runner', - // An existing Kubernetes volume that will be used - // as base for mounts - mountBase: { - volumeName: 'workdir', - // Every mount must start with the base path - // see example below - basePath: '/workdir', - }, - // Define a Pod template for the Jobs. It has to include - // a volume definition named as the mountBase volumeName - podTemplate: { - spec: { - containers: [], - volumes: [ - { - name: 'workdir', - persistentVolumeClaim: { - claimName: 'workdir-claim', - }, - }, - ], - }, - }, -}; -const containerRunner = new KubernetesContainerRunner(options); - -const runOptions: RunContainerOptions = { - imageName: 'golang:1.17', - args: ['echo', 'hello world'], - mountDirs: { - '/workdir/app': '/app', - }, -}; -containerRunner.runContainer(runOptions); -``` diff --git a/.changeset/lazy-beds-pull.md b/.changeset/lazy-beds-pull.md new file mode 100644 index 0000000000..0d8be46576 --- /dev/null +++ b/.changeset/lazy-beds-pull.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated fallback versions of dependencies in all templates. diff --git a/.changeset/lazy-fireants-check.md b/.changeset/lazy-fireants-check.md new file mode 100644 index 0000000000..234501b07a --- /dev/null +++ b/.changeset/lazy-fireants-check.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +Add useCustomResources react hook for fetching Kubernetes Custom Resources diff --git a/.changeset/lazy-snakes-film.md b/.changeset/lazy-snakes-film.md deleted file mode 100644 index ffb9c539c8..0000000000 --- a/.changeset/lazy-snakes-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Adds code to generate ids for headers parsed through the MarkdownContent component. diff --git a/.changeset/light-beans-share.md b/.changeset/light-beans-share.md deleted file mode 100644 index 51c59ff7d0..0000000000 --- a/.changeset/light-beans-share.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/plugin-search-backend-module-pg': minor ---- - -Fixed a bug in search-backend-module-pg where it ignores the skip migration database options when using the database. - -To use this new implementation you need to create the instance of `DatabaseDocumentStore` using the `PluginDatabaseManager` instead of `Knex`; - -``` -import { DatabaseManager, getRootLogger, loadBackendConfig } from '@backstage/backend-common'; -import { DatabaseDocumentStore } from '@backstage/plugin-search-backend-module-pg'; - -const config = await loadBackendConfig({ argv: process.argv, logger: getRootLogger() }); -const databaseManager = DatabaseManager.fromConfig(config, { migrations: { skip: true } }); -const databaseDocumentStore = await DatabaseDocumentStore.create(databaseManager); -``` diff --git a/.changeset/light-donuts-obey.md b/.changeset/light-donuts-obey.md deleted file mode 100644 index abe6a1697c..0000000000 --- a/.changeset/light-donuts-obey.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Added support for handling broken symlinks within the scaffolder backend. This is intended for templates that may hold a symlink that is invalid at build time but valid within the destination repo. diff --git a/.changeset/little-roses-rule.md b/.changeset/little-roses-rule.md new file mode 100644 index 0000000000..4acec131e6 --- /dev/null +++ b/.changeset/little-roses-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added a new `repo test` command. diff --git a/.changeset/loud-comics-search.md b/.changeset/loud-comics-search.md deleted file mode 100644 index 27f5b3c5d9..0000000000 --- a/.changeset/loud-comics-search.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@backstage/plugin-tech-insights': minor ---- - -Added the possibility to display check results of different types on a single scorecard. - -- **BREAKING** Removed the `getScorecardsDefinition` method from the `TechInsightsApi` interface. Added the `getCheckResultRenderers` method that returns rendering components for given types. -- **BREAKING** The `CheckResultRenderer` type now exposes the `component` factory method that creates a React component used to display a result of a provided check result. -- The `TechInsightsClient` constructor accepts now the optional `renderers` parameter that can be used to inject a custom renderer. -- **BREAKING** The `title` parameter in the `EntityTechInsightsScorecardContent` and `EntityTechInsightsScorecardCard` components is now mandatory. -- The `jsonRulesEngineCheckResultRenderer` used to render `json-rules-engine` check results is exported. -- The `BooleanCheck` component that can be used to render other check results types is also exported. - -If you were overriding the `getScorecardsDefinition` method to adjust the rendering of check results, you should now provide a custom renderer using `renderers` parameter in the `TechInsightsClient` class. - -See the [README](https://github.com/backstage/backstage/tree/master/plugins/tech-insights/README.md) for more details. diff --git a/.changeset/lovely-ants-invite.md b/.changeset/lovely-ants-invite.md deleted file mode 100644 index 688850f776..0000000000 --- a/.changeset/lovely-ants-invite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Applied the fix from version 1.5.1 of this package, which is part of the v1.5.1 release of Backstage. diff --git a/.changeset/lucky-ads-worry.md b/.changeset/lucky-ads-worry.md deleted file mode 100644 index a7f749da17..0000000000 --- a/.changeset/lucky-ads-worry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Support displaying and ordering by counts in `EntityTagPicker` field. Add the `showCounts` option to enable this. Also support configuring `helperText`. diff --git a/.changeset/lucky-points-wash.md b/.changeset/lucky-points-wash.md deleted file mode 100644 index 961c354574..0000000000 --- a/.changeset/lucky-points-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-org': patch ---- - -Added an edit button to the `UserProfileCard` that is enabled when the `backstage.io/edit-url` is present, this matches how the `GroupProfileCard` works diff --git a/.changeset/many-rules-raise.md b/.changeset/many-rules-raise.md deleted file mode 100644 index 40a26a85fe..0000000000 --- a/.changeset/many-rules-raise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-ldap': patch ---- - -Added ae-dir LDAP vendor to correctly extract DN. diff --git a/.changeset/mean-tomatoes-visit.md b/.changeset/mean-tomatoes-visit.md deleted file mode 100644 index 111063d805..0000000000 --- a/.changeset/mean-tomatoes-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Updated the `TechDocsReaderPage` to be compatible with React Router v6 stable. diff --git a/.changeset/metal-candles-tease.md b/.changeset/metal-candles-tease.md deleted file mode 100644 index a48df562e4..0000000000 --- a/.changeset/metal-candles-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/dev-utils': patch ---- - -Fixed routing when using React Router v6 stable. diff --git a/.changeset/metal-crabs-wash.md b/.changeset/metal-crabs-wash.md deleted file mode 100644 index 6ecb50ef67..0000000000 --- a/.changeset/metal-crabs-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -Renamed the `RedirectInfo` type to `OAuthStartResponse` diff --git a/.changeset/metal-weeks-kiss.md b/.changeset/metal-weeks-kiss.md deleted file mode 100644 index 90e453dd5c..0000000000 --- a/.changeset/metal-weeks-kiss.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/backend-app-api': patch -'@backstage/backend-defaults': patch -'@backstage/backend-test-utils': patch ---- - -Updated to support new `ServiceFactory` formats. diff --git a/.changeset/moody-berries-sneeze.md b/.changeset/moody-berries-sneeze.md deleted file mode 100644 index 87a5230f2c..0000000000 --- a/.changeset/moody-berries-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Fixed a bug where addons wouldn't render on sub pages when using React Route v6 stable. diff --git a/.changeset/neat-kangaroos-turn.md b/.changeset/neat-kangaroos-turn.md deleted file mode 100644 index 6f1572d096..0000000000 --- a/.changeset/neat-kangaroos-turn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Tweaked workspace packaging to not rewrite existing `package.json` files. diff --git a/.changeset/nervous-rivers-sneeze.md b/.changeset/nervous-rivers-sneeze.md deleted file mode 100644 index 534dbb4a1a..0000000000 --- a/.changeset/nervous-rivers-sneeze.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Switch out `sucrase` for `swc` for transpilation. - -`sucrase` is a little more relaxed when it comes to supporting the ways of mocking in `jest`. You might have to make some changes to your tests to meet the `jest` standard and spec if your tests seems to start failing. - -Mocks that look like this are invalid, and they will throw a reference error in line with the `jest` documentation [here on example 3](https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter) - -```ts -const mockCommandExists = jest.fn(); -jest.mock('command-exists', () => mockCommandExists); -``` - -You might need to update these mocks to look a little like the following to defer the call to the `jest.fn()` spy until the mock is called. - -```ts -const mockCommandExists = jest.fn(); -jest.mock( - 'command-exists', - () => - (...args: any[]) => - commandExists(...args), -); -``` - -Also, imports are immutable. So it means that you might get some errors when trying to use `jest.spyOn` with starred imports. You might see an error like this: - -```log -TypeError: Cannot redefine property: executeFrameHandlerStrategy - at Function.defineProperty () - - 20 | import { AuthResolverContext } from '../types'; - 21 | - > 22 | const mockFrameHandler = jest.spyOn( - | ^ - 23 | helpers, - 24 | 'executeFrameHandlerStrategy', - 25 | ) as unknown as jest.MockedFunction< -``` - -This happens when you try to do `import * as something from './something'` and then `jest.spyOn(something, 'test)`. You will need to add a `jest.mock` call to mock out the required starred import to return `jest.fn()` functions from the start. Something like this fixes the above test: - -```ts -jest.mock('../../helpers', () => ({ - executeFrameHandlerStrategy: jest.fn(), -})); -``` - -You can also remove any occurrence of `hot(App)` and any import of `react-hot-loader` if you're using the that package locally, as all this has now been replaced with [React Refresh](https://www.npmjs.com/package/react-refresh) which you will get out of the box with the new CLI. - -**Note** If you're experiencing difficulties with running tests after the migration, please reach out to us on Discord to see if we can help, or raise an issue. But in the meantime you can switch back to the existing behaviour by using the following config in your root `package.json`. - -```json -"jest": { - "transform": { - "\\.(js|jsx|ts|tsx|mjs|cjs)$": "@backstage/cli/config/jestSucraseTransform.js", - "\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg|eot|woff|woff2|ttf)$": "@backstage/cli/config/jestFileTransform.js", - "\\.(yaml)$": "jest-transform-yaml" - } -} -``` diff --git a/.changeset/new-taxis-vanish.md b/.changeset/new-taxis-vanish.md deleted file mode 100644 index 4d0c6d64b9..0000000000 --- a/.changeset/new-taxis-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Addition of a dismissible Error Banner in Scaffolder page diff --git a/.changeset/ninety-knives-knock.md b/.changeset/ninety-knives-knock.md deleted file mode 100644 index 19797e472c..0000000000 --- a/.changeset/ninety-knives-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins-backend': patch ---- - -Unwrap error message when getting projects diff --git a/.changeset/old-cobras-suffer.md b/.changeset/old-cobras-suffer.md new file mode 100644 index 0000000000..2891c29133 --- /dev/null +++ b/.changeset/old-cobras-suffer.md @@ -0,0 +1,6 @@ +--- +'@backstage/test-utils': patch +'@backstage/plugin-cicd-statistics': patch +--- + +Align on the version of `@material-ui/icons` used, to `^4.9.1` like other packages in the main repo diff --git a/.changeset/old-lemons-switch.md b/.changeset/old-lemons-switch.md deleted file mode 100644 index 62f8d099b3..0000000000 --- a/.changeset/old-lemons-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -The `RoutedTabs` component has been updated to be compatible with React Router v6 stable. diff --git a/.changeset/old-readers-provide.md b/.changeset/old-readers-provide.md deleted file mode 100644 index db8677b0ca..0000000000 --- a/.changeset/old-readers-provide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-pull-requests-board': patch ---- - -Clarified GitHub app permissions required by plugin diff --git a/.changeset/old-rockets-doubt.md b/.changeset/old-rockets-doubt.md deleted file mode 100644 index 459cd2790c..0000000000 --- a/.changeset/old-rockets-doubt.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Added port ranges in allowed hosts: - -```yaml -reading: - allow: - - host: *.examples.org:900-1000 -``` diff --git a/.changeset/old-tables-joke.md b/.changeset/old-tables-joke.md deleted file mode 100644 index d458244b3a..0000000000 --- a/.changeset/old-tables-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -Simplified the `ServiceFactory` type and removed `AnyServiceFactory`. diff --git a/.changeset/perfect-moose-drum.md b/.changeset/perfect-moose-drum.md new file mode 100644 index 0000000000..833e5eeb35 --- /dev/null +++ b/.changeset/perfect-moose-drum.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated lockfile parsing to have better support for Yarn 3. diff --git a/.changeset/pink-feet-repeat.md b/.changeset/pink-feet-repeat.md deleted file mode 100644 index c4ac7560a6..0000000000 --- a/.changeset/pink-feet-repeat.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/plugin-apache-airflow': patch ---- - -Got rid of usages of the uppercase String type diff --git a/.changeset/pink-moons-smell.md b/.changeset/pink-moons-smell.md deleted file mode 100644 index 77475295be..0000000000 --- a/.changeset/pink-moons-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Added `repositoryId` output when create a repository in Azure diff --git a/.changeset/popular-shirts-happen.md b/.changeset/popular-shirts-happen.md deleted file mode 100644 index 137354fe2b..0000000000 --- a/.changeset/popular-shirts-happen.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-permission-react': patch ---- - -**DEPRECATION**: The `PermissionedRoute` component has been deprecated in favor of the new `RequirePermission` component. This is because the usage pattern of `PermissionedRoute` is not compatible with React Router v6 stable. - -Embed the type from `react-router` instead of exporting it directly. diff --git a/.changeset/pre.json b/.changeset/pre.json index 028a1a665f..46123733ca 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -2,334 +2,178 @@ "mode": "pre", "tag": "next", "initialVersions": { - "example-app": "0.2.74", - "@backstage/app-defaults": "1.0.5", - "example-backend": "0.2.74", - "@backstage/backend-app-api": "0.2.0", - "@backstage/backend-common": "0.15.0", - "@backstage/backend-defaults": "0.1.0", - "example-backend-next": "0.0.2", - "@backstage/backend-plugin-api": "0.1.1", - "@backstage/backend-tasks": "0.3.4", - "@backstage/backend-test-utils": "0.1.27", - "@backstage/catalog-client": "1.0.4", - "@backstage/catalog-model": "1.1.0", - "@backstage/cli": "0.18.1", - "@backstage/cli-common": "0.1.9", - "@backstage/codemods": "0.1.38", - "@backstage/config": "1.0.1", - "@backstage/config-loader": "1.1.3", - "@backstage/core-app-api": "1.0.5", - "@backstage/core-components": "0.11.0", - "@backstage/core-plugin-api": "1.0.5", - "@backstage/create-app": "0.4.30", - "@backstage/dev-utils": "1.0.5", + "example-app": "0.2.75", + "@backstage/app-defaults": "1.0.6", + "example-backend": "0.2.75", + "@backstage/backend-app-api": "0.2.1", + "@backstage/backend-common": "0.15.1", + "@backstage/backend-defaults": "0.1.1", + "example-backend-next": "0.0.3", + "@backstage/backend-plugin-api": "0.1.2", + "@backstage/backend-tasks": "0.3.5", + "@backstage/backend-test-utils": "0.1.28", + "@backstage/catalog-client": "1.1.0", + "@backstage/catalog-model": "1.1.1", + "@backstage/cli": "0.19.0", + "@backstage/cli-common": "0.1.10", + "@backstage/codemods": "0.1.39", + "@backstage/config": "1.0.2", + "@backstage/config-loader": "1.1.4", + "@backstage/core-app-api": "1.1.0", + "@backstage/core-components": "0.11.1", + "@backstage/core-plugin-api": "1.0.6", + "@backstage/create-app": "0.4.31", + "@backstage/dev-utils": "1.0.6", "e2e-test": "0.2.0", - "@backstage/errors": "1.1.0", - "@backstage/integration": "1.3.0", - "@backstage/integration-react": "1.1.3", - "@backstage/release-manifests": "0.0.5", - "@techdocs/cli": "1.2.0", - "techdocs-cli-embedded-app": "0.2.73", - "@backstage/test-utils": "1.1.3", + "@backstage/errors": "1.1.1", + "@backstage/integration": "1.3.1", + "@backstage/integration-react": "1.1.4", + "@backstage/release-manifests": "0.0.6", + "@techdocs/cli": "1.2.1", + "techdocs-cli-embedded-app": "0.2.74", + "@backstage/test-utils": "1.2.0", "@backstage/theme": "0.2.16", "@backstage/types": "1.0.0", "@backstage/version-bridge": "1.0.1", - "@backstage/plugin-adr": "0.2.0", - "@backstage/plugin-adr-backend": "0.2.0", - "@backstage/plugin-adr-common": "0.2.0", - "@backstage/plugin-airbrake": "0.3.8", - "@backstage/plugin-airbrake-backend": "0.2.8", - "@backstage/plugin-allure": "0.1.24", - "@backstage/plugin-analytics-module-ga": "0.1.19", - "@backstage/plugin-apache-airflow": "0.2.1", - "@backstage/plugin-api-docs": "0.8.8", + "@backstage/plugin-adr": "0.2.1", + "@backstage/plugin-adr-backend": "0.2.1", + "@backstage/plugin-adr-common": "0.2.1", + "@backstage/plugin-airbrake": "0.3.9", + "@backstage/plugin-airbrake-backend": "0.2.9", + "@backstage/plugin-allure": "0.1.25", + "@backstage/plugin-analytics-module-ga": "0.1.20", + "@backstage/plugin-apache-airflow": "0.2.2", + "@backstage/plugin-api-docs": "0.8.9", "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.0", - "@backstage/plugin-apollo-explorer": "0.1.1", - "@backstage/plugin-app-backend": "0.3.35", - "@backstage/plugin-auth-backend": "0.15.1", - "@backstage/plugin-auth-node": "0.2.4", - "@backstage/plugin-azure-devops": "0.1.24", - "@backstage/plugin-azure-devops-backend": "0.3.14", - "@backstage/plugin-azure-devops-common": "0.2.4", - "@backstage/plugin-badges": "0.2.32", - "@backstage/plugin-badges-backend": "0.1.29", - "@backstage/plugin-bazaar": "0.1.23", - "@backstage/plugin-bazaar-backend": "0.1.19", - "@backstage/plugin-bitbucket-cloud-common": "0.1.2", - "@backstage/plugin-bitrise": "0.1.35", - "@backstage/plugin-catalog": "1.5.0", - "@backstage/plugin-catalog-backend": "1.3.1", - "@backstage/plugin-catalog-backend-module-aws": "0.1.8", - "@backstage/plugin-catalog-backend-module-azure": "0.1.6", - "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.2", - "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.2", - "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.0", - "@backstage/plugin-catalog-backend-module-gerrit": "0.1.3", - "@backstage/plugin-catalog-backend-module-github": "0.1.6", - "@backstage/plugin-catalog-backend-module-gitlab": "0.1.6", - "@backstage/plugin-catalog-backend-module-ldap": "0.5.2", - "@backstage/plugin-catalog-backend-module-msgraph": "0.4.1", - "@backstage/plugin-catalog-backend-module-openapi": "0.1.1", - "@backstage/plugin-catalog-common": "1.0.5", - "@internal/plugin-catalog-customized": "0.0.1", - "@backstage/plugin-catalog-graph": "0.2.20", - "@backstage/plugin-catalog-graphql": "0.3.12", - "@backstage/plugin-catalog-import": "0.8.11", - "@backstage/plugin-catalog-node": "1.0.1", - "@backstage/plugin-catalog-react": "1.1.3", - "@backstage/plugin-cicd-statistics": "0.1.10", - "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.4", - "@backstage/plugin-circleci": "0.3.8", - "@backstage/plugin-cloudbuild": "0.3.8", - "@backstage/plugin-code-climate": "0.1.8", - "@backstage/plugin-code-coverage": "0.2.1", - "@backstage/plugin-code-coverage-backend": "0.2.1", - "@backstage/plugin-codescene": "0.1.3", - "@backstage/plugin-config-schema": "0.1.31", - "@backstage/plugin-cost-insights": "0.11.30", + "@backstage/plugin-apollo-explorer": "0.1.2", + "@backstage/plugin-app-backend": "0.3.36", + "@backstage/plugin-auth-backend": "0.16.0", + "@backstage/plugin-auth-node": "0.2.5", + "@backstage/plugin-azure-devops": "0.2.0", + "@backstage/plugin-azure-devops-backend": "0.3.15", + "@backstage/plugin-azure-devops-common": "0.3.0", + "@backstage/plugin-badges": "0.2.33", + "@backstage/plugin-badges-backend": "0.1.30", + "@backstage/plugin-bazaar": "0.1.24", + "@backstage/plugin-bazaar-backend": "0.1.20", + "@backstage/plugin-bitbucket-cloud-common": "0.1.3", + "@backstage/plugin-bitrise": "0.1.36", + "@backstage/plugin-catalog": "1.5.1", + "@backstage/plugin-catalog-backend": "1.4.0", + "@backstage/plugin-catalog-backend-module-aws": "0.1.9", + "@backstage/plugin-catalog-backend-module-azure": "0.1.7", + "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.3", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.3", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.1", + "@backstage/plugin-catalog-backend-module-gerrit": "0.1.4", + "@backstage/plugin-catalog-backend-module-github": "0.1.7", + "@backstage/plugin-catalog-backend-module-gitlab": "0.1.7", + "@backstage/plugin-catalog-backend-module-ldap": "0.5.3", + "@backstage/plugin-catalog-backend-module-msgraph": "0.4.2", + "@backstage/plugin-catalog-backend-module-openapi": "0.1.2", + "@backstage/plugin-catalog-common": "1.0.6", + "@internal/plugin-catalog-customized": "0.0.2", + "@backstage/plugin-catalog-graph": "0.2.21", + "@backstage/plugin-catalog-graphql": "0.3.13", + "@backstage/plugin-catalog-import": "0.8.12", + "@backstage/plugin-catalog-node": "1.1.0", + "@backstage/plugin-catalog-react": "1.1.4", + "@backstage/plugin-cicd-statistics": "0.1.11", + "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.5", + "@backstage/plugin-circleci": "0.3.9", + "@backstage/plugin-cloudbuild": "0.3.9", + "@backstage/plugin-code-climate": "0.1.9", + "@backstage/plugin-code-coverage": "0.2.2", + "@backstage/plugin-code-coverage-backend": "0.2.2", + "@backstage/plugin-codescene": "0.1.4", + "@backstage/plugin-config-schema": "0.1.32", + "@backstage/plugin-cost-insights": "0.11.31", "@backstage/plugin-cost-insights-common": "0.1.1", - "@backstage/plugin-dynatrace": "0.1.2", - "@internal/plugin-todo-list": "1.0.4", - "@internal/plugin-todo-list-backend": "1.0.4", - "@internal/plugin-todo-list-common": "1.0.3", - "@backstage/plugin-explore": "0.3.39", - "@backstage/plugin-explore-react": "0.0.20", - "@backstage/plugin-firehydrant": "0.1.25", - "@backstage/plugin-fossa": "0.2.40", - "@backstage/plugin-gcalendar": "0.3.4", - "@backstage/plugin-gcp-projects": "0.3.27", - "@backstage/plugin-git-release-manager": "0.3.21", - "@backstage/plugin-github-actions": "0.5.8", - "@backstage/plugin-github-deployments": "0.1.39", - "@backstage/plugin-github-issues": "0.1.0", - "@backstage/plugin-github-pull-requests-board": "0.1.2", - "@backstage/plugin-gitops-profiles": "0.3.26", - "@backstage/plugin-gocd": "0.1.14", - "@backstage/plugin-graphiql": "0.2.40", - "@backstage/plugin-graphql-backend": "0.1.25", - "@backstage/plugin-home": "0.4.24", - "@backstage/plugin-ilert": "0.1.34", - "@backstage/plugin-jenkins": "0.7.7", - "@backstage/plugin-jenkins-backend": "0.1.25", - "@backstage/plugin-jenkins-common": "0.1.7", - "@backstage/plugin-kafka": "0.3.8", - "@backstage/plugin-kafka-backend": "0.2.28", - "@backstage/plugin-kubernetes": "0.7.1", - "@backstage/plugin-kubernetes-backend": "0.7.1", - "@backstage/plugin-kubernetes-common": "0.4.1", - "@backstage/plugin-lighthouse": "0.3.8", - "@backstage/plugin-newrelic": "0.3.26", - "@backstage/plugin-newrelic-dashboard": "0.2.1", - "@backstage/plugin-org": "0.5.8", - "@backstage/plugin-pagerduty": "0.5.1", - "@backstage/plugin-periskop": "0.1.6", - "@backstage/plugin-periskop-backend": "0.1.6", - "@backstage/plugin-permission-backend": "0.5.10", - "@backstage/plugin-permission-common": "0.6.3", - "@backstage/plugin-permission-node": "0.6.4", - "@backstage/plugin-permission-react": "0.4.4", - "@backstage/plugin-proxy-backend": "0.2.29", - "@backstage/plugin-rollbar": "0.4.8", - "@backstage/plugin-rollbar-backend": "0.1.32", - "@backstage/plugin-scaffolder": "1.5.0", - "@backstage/plugin-scaffolder-backend": "1.5.0", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.10", - "@backstage/plugin-scaffolder-backend-module-rails": "0.4.3", - "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.8", - "@backstage/plugin-scaffolder-common": "1.1.2", - "@backstage/plugin-search": "1.0.1", - "@backstage/plugin-search-backend": "1.0.1", - "@backstage/plugin-search-backend-module-elasticsearch": "1.0.1", - "@backstage/plugin-search-backend-module-pg": "0.3.6", - "@backstage/plugin-search-backend-node": "1.0.1", - "@backstage/plugin-search-common": "1.0.0", - "@backstage/plugin-search-react": "1.0.1", - "@backstage/plugin-sentry": "0.4.1", - "@backstage/plugin-shortcuts": "0.3.0", - "@backstage/plugin-sonarqube": "0.4.0", - "@backstage/plugin-sonarqube-backend": "0.1.0", - "@backstage/plugin-splunk-on-call": "0.3.32", - "@backstage/plugin-stack-overflow": "0.1.4", - "@backstage/plugin-stack-overflow-backend": "0.1.4", - "@backstage/plugin-tech-insights": "0.2.4", - "@backstage/plugin-tech-insights-backend": "0.5.1", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.19", + "@backstage/plugin-dynatrace": "0.2.0", + "@internal/plugin-todo-list": "1.0.5", + "@internal/plugin-todo-list-backend": "1.0.5", + "@internal/plugin-todo-list-common": "1.0.4", + "@backstage/plugin-explore": "0.3.40", + "@backstage/plugin-explore-react": "0.0.21", + "@backstage/plugin-firehydrant": "0.1.26", + "@backstage/plugin-fossa": "0.2.41", + "@backstage/plugin-gcalendar": "0.3.5", + "@backstage/plugin-gcp-projects": "0.3.28", + "@backstage/plugin-git-release-manager": "0.3.22", + "@backstage/plugin-github-actions": "0.5.9", + "@backstage/plugin-github-deployments": "0.1.40", + "@backstage/plugin-github-issues": "0.1.1", + "@backstage/plugin-github-pull-requests-board": "0.1.3", + "@backstage/plugin-gitops-profiles": "0.3.27", + "@backstage/plugin-gocd": "0.1.15", + "@backstage/plugin-graphiql": "0.2.41", + "@backstage/plugin-graphql-backend": "0.1.26", + "@backstage/plugin-home": "0.4.25", + "@backstage/plugin-ilert": "0.1.35", + "@backstage/plugin-jenkins": "0.7.8", + "@backstage/plugin-jenkins-backend": "0.1.26", + "@backstage/plugin-jenkins-common": "0.1.8", + "@backstage/plugin-kafka": "0.3.9", + "@backstage/plugin-kafka-backend": "0.2.29", + "@backstage/plugin-kubernetes": "0.7.2", + "@backstage/plugin-kubernetes-backend": "0.7.2", + "@backstage/plugin-kubernetes-common": "0.4.2", + "@backstage/plugin-lighthouse": "0.3.9", + "@backstage/plugin-newrelic": "0.3.27", + "@backstage/plugin-newrelic-dashboard": "0.2.2", + "@backstage/plugin-org": "0.5.9", + "@backstage/plugin-pagerduty": "0.5.2", + "@backstage/plugin-periskop": "0.1.7", + "@backstage/plugin-periskop-backend": "0.1.7", + "@backstage/plugin-permission-backend": "0.5.11", + "@backstage/plugin-permission-common": "0.6.4", + "@backstage/plugin-permission-node": "0.6.5", + "@backstage/plugin-permission-react": "0.4.5", + "@backstage/plugin-playlist": "0.1.0", + "@backstage/plugin-playlist-backend": "0.1.0", + "@backstage/plugin-playlist-common": "0.1.0", + "@backstage/plugin-proxy-backend": "0.2.30", + "@backstage/plugin-rollbar": "0.4.9", + "@backstage/plugin-rollbar-backend": "0.1.33", + "@backstage/plugin-scaffolder": "1.6.0", + "@backstage/plugin-scaffolder-backend": "1.6.0", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.11", + "@backstage/plugin-scaffolder-backend-module-rails": "0.4.4", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.9", + "@backstage/plugin-scaffolder-common": "1.2.0", + "@backstage/plugin-search": "1.0.2", + "@backstage/plugin-search-backend": "1.0.2", + "@backstage/plugin-search-backend-module-elasticsearch": "1.0.2", + "@backstage/plugin-search-backend-module-pg": "0.4.0", + "@backstage/plugin-search-backend-node": "1.0.2", + "@backstage/plugin-search-common": "1.0.1", + "@backstage/plugin-search-react": "1.1.0", + "@backstage/plugin-sentry": "0.4.2", + "@backstage/plugin-shortcuts": "0.3.1", + "@backstage/plugin-sonarqube": "0.4.1", + "@backstage/plugin-sonarqube-backend": "0.1.1", + "@backstage/plugin-splunk-on-call": "0.3.33", + "@backstage/plugin-stack-overflow": "0.1.5", + "@backstage/plugin-stack-overflow-backend": "0.1.5", + "@backstage/plugin-tech-insights": "0.3.0", + "@backstage/plugin-tech-insights-backend": "0.5.2", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.20", "@backstage/plugin-tech-insights-common": "0.2.6", - "@backstage/plugin-tech-insights-node": "0.3.3", - "@backstage/plugin-tech-radar": "0.5.15", - "@backstage/plugin-techdocs": "1.3.1", - "@backstage/plugin-techdocs-addons-test-utils": "1.0.3", - "@backstage/plugin-techdocs-backend": "1.2.1", - "@backstage/plugin-techdocs-module-addons-contrib": "1.0.3", - "@backstage/plugin-techdocs-node": "1.3.0", - "@backstage/plugin-techdocs-react": "1.0.3", - "@backstage/plugin-todo": "0.2.10", - "@backstage/plugin-todo-backend": "0.1.32", - "@backstage/plugin-user-settings": "0.4.7", - "@backstage/plugin-vault": "0.1.2", - "@backstage/plugin-vault-backend": "0.2.1", - "@backstage/plugin-xcmetrics": "0.2.28" + "@backstage/plugin-tech-insights-node": "0.3.4", + "@backstage/plugin-tech-radar": "0.5.16", + "@backstage/plugin-techdocs": "1.3.2", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.4", + "@backstage/plugin-techdocs-backend": "1.3.0", + "@backstage/plugin-techdocs-module-addons-contrib": "1.0.4", + "@backstage/plugin-techdocs-node": "1.4.0", + "@backstage/plugin-techdocs-react": "1.0.4", + "@backstage/plugin-todo": "0.2.11", + "@backstage/plugin-todo-backend": "0.1.33", + "@backstage/plugin-user-settings": "0.4.8", + "@backstage/plugin-user-settings-backend": "0.1.0", + "@backstage/plugin-vault": "0.1.3", + "@backstage/plugin-vault-backend": "0.2.2", + "@backstage/plugin-xcmetrics": "0.2.29" }, - "changesets": [ - "afraid-garlics-glow", - "afraid-horses-lick", - "afraid-radios-boil", - "beige-coins-type", - "beige-monkeys-add", - "beige-pumas-tap", - "big-dolls-reflect", - "big-spies-check", - "big-teachers-tell", - "blue-snakes-compete", - "brave-steaks-lay", - "chatty-cars-kick", - "clever-beans-turn", - "clever-turtles-work", - "cold-frogs-kiss", - "cool-cameras-count", - "cuddly-clocks-dance", - "cuddly-needles-marry", - "curly-hounds-wait", - "dry-games-end", - "dry-tips-build", - "dull-spoons-doubt", - "eight-shrimps-call", - "eight-spies-protect", - "eleven-bees-marry", - "empty-colts-whisper", - "famous-cups-roll", - "famous-hounds-sit", - "famous-pianos-kick", - "famous-wombats-happen", - "fast-paws-press", - "five-carrots-pay", - "flat-crabs-love", - "flat-humans-dance", - "flat-plums-shout", - "flat-walls-kiss", - "fresh-rabbits-juggle", - "funny-hounds-obey", - "funny-years-speak", - "gold-fishes-yawn", - "gold-hounds-vanish", - "gold-laws-attend", - "good-avocados-grow", - "good-papayas-dress", - "gorgeous-boats-prove", - "gorgeous-pillows-retire", - "gorgeous-swans-kiss", - "great-cats-sit", - "great-rules-march", - "grumpy-kiwis-juggle", - "happy-kiwis-look", - "heavy-ligers-laugh", - "hot-brooms-drive", - "hot-files-begin", - "hot-suits-glow", - "hungry-dogs-agree", - "itchy-brooms-end", - "itchy-kiwis-warn", - "large-books-deny", - "large-eagles-shop", - "large-trainers-float", - "late-turtles-listen", - "lazy-snakes-film", - "light-beans-share", - "light-donuts-obey", - "loud-comics-search", - "lovely-ants-invite", - "lucky-ads-worry", - "lucky-points-wash", - "many-rules-raise", - "mean-tomatoes-visit", - "metal-candles-tease", - "metal-crabs-wash", - "metal-weeks-kiss", - "moody-berries-sneeze", - "neat-kangaroos-turn", - "nervous-rivers-sneeze", - "new-taxis-vanish", - "old-lemons-switch", - "old-readers-provide", - "old-rockets-doubt", - "old-tables-joke", - "pink-feet-repeat", - "pink-moons-smell", - "popular-shirts-happen", - "purple-jeans-bathe", - "purple-plums-travel", - "purple-wolves-confess", - "quick-items-invite", - "quick-lamps-talk", - "rare-tips-glow", - "red-numbers-suffer", - "renovate-16f36a0", - "renovate-1a6cc1f", - "renovate-2925783", - "renovate-4f0c2e3", - "renovate-58c2860", - "renovate-7d3b357", - "renovate-c98c336", - "renovate-dbf54c0", - "renovate-f18a0fc", - "rich-cooks-camp", - "rich-melons-compete", - "rotten-books-crash", - "rude-books-rush", - "rude-tips-argue", - "rude-weeks-retire", - "search-feet-flash", - "search-planets-flash", - "search-zebras-tap", - "selfish-wasps-cough", - "shaggy-chicken-behave", - "shaggy-vans-repeat", - "sharp-swans-suffer", - "shiny-lobsters-fix", - "shiny-ties-leave", - "shiny-walls-kiss", - "short-mice-explode", - "silent-kings-live", - "slimy-stingrays-type", - "slimy-zebras-reply", - "slow-phones-count", - "small-lemons-brake", - "small-walls-promise", - "smart-sloths-search", - "smart-squids-change", - "smooth-yaks-hug", - "spicy-cherries-remember", - "spotty-points-build", - "strong-games-kiss", - "strong-planes-return", - "stupid-pigs-appear", - "sweet-fishes-taste", - "sweet-grapes-explain", - "swift-readers-sin", - "tall-trains-remain", - "tame-papayas-protect", - "tame-socks-sniff", - "tame-trainers-rule", - "tasty-clouds-cover", - "techdocs-feet-dress", - "techdocs-jeans-wait", - "tender-ladybugs-relax", - "tender-pumpkins-burn", - "thin-cows-watch", - "tiny-oranges-thank", - "tiny-rocks-flash", - "tough-dolphins-smile", - "twenty-dolls-smoke", - "two-planets-provide", - "veka-fingrar-drar", - "weak-camels-roll", - "weak-radios-sin", - "weak-yaks-learn", - "wild-sheep-roll", - "witty-cats-wink", - "witty-queens-happen", - "young-falcons-sleep", - "young-trees-rescue" - ] + "changesets": [] } diff --git a/.changeset/purple-jeans-bathe.md b/.changeset/purple-jeans-bathe.md deleted file mode 100644 index 39706db2c4..0000000000 --- a/.changeset/purple-jeans-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Updated `ServiceRegistry` to not initialize factories more than once. diff --git a/.changeset/purple-plums-travel.md b/.changeset/purple-plums-travel.md deleted file mode 100644 index 0077b15808..0000000000 --- a/.changeset/purple-plums-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Fixed a misconfiguration where all modules where treated as ESM by the React Refresh plugin for Webpack. diff --git a/.changeset/purple-wolves-confess.md b/.changeset/purple-wolves-confess.md deleted file mode 100644 index 33333c1a1b..0000000000 --- a/.changeset/purple-wolves-confess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Removed internal dependencies on Lerna. It is now no longer necessary to have Lerna installed in a project to use all features of the Backstage CLI. diff --git a/.changeset/quick-items-invite.md b/.changeset/quick-items-invite.md deleted file mode 100644 index 33f90cb662..0000000000 --- a/.changeset/quick-items-invite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-node': patch ---- - -Updated usage of experimental backend service APIs. diff --git a/.changeset/quick-lamps-talk.md b/.changeset/quick-lamps-talk.md deleted file mode 100644 index 3450113520..0000000000 --- a/.changeset/quick-lamps-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins-backend': patch ---- - -Extend configuration so that extra headers can be sent to jenkins instance diff --git a/.changeset/quiet-hats-kick.md b/.changeset/quiet-hats-kick.md new file mode 100644 index 0000000000..a0bbab4cb1 --- /dev/null +++ b/.changeset/quiet-hats-kick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-jenkins-backend': patch +--- + +Fixed a bug where `extraRequestHeaders` configuration was ignored. diff --git a/.changeset/rare-tips-glow.md b/.changeset/rare-tips-glow.md deleted file mode 100644 index 6667fd5871..0000000000 --- a/.changeset/rare-tips-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Reset error state on success diff --git a/.changeset/red-numbers-suffer.md b/.changeset/red-numbers-suffer.md deleted file mode 100644 index c1baae1e92..0000000000 --- a/.changeset/red-numbers-suffer.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -'@backstage/plugin-allure': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-bitrise': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-catalog-graphql': patch -'@backstage/plugin-cicd-statistics': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-explore-react': patch -'@backstage/plugin-firehydrant': patch -'@backstage/plugin-gcalendar': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-github-deployments': patch -'@backstage/plugin-github-pull-requests-board': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-graphql-backend': patch -'@backstage/plugin-home': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-kubernetes-common': patch -'@backstage/plugin-newrelic': patch -'@backstage/plugin-newrelic-dashboard': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-search-backend-module-pg': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-xcmetrics': patch ---- - -Minor API signatures cleanup diff --git a/.changeset/renovate-16f36a0.md b/.changeset/renovate-16f36a0.md deleted file mode 100644 index 74c33ec0a7..0000000000 --- a/.changeset/renovate-16f36a0.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-graphql-backend': patch -'@backstage/plugin-kubernetes-backend': patch -'@backstage/plugin-vault-backend': patch ---- - -Updated dependency `helmet` to `^6.0.0`. - -Please note that these policies are no longer applied by default: - -helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default -helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. diff --git a/.changeset/renovate-1a6cc1f.md b/.changeset/renovate-1a6cc1f.md deleted file mode 100644 index ea50aeb8f5..0000000000 --- a/.changeset/renovate-1a6cc1f.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/backend-test-utils': patch -'@backstage/catalog-client': patch -'@backstage/cli': patch -'@backstage/config-loader': patch -'@backstage/core-app-api': patch -'@backstage/core-components': patch -'@backstage/core-plugin-api': patch -'@backstage/integration-react': patch -'@backstage/integration': patch -'@backstage/release-manifests': patch -'@backstage/test-utils': patch -'@backstage/plugin-adr-backend': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-airbrake-backend': patch -'@backstage/plugin-airbrake': patch -'@backstage/plugin-allure': patch -'@backstage/plugin-analytics-module-ga': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-apollo-explorer': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-auth-node': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-bitbucket-cloud-common': patch -'@backstage/plugin-bitrise': patch -'@backstage/plugin-catalog-backend-module-azure': patch -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch -'@backstage/plugin-catalog-backend-module-bitbucket-server': patch -'@backstage/plugin-catalog-backend-module-bitbucket': patch -'@backstage/plugin-catalog-backend-module-gerrit': patch -'@backstage/plugin-catalog-backend-module-github': patch -'@backstage/plugin-catalog-backend-module-gitlab': patch -'@backstage/plugin-catalog-backend-module-msgraph': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-catalog-graphql': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-code-coverage-backend': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-codescene': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-explore-react': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-firehydrant': patch -'@backstage/plugin-fossa': patch -'@backstage/plugin-gcalendar': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-github-deployments': patch -'@backstage/plugin-github-issues': patch -'@backstage/plugin-github-pull-requests-board': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-gocd': patch -'@backstage/plugin-graphiql': patch -'@backstage/plugin-graphql-backend': patch -'@backstage/plugin-home': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-newrelic': patch -'@backstage/plugin-org': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-periskop-backend': patch -'@backstage/plugin-periskop': patch -'@backstage/plugin-permission-backend': patch -'@backstage/plugin-permission-common': patch -'@backstage/plugin-permission-node': patch -'@backstage/plugin-proxy-backend': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-search': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-sonarqube-backend': patch -'@backstage/plugin-sonarqube': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-stack-overflow': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-techdocs-addons-test-utils': patch -'@backstage/plugin-techdocs-backend': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-todo-backend': patch -'@backstage/plugin-todo': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-vault-backend': patch -'@backstage/plugin-vault': patch -'@backstage/plugin-xcmetrics': patch ---- - -Updated dependency `msw` to `^0.47.0`. diff --git a/.changeset/renovate-2925783.md b/.changeset/renovate-2925783.md deleted file mode 100644 index 0dffcb1b15..0000000000 --- a/.changeset/renovate-2925783.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/backend-test-utils': patch -'@backstage/catalog-client': patch -'@backstage/cli': patch -'@backstage/config-loader': patch -'@backstage/core-app-api': patch -'@backstage/core-components': patch -'@backstage/core-plugin-api': patch -'@backstage/integration-react': patch -'@backstage/integration': patch -'@backstage/release-manifests': patch -'@backstage/test-utils': patch -'@backstage/plugin-adr-backend': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-airbrake-backend': patch -'@backstage/plugin-airbrake': patch -'@backstage/plugin-allure': patch -'@backstage/plugin-analytics-module-ga': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-apollo-explorer': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-auth-node': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-bitbucket-cloud-common': patch -'@backstage/plugin-bitrise': patch -'@backstage/plugin-catalog-backend-module-azure': patch -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch -'@backstage/plugin-catalog-backend-module-bitbucket-server': patch -'@backstage/plugin-catalog-backend-module-bitbucket': patch -'@backstage/plugin-catalog-backend-module-gerrit': patch -'@backstage/plugin-catalog-backend-module-github': patch -'@backstage/plugin-catalog-backend-module-gitlab': patch -'@backstage/plugin-catalog-backend-module-msgraph': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-catalog-graphql': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-code-coverage-backend': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-codescene': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-explore-react': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-firehydrant': patch -'@backstage/plugin-fossa': patch -'@backstage/plugin-gcalendar': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-github-deployments': patch -'@backstage/plugin-github-issues': patch -'@backstage/plugin-github-pull-requests-board': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-gocd': patch -'@backstage/plugin-graphiql': patch -'@backstage/plugin-graphql-backend': patch -'@backstage/plugin-home': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-newrelic': patch -'@backstage/plugin-org': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-periskop-backend': patch -'@backstage/plugin-periskop': patch -'@backstage/plugin-permission-backend': patch -'@backstage/plugin-permission-common': patch -'@backstage/plugin-permission-node': patch -'@backstage/plugin-proxy-backend': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-search': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-sonarqube-backend': patch -'@backstage/plugin-sonarqube': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-stack-overflow': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-techdocs-addons-test-utils': patch -'@backstage/plugin-techdocs-backend': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-todo-backend': patch -'@backstage/plugin-todo': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-vault-backend': patch -'@backstage/plugin-vault': patch -'@backstage/plugin-xcmetrics': patch ---- - -Updated dependency `msw` to `^0.46.0`. diff --git a/.changeset/renovate-4f0c2e3.md b/.changeset/renovate-4f0c2e3.md deleted file mode 100644 index 0aab9dc9a6..0000000000 --- a/.changeset/renovate-4f0c2e3.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/cli': minor ---- - -Updated dependency `eslint-plugin-jest` to `^27.0.0`. - -Note that this major update to the Jest plugin contains some breaking changes. -This means that some of your tests may start seeing some new lint errors. [Read -about them -here](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md#2700-2022-08-28). - -These are mostly possible to fix automatically. You can try to run `yarn backstage-cli repo lint --fix` in your repo root to have most or all of them -corrected. diff --git a/.changeset/renovate-58c2860.md b/.changeset/renovate-58c2860.md deleted file mode 100644 index a03ccdf6f8..0000000000 --- a/.changeset/renovate-58c2860.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins-backend': patch ---- - -Updated dependency `jenkins` to `^1.0.0`. diff --git a/.changeset/renovate-7d3b357.md b/.changeset/renovate-7d3b357.md deleted file mode 100644 index bdb86d12c2..0000000000 --- a/.changeset/renovate-7d3b357.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Updated dependency `json-schema-library` to `^7.0.0`. diff --git a/.changeset/renovate-a02d90b.md b/.changeset/renovate-a02d90b.md new file mode 100644 index 0000000000..7bb69d9988 --- /dev/null +++ b/.changeset/renovate-a02d90b.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Updated dependency `@asyncapi/react-component` to `1.0.0-next.42`. diff --git a/.changeset/renovate-c98c336.md b/.changeset/renovate-c98c336.md deleted file mode 100644 index b2139cfe20..0000000000 --- a/.changeset/renovate-c98c336.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/backend-test-utils': patch -'@backstage/catalog-client': patch -'@backstage/cli': patch -'@backstage/config-loader': patch -'@backstage/core-app-api': patch -'@backstage/core-components': patch -'@backstage/core-plugin-api': patch -'@backstage/integration-react': patch -'@backstage/integration': patch -'@backstage/release-manifests': patch -'@backstage/test-utils': patch -'@backstage/plugin-adr-backend': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-airbrake-backend': patch -'@backstage/plugin-airbrake': patch -'@backstage/plugin-allure': patch -'@backstage/plugin-analytics-module-ga': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-apollo-explorer': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-auth-backend': patch -'@backstage/plugin-auth-node': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-azure-devops': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-bitbucket-cloud-common': patch -'@backstage/plugin-bitrise': patch -'@backstage/plugin-catalog-backend-module-azure': patch -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch -'@backstage/plugin-catalog-backend-module-bitbucket-server': patch -'@backstage/plugin-catalog-backend-module-bitbucket': patch -'@backstage/plugin-catalog-backend-module-gerrit': patch -'@backstage/plugin-catalog-backend-module-github': patch -'@backstage/plugin-catalog-backend-module-gitlab': patch -'@backstage/plugin-catalog-backend-module-msgraph': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-catalog-graphql': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-code-coverage-backend': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-codescene': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-cost-insights': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-explore-react': patch -'@backstage/plugin-explore': patch -'@backstage/plugin-firehydrant': patch -'@backstage/plugin-fossa': patch -'@backstage/plugin-gcalendar': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-github-deployments': patch -'@backstage/plugin-github-issues': patch -'@backstage/plugin-github-pull-requests-board': patch -'@backstage/plugin-gitops-profiles': patch -'@backstage/plugin-gocd': patch -'@backstage/plugin-graphiql': patch -'@backstage/plugin-graphql-backend': patch -'@backstage/plugin-home': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-jenkins': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-newrelic': patch -'@backstage/plugin-org': patch -'@backstage/plugin-pagerduty': patch -'@backstage/plugin-periskop-backend': patch -'@backstage/plugin-periskop': patch -'@backstage/plugin-permission-backend': patch -'@backstage/plugin-permission-common': patch -'@backstage/plugin-permission-node': patch -'@backstage/plugin-proxy-backend': patch -'@backstage/plugin-rollbar-backend': patch -'@backstage/plugin-rollbar': patch -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-search': patch -'@backstage/plugin-sentry': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-sonarqube-backend': patch -'@backstage/plugin-sonarqube': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-stack-overflow': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-tech-radar': patch -'@backstage/plugin-techdocs-addons-test-utils': patch -'@backstage/plugin-techdocs-backend': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-todo-backend': patch -'@backstage/plugin-todo': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-vault-backend': patch -'@backstage/plugin-vault': patch -'@backstage/plugin-xcmetrics': patch ---- - -Updated dependency `msw` to `^0.45.0`. diff --git a/.changeset/renovate-dbf54c0.md b/.changeset/renovate-dbf54c0.md deleted file mode 100644 index 5ddae2f31e..0000000000 --- a/.changeset/renovate-dbf54c0.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-module-elasticsearch': patch ---- - -Updated dependency `aws-os-connection` to `^0.2.0`. diff --git a/.changeset/renovate-f18a0fc.md b/.changeset/renovate-f18a0fc.md deleted file mode 100644 index 6f17cb8c0f..0000000000 --- a/.changeset/renovate-f18a0fc.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Updated dependency `@types/minimatch` to `^5.0.0`. diff --git a/.changeset/rich-carrots-reflect.md b/.changeset/rich-carrots-reflect.md new file mode 100644 index 0000000000..13a67bcd56 --- /dev/null +++ b/.changeset/rich-carrots-reflect.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Switched the Jest transform for YAML files to use a custom one available at `@backstage/cli/config/jestYamlTransform.js`. diff --git a/.changeset/rich-cooks-camp.md b/.changeset/rich-cooks-camp.md deleted file mode 100644 index 04d86033f7..0000000000 --- a/.changeset/rich-cooks-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Cloudflare Access Provider: Add JWT to CloudflareAccessResult diff --git a/.changeset/rich-melons-compete.md b/.changeset/rich-melons-compete.md deleted file mode 100644 index 68297159f7..0000000000 --- a/.changeset/rich-melons-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-gcalendar': patch ---- - -Upgrade `react-query:3` to `@tanstack/react-query:4` diff --git a/.changeset/rotten-books-crash.md b/.changeset/rotten-books-crash.md deleted file mode 100644 index b0c020a743..0000000000 --- a/.changeset/rotten-books-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/dev-utils': patch ---- - -Removed the dependency and setup of `react-hot-loader`, since the `@backstage/cli` now uses `swc` with `React Refresh` instead. diff --git a/.changeset/rude-books-rush.md b/.changeset/rude-books-rush.md deleted file mode 100644 index a98e90e27e..0000000000 --- a/.changeset/rude-books-rush.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/backend-tasks': patch -'@backstage/plugin-app-backend': patch -'@backstage/plugin-bazaar-backend': patch -'@backstage/plugin-code-coverage-backend': patch -'@backstage/plugin-tech-insights-backend': patch ---- - -Fixed a bug where the database option to skip migrations was ignored. diff --git a/.changeset/rude-bulldogs-sleep.md b/.changeset/rude-bulldogs-sleep.md new file mode 100644 index 0000000000..8a2a0590b3 --- /dev/null +++ b/.changeset/rude-bulldogs-sleep.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Allow Placeholder value to be any value, not only string. diff --git a/.changeset/rude-tips-argue.md b/.changeset/rude-tips-argue.md deleted file mode 100644 index a66e0935b9..0000000000 --- a/.changeset/rude-tips-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/integration': patch ---- - -Fixed bug in the `bitbucketServer` integration where token did not take precedence over supplied username and password which is described in the documentation. diff --git a/.changeset/rude-weeks-retire.md b/.changeset/rude-weeks-retire.md deleted file mode 100644 index c2073c5826..0000000000 --- a/.changeset/rude-weeks-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Added the `refresh` function to the Connection of the entity providers. diff --git a/.changeset/search-countries-exercise.md b/.changeset/search-countries-exercise.md deleted file mode 100644 index 390813444f..0000000000 --- a/.changeset/search-countries-exercise.md +++ /dev/null @@ -1,283 +0,0 @@ ---- -'@backstage/plugin-search-react': minor ---- - -The `` component now accepts a optional `query` prop to request results from the search api: - -> Note: If a query prop is not defined, the results will by default be consumed from the context. - -Example: - -```jsx -import React, { useState, useCallback } from 'react'; - -import { Grid, List, Paper } from '@material-ui/core'; - -import { Page, Header, Content, Lifecycle } from '@backstage/core-components'; -import { - DefaultResultListItem, - SearchBarBase, - SearchResult, -} from '@backstage/plugin-search-react'; - -const SearchPage = () => { - const [query, setQuery] = useState({ - term: '', - types: [], - filters: {}, - }); - - const handleChange = useCallback( - (term: string) => { - setQuery(prevQuery => ({ ...prevQuery, term })); - }, - [setQuery], - ); - - return ( - -
} /> - - - - - - - - - - {({ results }) => ( - - {results.map(({ document }) => ( - - ))} - - )} - - - - - - ); -}; -``` - -Additionally, a search page can also be composed using these two new results layout components: - -```jsx -// Example rendering results as list - - {({ results }) => ( - { - switch (type) { - case 'custom-result-item': - return ( - - ); - default: - return ( - - ); - } - }} - /> - )} - -``` - -```jsx -// Example rendering results as groups - - {({ results }) => ( - <> - } - title="Custom" - link="See all custom results" - resultItems={results.filter( - ({ type }) => type === 'custom-result-item', - )} - renderResultItem={({ document }) => ( - - )} - /> - } - title="Default" - resultItems={results.filter( - ({ type }) => type !== 'custom-result-item', - )} - renderResultItem={({ document }) => ( - - )} - /> - - )} - -``` - -A `SearchResultList` and `SearchResultGroup` components were also created for users who have search pages with multiple queries, both are specializations of `SearchResult` and also accept a `query` as a prop as well: - -```jsx -// Example using the -const SearchPage = () => { - const query = { - term: 'example', - }; - - return ( - { - switch (type) { - case 'custom': - return ( - } - result={document} - highlight={highlight} - rank={rank} - /> - ); - default: - return ( - - ); - } - }} - /> - ); -}; -``` - -```jsx -// Example using the for creating a component that search and group software catalog results -import React, { useState, useCallback } from 'react'; - -import { MenuItem } from '@material-ui/core'; - -import { JsonValue } from '@backstage/types'; -import { CatalogIcon } from '@backstage/core-components'; -import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; -import { - SearchResultGroup, - SearchResultGroupTextFilterField, - SearchResultGroupSelectFilterField, -} from @backstage/plugin-search-react; -import { SearchQuery } from '@backstage/plugin-search-common'; - -const CatalogResultsGroup = () => { - const [query, setQuery] = useState>({ - types: ['software-catalog'], - }); - - const filterOptions = [ - { - label: 'Lifecycle', - value: 'lifecycle', - }, - { - label: 'Owner', - value: 'owner', - }, - ]; - - const handleFilterAdd = useCallback( - (key: string) => () => { - setQuery(prevQuery => { - const { filters: prevFilters, ...rest } = prevQuery; - const newFilters = { ...prevFilters, [key]: undefined }; - return { ...rest, filters: newFilters }; - }); - }, - [], - ); - - const handleFilterChange = useCallback( - (key: string) => (value: JsonValue) => { - setQuery(prevQuery => { - const { filters: prevFilters, ...rest } = prevQuery; - const newFilters = { ...prevFilters, [key]: value }; - return { ...rest, filters: newFilters }; - }); - }, - [], - ); - - const handleFilterDelete = useCallback( - (key: string) => () => { - setQuery(prevQuery => { - const { filters: prevFilters, ...rest } = prevQuery; - const newFilters = { ...prevFilters }; - delete newFilters[key]; - return { ...rest, filters: newFilters }; - }); - }, - [], - ); - - return ( - } - title="Software Catalog" - link="See all software catalog results" - filterOptions={filterOptions} - renderFilterOption={({ label, value }) => ( - - {label} - - )} - renderFilterField={(key: string) => { - switch (key) { - case 'lifecycle': - return ( - - Production - Experimental - - ); - case 'owner': - return ( - - ); - default: - return null; - } - } - renderResultItem={({ document, highlight, rank }) => ( - - )} - /> - ); -}; -``` diff --git a/.changeset/search-feet-flash.md b/.changeset/search-feet-flash.md deleted file mode 100644 index c7423f0317..0000000000 --- a/.changeset/search-feet-flash.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -'@backstage/plugin-search-react': minor ---- - -Provides search autocomplete functionality through a `SearchAutocomplete` component. -A `SearchAutocompleteDefaultOption` can also be used to render options with icons, primary texts, and secondary texts. -Example: - -```jsx -import React, { ChangeEvent, useState, useCallback } from 'react'; -import useAsync from 'react-use/lib/useAsync'; - -import { Grid, Paper } from '@material-ui/core'; - -import { Page, Content } from '@backstage/core-components'; -import { SearchAutocomplete, SearchAutocompleteDefaultOption} from '@backstage/plugin-search-react'; - -const OptionsIcon = () => - -const SearchPage = () => { - const [inputValue, setInputValue] = useState(''); - - const options = useAsync(async () => { - // Gets and returns autocomplete options - }, [inputValue]) - - const useCallback((_event: ChangeEvent<{}>, newInputValue: string) => { - setInputValue(newInputValue); - }, [setInputValue]) - - return ( - - - - - - option.title} - renderOption={option => ( - } - primaryText={option.title} - secondaryText={option.text} - /> - )} - /> - - - - {'/* Filters and results are omitted */'} - - - ); -}; -``` diff --git a/.changeset/search-planets-flash.md b/.changeset/search-planets-flash.md deleted file mode 100644 index 49431128f9..0000000000 --- a/.changeset/search-planets-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search': patch ---- - -Use the new `inheritParentContextIfAvailable` search context property in `SearchModal` instead of manually checking if a parent context exists, this conditional statement was previously duplicated in more than one component like in `SearchBar` as well and is now only done in ` SearchContextProvider`. diff --git a/.changeset/search-zebras-tap.md b/.changeset/search-zebras-tap.md deleted file mode 100644 index 4593478a51..0000000000 --- a/.changeset/search-zebras-tap.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-search-react': minor ---- - -We noticed a repeated check for the existence of a parent context before creating a child search context in more the one component such as Search Modal and Search Bar and to remove code duplication we extract the conditional to the context provider, now you can use it passing an `inheritParentContextIfAvailable` prop to the `SearchContextProvider`. - -Note: This added property does not create a local context if there is a parent context and in this case, you cannot use it together with `initialState`, it will result in a type error because the parent context is already initialized. diff --git a/.changeset/selfish-turkeys-exist.md b/.changeset/selfish-turkeys-exist.md new file mode 100644 index 0000000000..3530b3a864 --- /dev/null +++ b/.changeset/selfish-turkeys-exist.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder': minor +--- + +EntityPickers now support flags to control when to include default namespace +in result diff --git a/.changeset/selfish-wasps-cough.md b/.changeset/selfish-wasps-cough.md deleted file mode 100644 index cc00520385..0000000000 --- a/.changeset/selfish-wasps-cough.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Added `yarn new` as one of the scripts installed by default, which calls `backstage-cli new`. This script replaces `create-plugin`, which you can now remove if you want to. It is kept in the `create-app` template for backwards compatibility. - -The `remove-plugin` command has been removed, as it has been removed from the Backstage CLI. - -To apply these changes to an existing app, make the following change to the root `package.json`: - -```diff -- "remove-plugin": "backstage-cli remove-plugin" -+ "new": "backstage-cli new --scope internal" -``` diff --git a/.changeset/shaggy-books-smell.md b/.changeset/shaggy-books-smell.md new file mode 100644 index 0000000000..3f8aaa389c --- /dev/null +++ b/.changeset/shaggy-books-smell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-insights-node': patch +--- + +Updated package role to be `node-library`. diff --git a/.changeset/shaggy-chicken-behave.md b/.changeset/shaggy-chicken-behave.md deleted file mode 100644 index 41445b0930..0000000000 --- a/.changeset/shaggy-chicken-behave.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor -'@backstage/plugin-scaffolder-common': minor ---- - -add entity metadata to the template info type diff --git a/.changeset/shaggy-vans-repeat.md b/.changeset/shaggy-vans-repeat.md deleted file mode 100644 index fd865c702c..0000000000 --- a/.changeset/shaggy-vans-repeat.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -The options part of `DatabaseManager.fromConfig` now accepts an optional logger -field. You may want to supply that logger in your backend initialization code to -ensure that you can get relevant logging data when things happen related to the -connection pool. - -In `packages/backend/src/index.ts`: - -```diff - function makeCreateEnv(config: Config) { - const root = getRootLogger(); - ... -- const databaseManager = DatabaseManager.fromConfig(config); -+ const databaseManager = DatabaseManager.fromConfig(config, { logger: root }); -``` diff --git a/.changeset/sharp-swans-suffer.md b/.changeset/sharp-swans-suffer.md deleted file mode 100644 index 841d746634..0000000000 --- a/.changeset/sharp-swans-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor ---- - -Added support for `async` validation for the `next` version of the plugin diff --git a/.changeset/shiny-lobsters-fix.md b/.changeset/shiny-lobsters-fix.md deleted file mode 100644 index 99e255acbe..0000000000 --- a/.changeset/shiny-lobsters-fix.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -'@backstage/plugin-azure-devops-backend': patch ---- - -`createRouter` now requires an additional reader: `UrlReader` argument - -```diff -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - return createRouter({ - logger: env.logger, - config: env.config, -+ reader: env.reader, - }); -} -``` - -Remember to check if you have already provided these settings previously. - -#### [Azure DevOps] - -```yaml -# app-config.yaml -azureDevOps: - host: dev.azure.com - token: my-token - organization: my-company -``` - -#### [Azure Integrations] - -```yaml -# app-config.yaml -integrations: - azure: - - host: dev.azure.com - token: ${AZURE_TOKEN} -``` diff --git a/.changeset/shiny-ties-leave.md b/.changeset/shiny-ties-leave.md deleted file mode 100644 index 8e146e9af1..0000000000 --- a/.changeset/shiny-ties-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-github': patch ---- - -Add support for including (or excluding) Github repositories by topic diff --git a/.changeset/shiny-walls-kiss.md b/.changeset/shiny-walls-kiss.md deleted file mode 100644 index 0f13acb154..0000000000 --- a/.changeset/shiny-walls-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-stack-overflow': patch ---- - -Create a front end API. diff --git a/.changeset/short-mice-explode.md b/.changeset/short-mice-explode.md deleted file mode 100644 index 42ebfa9f5c..0000000000 --- a/.changeset/short-mice-explode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -The `createServiceFactory` method has been updated to return a higher-order factory that can accept options. diff --git a/.changeset/silent-kings-live.md b/.changeset/silent-kings-live.md deleted file mode 100644 index 7e55f5ae78..0000000000 --- a/.changeset/silent-kings-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Removed the depreacated `publish:file` action, use the template editor to test templates instead. diff --git a/.changeset/slimy-stingrays-type.md b/.changeset/slimy-stingrays-type.md deleted file mode 100644 index 9c520cf1d0..0000000000 --- a/.changeset/slimy-stingrays-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -fix uiSchema generation when using complex dependencies diff --git a/.changeset/slimy-zebras-reply.md b/.changeset/slimy-zebras-reply.md deleted file mode 100644 index 5b309f5129..0000000000 --- a/.changeset/slimy-zebras-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-ldap': patch ---- - -Now the `searchStream` method in LDAP client awaits the callbacks diff --git a/.changeset/slow-phones-count.md b/.changeset/slow-phones-count.md deleted file mode 100644 index ce2f9c8432..0000000000 --- a/.changeset/slow-phones-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Updated service implementations and backend wiring to support scoped service. diff --git a/.changeset/small-lemons-brake.md b/.changeset/small-lemons-brake.md deleted file mode 100644 index ac7906fc28..0000000000 --- a/.changeset/small-lemons-brake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Include entity ref into error message when catalog policies fail diff --git a/.changeset/small-walls-promise.md b/.changeset/small-walls-promise.md deleted file mode 100644 index 0455b0d776..0000000000 --- a/.changeset/small-walls-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-insights-backend': patch ---- - -Changed the description of the `techdocsAnnotationFactName` fact. diff --git a/.changeset/smart-sloths-search.md b/.changeset/smart-sloths-search.md deleted file mode 100644 index 542928debc..0000000000 --- a/.changeset/smart-sloths-search.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-auth-backend': minor ---- - -The auth0 integration is updated to use the `passport-auth0` library. The configuration under `auth.providers.auth0.\*` now supports an optional `audience` parameter; providing that allows you to connect to the correct API to get permissions, access tokens, and full profile information. - -[What is an Audience](https://community.auth0.com/t/what-is-the-audience/71414) diff --git a/.changeset/smart-squids-change.md b/.changeset/smart-squids-change.md deleted file mode 100644 index 51396114c9..0000000000 --- a/.changeset/smart-squids-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added a new `backstage-cli repo clean` command that cleans the repo root and runs the clean script in all packages. diff --git a/.changeset/smooth-camels-melt.md b/.changeset/smooth-camels-melt.md deleted file mode 100644 index 7d76bbd9ba..0000000000 --- a/.changeset/smooth-camels-melt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -The `branch` command has been added to the `isomorphic-git` wrapper. diff --git a/.changeset/smooth-yaks-hug.md b/.changeset/smooth-yaks-hug.md deleted file mode 100644 index d93176540f..0000000000 --- a/.changeset/smooth-yaks-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-github': patch ---- - -Added the ability to configure the host for the `GitHubEntityProvider` to use against GitHub Enterprise diff --git a/.changeset/soft-falcons-love.md b/.changeset/soft-falcons-love.md new file mode 100644 index 0000000000..56ed193501 --- /dev/null +++ b/.changeset/soft-falcons-love.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-user-settings': minor +--- + +**BREAKING**: The `apiRef` passed to `ProviderSettingsItem` now needs to +implement `ProfileInfoApi & SessionApi`, rather than just the latter. This is +unlikely to have an effect on most users though, since the builtin auth +providers generally implement both. + +Fixed settings page showing providers as logged out when the user is using more +than one provider, and displayed some additional login information. diff --git a/.changeset/spicy-cherries-remember.md b/.changeset/spicy-cherries-remember.md deleted file mode 100644 index c742c7a1e8..0000000000 --- a/.changeset/spicy-cherries-remember.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/backend-app-api': patch -'@backstage/backend-defaults': patch -'@backstage/backend-test-utils': patch ---- - -Updated usages of `ServiceFactory`. diff --git a/.changeset/spotty-points-build.md b/.changeset/spotty-points-build.md deleted file mode 100644 index ffed46163d..0000000000 --- a/.changeset/spotty-points-build.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Limit the length of error messages that get written to the database and logs - to prevent performance issues diff --git a/.changeset/stale-paws-dance.md b/.changeset/stale-paws-dance.md deleted file mode 100644 index 2745faea1c..0000000000 --- a/.changeset/stale-paws-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins-backend': patch ---- - -Jenkins plugin works again diff --git a/.changeset/strong-games-kiss.md b/.changeset/strong-games-kiss.md deleted file mode 100644 index a5f6d5f864..0000000000 --- a/.changeset/strong-games-kiss.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-catalog-node': patch ---- - -The experimental `CatalogProcessingExtensionPoint` now accepts multiple providers and processors at once. diff --git a/.changeset/strong-planes-return.md b/.changeset/strong-planes-return.md deleted file mode 100644 index 5b5648528a..0000000000 --- a/.changeset/strong-planes-return.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-tech-insights-backend': patch -'@backstage/plugin-tech-insights-node': patch ---- - -Support for timeout in FactRetrieverRegistrationOptions diff --git a/.changeset/stupid-dragons-complain.md b/.changeset/stupid-dragons-complain.md new file mode 100644 index 0000000000..44be8342bf --- /dev/null +++ b/.changeset/stupid-dragons-complain.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Increased test database max connection pool size to reduce the risk of resource exhaustion. diff --git a/.changeset/stupid-pigs-appear.md b/.changeset/stupid-pigs-appear.md deleted file mode 100644 index bd34f984d1..0000000000 --- a/.changeset/stupid-pigs-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Added alpha `scaffolderPlugin` to be used with experimental backend system. diff --git a/.changeset/sweet-fishes-taste.md b/.changeset/sweet-fishes-taste.md deleted file mode 100644 index 8d1fcfc790..0000000000 --- a/.changeset/sweet-fishes-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-pull-requests-board': patch ---- - -Add optional `pullRequestLimit` prop to `EntityTeamPullRequestsCard` and `EntityTeamPullRequestsContent` to limit the number of PRs shown per repository. Excluding this prop will default the number of pull requests shown to 10 per repository (the existing functionality). diff --git a/.changeset/sweet-grapes-explain.md b/.changeset/sweet-grapes-explain.md deleted file mode 100644 index 014fbb3d41..0000000000 --- a/.changeset/sweet-grapes-explain.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/integration': patch ---- - -Improved caching around github app tokens. -Tokens are now cached for 50 minutes, not 10. -Calls to get app installations are also included in this cache. -If you have more than one github app configured, consider adding `allowedInstallationOwners` to your apps configuration to gain the most benefit from these performance changes. diff --git a/.changeset/sweet-insects-camp.md b/.changeset/sweet-insects-camp.md new file mode 100644 index 0000000000..4f91a196e8 --- /dev/null +++ b/.changeset/sweet-insects-camp.md @@ -0,0 +1,11 @@ +--- +'@backstage/cli': minor +--- + +**BREAKING**: Bumped `jest`, `jest-runtime`, and `jest-environment-jsdom` to v29. This is up from v27, so check out both the [v28](https://jestjs.io/docs/28.x/upgrading-to-jest28) and [v29](https://jestjs.io/docs/upgrading-to-jest29) (later [here](https://jestjs.io/docs/29.x/upgrading-to-jest29)) migration guides. + +Particular changes that where encountered in the main Backstage repo are: + +- The updated snapshot format. +- `jest.useFakeTimers('legacy')` is now `jest.useFakeTimers({ legacyFakeTimers: true })`. +- Error objects collected by `withLogCollector` from `@backstage/test-utils` are now objects with a `detail` property rather than a string. diff --git a/.changeset/swift-phones-cheat.md b/.changeset/swift-phones-cheat.md new file mode 100644 index 0000000000..6d1653caa3 --- /dev/null +++ b/.changeset/swift-phones-cheat.md @@ -0,0 +1,12 @@ +--- +'@backstage/cli': patch +--- + +Removed `tsx` and `jsx` as supported extensions in backend packages. For most +repos, this will not have any effect. But if you inadvertently had added some +`tsx`/`jsx` files to your backend package, you may now start to see `code: 'MODULE_NOT_FOUND'` errors when launching the backend locally. The reason for +this is that the offending files get ignored during transpilation. Hence, the +importing file can no longer find anything to import. + +The fix is to rename any `.tsx` files in your backend packages to `.ts` instead, +or `.jsx` to `.js`. diff --git a/.changeset/swift-readers-sin.md b/.changeset/swift-readers-sin.md deleted file mode 100644 index 05bd05a94c..0000000000 --- a/.changeset/swift-readers-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Allow custom images in the empty state component diff --git a/.changeset/tall-trains-remain.md b/.changeset/tall-trains-remain.md deleted file mode 100644 index 9a94672656..0000000000 --- a/.changeset/tall-trains-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -Updated `FlatRoutes` to be compatible with React Router v6 stable. diff --git a/.changeset/tame-papayas-protect.md b/.changeset/tame-papayas-protect.md deleted file mode 100644 index 9a0266748a..0000000000 --- a/.changeset/tame-papayas-protect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': patch ---- - -When defining a new `ServiceRef` you can now also include a `defaultFactory`, which will be used to construct instances of the service in case there is no explicit factory defined. diff --git a/.changeset/tame-socks-sniff.md b/.changeset/tame-socks-sniff.md deleted file mode 100644 index b6cca23380..0000000000 --- a/.changeset/tame-socks-sniff.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/app-defaults': patch -'@backstage/test-utils': patch ---- - -Add missing peer dependencies diff --git a/.changeset/tame-trainers-rule.md b/.changeset/tame-trainers-rule.md deleted file mode 100644 index c51eb868d4..0000000000 --- a/.changeset/tame-trainers-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Disable octokit throttling in publish:github:pull-request diff --git a/.changeset/tasty-clouds-cover.md b/.changeset/tasty-clouds-cover.md deleted file mode 100644 index 8cc703c822..0000000000 --- a/.changeset/tasty-clouds-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added dependency on `@types/jest` v27. The `@types/jest` dependency has also been removed from the plugin template and should be removed from any of your own internal packages. If you wish to override the version of `@types/jest` or `jest`, use Yarn resolutions. diff --git a/.changeset/techdocs-feet-dress.md b/.changeset/techdocs-feet-dress.md deleted file mode 100644 index d2b2a625af..0000000000 --- a/.changeset/techdocs-feet-dress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Use the new `SearchAutocomplete` component in the `TechDocsSearch` component to maintain consistency across search experiences and avoid code duplication. diff --git a/.changeset/techdocs-jeans-wait.md b/.changeset/techdocs-jeans-wait.md deleted file mode 100644 index b4d9007754..0000000000 --- a/.changeset/techdocs-jeans-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Always update the title and sub-title when the location changes on a `TechDocs` reader page. diff --git a/.changeset/tender-drinks-drive.md b/.changeset/tender-drinks-drive.md new file mode 100644 index 0000000000..0ea69832af --- /dev/null +++ b/.changeset/tender-drinks-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Add functionality to shutdown scaffolder tasks if they are stale diff --git a/.changeset/tender-ladybugs-relax.md b/.changeset/tender-ladybugs-relax.md deleted file mode 100644 index 2aefa09dc0..0000000000 --- a/.changeset/tender-ladybugs-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-stack-overflow': patch ---- - -Support showing HTML entity references from the API response before rendering the question title to the list component. diff --git a/.changeset/tender-pumpkins-burn.md b/.changeset/tender-pumpkins-burn.md deleted file mode 100644 index e431614707..0000000000 --- a/.changeset/tender-pumpkins-burn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Fixed a bug where scrolling for anchors where the id starts with number didn't work for the current page. diff --git a/.changeset/thick-kings-destroy.md b/.changeset/thick-kings-destroy.md new file mode 100644 index 0000000000..77c6be0d3c --- /dev/null +++ b/.changeset/thick-kings-destroy.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Make sure to stitch entities correctly after deletion, to ensure that their relations are updated. diff --git a/.changeset/thin-cows-watch.md b/.changeset/thin-cows-watch.md deleted file mode 100644 index 675a27769b..0000000000 --- a/.changeset/thin-cows-watch.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-permission-backend': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Uptake the `IdentityApi` change to use `getIdentity` instead of `authenticate` for retrieving the logged in users identity. diff --git a/.changeset/tiny-mails-bathe.md b/.changeset/tiny-mails-bathe.md new file mode 100644 index 0000000000..868c2a8d36 --- /dev/null +++ b/.changeset/tiny-mails-bathe.md @@ -0,0 +1,14 @@ +--- +'@backstage/create-app': patch +--- + +Updated the root `test` scripts to use `backstage-cli repo test`. + +To apply this change to an existing app, make the following change to the root `package.json`: + +```diff +- "test": "backstage-cli test", +- "test:all": "lerna run test -- --coverage", ++ "test": "backstage-cli repo test", ++ "test:all": "backstage-cli repo test --coverage", +``` diff --git a/.changeset/tiny-oranges-thank.md b/.changeset/tiny-oranges-thank.md deleted file mode 100644 index 179aeea1c4..0000000000 --- a/.changeset/tiny-oranges-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-insights-backend': patch ---- - -Modify Tech insight initialization to expose FactRetrieverEngine. Enables users to trigger fact retrieval manually or reschedule retrievers on runtime. diff --git a/.changeset/tiny-rocks-flash.md b/.changeset/tiny-rocks-flash.md deleted file mode 100644 index 32b467a6af..0000000000 --- a/.changeset/tiny-rocks-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Improved error messaging when failing to instantiate services. diff --git a/.changeset/tough-dolphins-smile.md b/.changeset/tough-dolphins-smile.md deleted file mode 100644 index bb4f0a39a8..0000000000 --- a/.changeset/tough-dolphins-smile.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -'@backstage/catalog-client': patch -'@backstage/core-plugin-api': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-apollo-explorer': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-badges': patch -'@backstage/plugin-badges-backend': patch -'@backstage/plugin-catalog-react': patch -'@backstage/plugin-circleci': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-scaffolder-backend': patch -'@backstage/plugin-search-backend-module-elasticsearch': patch -'@backstage/plugin-sonarqube-backend': patch -'@backstage/plugin-techdocs': patch ---- - -Minor API signatures cleanup diff --git a/.changeset/twenty-dolls-smoke.md b/.changeset/twenty-dolls-smoke.md deleted file mode 100644 index 7456bd5557..0000000000 --- a/.changeset/twenty-dolls-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Workaround for a rare race condition in tests. diff --git a/.changeset/two-planets-provide.md b/.changeset/two-planets-provide.md deleted file mode 100644 index 920eebc01d..0000000000 --- a/.changeset/two-planets-provide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor ---- - -Implementing review step for the scaffolder under `create/next` diff --git a/.changeset/veka-fingrar-drar.md b/.changeset/veka-fingrar-drar.md deleted file mode 100644 index cff2f45919..0000000000 --- a/.changeset/veka-fingrar-drar.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -If you'd like to send analytics events to multiple implementations, you may now -do so using the `MultipleAnalyticsApi` implementation provided by this package. - -```tsx -import { MultipleAnalyticsApi } from '@backstage/core-app-api'; -import { - analyticsApiRef, - configApiRef, - storageApiRef, - identityApiRef, -} from '@internal/backstage/core-plugin-api'; -import { CustomAnalyticsApi } from '@internal/analytics'; -import { VendorAnalyticsApi } from '@vendor/analytics'; - -createApiFactory({ - api: analyticsApiRef, - deps: { configApi: configApiRef, identityApi: identityApiRef, storageApi: storageApiRef }, - factory: ({ configApi, identityApi, storageApi }) => - MultipleAnalyticsApi.fromApis([ - VendorAnalyticsApi.fromConfig(configApi, { identityApi }), - CustomAnalyticsApi.fromConfig(configApi, { identityApi, storageApi }), - ]), -}), -``` diff --git a/.changeset/weak-camels-roll.md b/.changeset/weak-camels-roll.md deleted file mode 100644 index 90636605ec..0000000000 --- a/.changeset/weak-camels-roll.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Updated the root `package.json` to use the new `backstage-cli repo clean` command. - -To apply this change to an existing project, make the following change to the root `package.json`: - -```diff -- "clean": "backstage-cli clean && lerna run clean", -+ "clean": "backstage-cli repo clean", -``` diff --git a/.changeset/weak-radios-sin.md b/.changeset/weak-radios-sin.md deleted file mode 100644 index e2aac006d4..0000000000 --- a/.changeset/weak-radios-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Updated `versions:bump` command to be compatible with Yarn 3. diff --git a/.changeset/weak-yaks-learn.md b/.changeset/weak-yaks-learn.md deleted file mode 100644 index 7a94e10d68..0000000000 --- a/.changeset/weak-yaks-learn.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -'@backstage/plugin-azure-devops': minor -'@backstage/plugin-azure-devops-common': minor ---- - -Added README card `EntityAzureReadmeCard` for Azure Devops. - -To get the README component working you'll need to do the following two steps: - -1. First we need to add the @backstage/plugin-azure-devops package to your frontend app: - - ```bash - # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-azure-devops - ``` - -2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app: - - ```tsx - // In packages/app/src/components/catalog/EntityPage.tsx - import { - EntityAzureReadmeCard, - isAzureDevOpsAvailable, - } from '@backstage/plugin-azure-devops'; - - // As it is a card, you can customize it the way you prefer - // For example in the Service section - - const overviewContent = ( - - - - - ... - - - - - - - - ); - ``` - -**Notes:** - -- You'll need to add the `EntitySwitch.Case` above from step 2 to all the entity sections you want to see Readme in. For example if you wanted to see Readme when looking at Website entities then you would need to add this to the `websiteEntityPage` section. -- The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation -- The `maxHeight` property on the `EntityAzureReadmeCard` will set the maximum screen size you would like to see, if not set it will default to 100% diff --git a/.changeset/wild-sheep-roll.md b/.changeset/wild-sheep-roll.md deleted file mode 100644 index 1551100b2a..0000000000 --- a/.changeset/wild-sheep-roll.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-api-docs': patch ---- - -Minor cleanup of the public API surface to reduce the number of warnings diff --git a/.changeset/wise-ligers-scream.md b/.changeset/wise-ligers-scream.md new file mode 100644 index 0000000000..d71c69fd42 --- /dev/null +++ b/.changeset/wise-ligers-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +Fixed bug in `CostOverviewBreakdownChart` component where some datasets caused the cost overview breakdown chart to tear. diff --git a/.changeset/witty-cats-wink.md b/.changeset/witty-cats-wink.md deleted file mode 100644 index 82f6d3098f..0000000000 --- a/.changeset/witty-cats-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-msgraph': patch ---- - -Fix typo diff --git a/.changeset/witty-queens-happen.md b/.changeset/witty-queens-happen.md deleted file mode 100644 index 969bd2c79e..0000000000 --- a/.changeset/witty-queens-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-adr': patch ---- - -Fix parsing of ADR location which includes a trailing slash diff --git a/.changeset/yellow-tips-exist.md b/.changeset/yellow-tips-exist.md deleted file mode 100644 index d7b02533e0..0000000000 --- a/.changeset/yellow-tips-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-github': patch ---- - -Remove the duplicated `scheduleFn` initialization in `GitHubEntityProvider`. diff --git a/.changeset/young-falcons-sleep.md b/.changeset/young-falcons-sleep.md deleted file mode 100644 index d7bcef96e6..0000000000 --- a/.changeset/young-falcons-sleep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -The number of task workers used to execute templates now default to 3, rather than 1. diff --git a/.changeset/young-trees-rescue.md b/.changeset/young-trees-rescue.md deleted file mode 100644 index d2ea5b3aeb..0000000000 --- a/.changeset/young-trees-rescue.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Added `EntityLinksCard` to the system `EntityPage`. - -For an existing installation where you want to display the links card for entity pages of kind `system` you should make the following adjustment to `packages/app/src/components/catalog/EntityPage.tsx` - -```diff -const systemPage = ( - ... - - - -+ -+ -+ -- -+ - - - ... -); -``` diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f00771d29d..cb1dd5d322 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,6 +16,8 @@ yarn.lock @backstage/reviewers @backst /docs/plugins/integrating-search-into-plugins.md @backstage/reviewers @backstage/techdocs-core /packages/techdocs-cli @backstage/reviewers @backstage/techdocs-core /packages/techdocs-cli-embedded-app @backstage/reviewers @backstage/techdocs-core +/plugins/adr @backstage/reviewers @kuangp +/plugins/adr-* @backstage/reviewers @kuangp /plugins/allure @backstage/reviewers @deepak-bhardwaj-ps /plugins/apache-airflow @backstage/reviewers @cmpadden /plugins/api-docs @backstage/reviewers @backstage/sda-se-reviewers @@ -47,6 +49,8 @@ yarn.lock @backstage/reviewers @backst /plugins/kubernetes @backstage/reviewers @backstage/warpspeed /plugins/kubernetes-* @backstage/reviewers @backstage/warpspeed /plugins/newrelic-dashboard @backstage/reviewers @mufaddal7 +/plugins/playlist @backstage/reviewers @kuangp +/plugins/playlist-* @backstage/reviewers @kuangp /plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski /plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka /plugins/search @backstage/reviewers @backstage/techdocs-core @@ -56,6 +60,7 @@ yarn.lock @backstage/reviewers @backst /plugins/stack-overflow-backend @backstage/reviewers @backstage/techdocs-core /plugins/techdocs @backstage/reviewers @backstage/techdocs-core /plugins/techdocs-* @backstage/reviewers @backstage/techdocs-core +/plugins/user-settings-backend @backstage/reviewers @backstage/sda-se-reviewers /tech-insights-backend @backstage/reviewers @xantier @iain-b /tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b /tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d63a8b6d77..3c6ac696e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,7 +197,7 @@ jobs: - name: test changed packages if: ${{ steps.yarn-lock.outcome == 'success' }} - run: yarn lerna run test --since origin/master -- --coverage --runInBand + run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --since origin/master env: BACKSTAGE_NEXT_TESTS: 1 BACKSTAGE_TEST_DISABLE_DOCKER: 1 @@ -208,7 +208,7 @@ jobs: - name: test all packages (and upload coverage) if: ${{ steps.yarn-lock.outcome == 'failure' }} run: | - yarn lerna run test -- --coverage --runInBand + yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --coverage bash <(curl -s https://codecov.io/bash) -N $(git rev-parse FETCH_HEAD) env: BACKSTAGE_NEXT_TESTS: 1 diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index e776c47537..a4664288da 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -55,7 +55,9 @@ jobs: # not flagged as the latest release, which means that people will not get this # version of the package unless requested explicitly - name: publish nightly release - run: yarn changeset publish --tag nightly + run: | + yarn config set -H 'npmAuthToken' "${{secrets.NPM_TOKEN}}" + yarn workspaces foreach -v --no-private npm publish --access public --tolerate-republish --tag nightly env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 6fa34e408f..64e5fe813a 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -97,7 +97,7 @@ jobs: - name: test (and upload coverage) run: | - yarn lerna run test -- --coverage --runInBand + yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --coverage bash <(curl -s https://codecov.io/bash) # Upload code coverage for some specific flags. Also see .codecov.yml bash <(curl -s https://codecov.io/bash) -f packages/core-app-api/coverage/* -F core-app-api @@ -162,10 +162,11 @@ jobs: # Publishes current version of packages that are not already present in the registry - name: publish run: | + yarn config set -H 'npmAuthToken' "${{secrets.NPM_TOKEN}}" if [ -f ".changeset/pre.json" ]; then - yarn lerna publish from-package --yes --dist-tag next + yarn workspaces foreach -v --no-private npm publish --access public --tolerate-republish --tag next else - yarn lerna publish from-package --yes + yarn workspaces foreach -v --no-private npm publish --access public --tolerate-republish fi env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 0b12caf2fc..8bbaa645bf 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -46,7 +46,7 @@ jobs: run: yarn lint:type-deps - name: test - run: yarn lerna run test + run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M env: BACKSTAGE_NEXT_TESTS: 1 BACKSTAGE_TEST_DISABLE_DOCKER: 1 diff --git a/app-config.yaml b/app-config.yaml index 7c93d9cbd2..22ca1fcaff 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -444,3 +444,6 @@ apacheAirflow: gocd: baseUrl: https://your.gocd.instance.com + +permission: + enabled: true diff --git a/contrib/docs/tutorials/enable-ssl-self-signed.md b/contrib/docs/tutorials/enable-ssl-self-signed.md new file mode 100644 index 0000000000..80f5d36a98 --- /dev/null +++ b/contrib/docs/tutorials/enable-ssl-self-signed.md @@ -0,0 +1,47 @@ +# Enabling SSL for Local Testing + +If you need to use an `https:` URL for local testing (i.e. if an OAuth provider requires a "secure" callback URL), you can use a self-signed certificate by following these steps. + +## Backend + +1. Generate a self-signed certificate and key for localhost and configure your system to trust it. The application [`mkcert`](https://github.com/FiloSottile/mkcert) is a helpful tool to accomplish this. +1. Update `backend.baseUrl` in app-config.local.yaml to use an `https:` address. +1. Add the certificate and key to `backend.https.certificate.cert` and `backend.https.certificate.cert`, respectively. + ```yaml + backend: + baseUrl: https://localhost:7007 + https: + certificate: + # You may copy the contents of the file... + cert: | + -----BEGIN CERTIFICATE----- + MIIDCTCCAfGgAwIBAgIUZ9VhZckcy690L + ... + -----END CERTIFICATE----- + # ... or use a path + key: + $file: ./certs/localhost-key.pem + ``` +1. Start the backend with `NODE_EXTRA_CA_CERTS=/absolute/path/to/cert.pem yarn start-backend` + +## Frontend + +1. As with the backend instructions above, a trusted certificate and key are needed. +1. Update `app.baseUrl` and `backend.cors.origin` in app-config.local.yaml to use an `https:` address. +1. Add the certificate and key to `app.https.certificate.cert` and `app.https.certificate.cert`, respectively. + ```yaml + app: + baseUrl: https://localhost:3000 + https: + certificate: + cert: + $file: ./certs/localhost.pem + key: + $file: ./certs/localhost-key.pem + backend: + cors: + origin: https://localhost:3000 + ``` +1. and start the app with `yarn start`. + +Depending on what plugins are in use, you may need to override additional URLs to use `https` for those endpoints to work. diff --git a/cypress/yarn.lock b/cypress/yarn.lock index 2dae48bd39..dd9856750c 100644 --- a/cypress/yarn.lock +++ b/cypress/yarn.lock @@ -1,1131 +1,1526 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@cypress/request@^2.88.10": - version "2.88.10" - resolved "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce" - integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - http-signature "~1.3.6" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^8.3.2" - -"@cypress/xvfb@^1.2.4": - version "1.2.4" - resolved "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" - integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== - dependencies: - debug "^3.1.0" - lodash.once "^4.1.1" - -"@types/node@*": - version "18.7.14" - resolved "https://registry.npmjs.org/@types/node/-/node-18.7.14.tgz#0fe081752a3333392d00586d815485a17c2cf3c9" - integrity sha512-6bbDaETVi8oyIARulOE9qF1/Qdi/23z6emrUh0fNJRUmjznqrixD4MpGDdgOFk5Xb0m2H6Xu42JGdvAxaJR/wA== - -"@types/node@^14.14.31": - version "14.18.26" - resolved "https://registry.npmjs.org/@types/node/-/node-14.18.26.tgz#239e19f8b4ea1a9eb710528061c1d733dc561996" - integrity sha512-0b+utRBSYj8L7XAp0d+DX7lI4cSmowNaaTkk6/1SKzbKkG+doLuPusB9EOvzLJ8ahJSk03bTLIL6cWaEd4dBKA== - -"@types/sinonjs__fake-timers@8.1.1": - version "8.1.1" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" - integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== - -"@types/sizzle@^2.3.2": - version "2.3.3" - resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" - integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== - -"@types/yauzl@^2.9.1": - version "2.10.0" - resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" - integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== - dependencies: - "@types/node" "*" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ansi-colors@^4.1.1: - version "4.1.3" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -ansi-escapes@^4.3.0: - version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -arch@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" - integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== - -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async@^3.2.0: - version "3.2.4" - resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -blob-util@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" - integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== - -bluebird@^3.7.2: - version "3.7.2" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -cachedir@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" - integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -check-more-types@^2.24.0: - version "2.24.0" - resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== - -ci-info@^3.2.0: - version "3.3.2" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" - integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-table3@~0.6.1: - version "0.6.2" - resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" - integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== - dependencies: - string-width "^4.2.0" - optionalDependencies: - "@colors/colors" "1.5.0" - -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.16: - version "2.0.19" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - -common-tags@^1.8.0: - version "1.8.2" - resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" - integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cypress@^10.0.0: - version "10.7.0" - resolved "https://registry.npmjs.org/cypress/-/cypress-10.7.0.tgz#2d37f8b9751c6de33ee48639cb7e67a2ce593231" - integrity sha512-gTFvjrUoBnqPPOu9Vl5SBHuFlzx/Wxg/ZXIz2H4lzoOLFelKeF7mbwYUOzgzgF0oieU2WhJAestQdkgwJMMTvQ== - dependencies: - "@cypress/request" "^2.88.10" - "@cypress/xvfb" "^1.2.4" - "@types/node" "^14.14.31" - "@types/sinonjs__fake-timers" "8.1.1" - "@types/sizzle" "^2.3.2" - arch "^2.2.0" - blob-util "^2.0.2" - bluebird "^3.7.2" - buffer "^5.6.0" - cachedir "^2.3.0" - chalk "^4.1.0" - check-more-types "^2.24.0" - cli-cursor "^3.1.0" - cli-table3 "~0.6.1" - commander "^5.1.0" - common-tags "^1.8.0" - dayjs "^1.10.4" - debug "^4.3.2" - enquirer "^2.3.6" - eventemitter2 "^6.4.3" - execa "4.1.0" - executable "^4.1.1" - extract-zip "2.0.1" - figures "^3.2.0" - fs-extra "^9.1.0" - getos "^3.2.1" - is-ci "^3.0.0" - is-installed-globally "~0.4.0" - lazy-ass "^1.6.0" - listr2 "^3.8.3" - lodash "^4.17.21" - log-symbols "^4.0.0" - minimist "^1.2.6" - ospath "^1.2.2" - pretty-bytes "^5.6.0" - proxy-from-env "1.0.0" - request-progress "^3.0.0" - semver "^7.3.2" - supports-color "^8.1.1" - tmp "~0.2.1" - untildify "^4.0.0" - yauzl "^2.10.0" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -dayjs@^1.10.4: - version "1.11.5" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93" - integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA== - -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.1, debug@^4.3.2: - version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enquirer@^2.3.6: - version "2.3.6" - resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== - dependencies: - ansi-colors "^4.1.1" - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -eventemitter2@^6.4.3: - version "6.4.7" - resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" - integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== - -execa@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -executable@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -figures@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -getos@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" - integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== - dependencies: - async "^3.2.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" - integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== - dependencies: - ini "2.0.0" - -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -http-signature@~1.3.6: - version "1.3.6" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" - integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== - dependencies: - assert-plus "^1.0.0" - jsprim "^2.0.2" - sshpk "^1.14.1" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - -is-ci@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" - integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== - dependencies: - ci-info "^3.2.0" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-installed-globally@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" - integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== - dependencies: - global-dirs "^3.0.0" - is-path-inside "^3.0.2" - -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" - integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -lazy-ass@^1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== - -listr2@^3.8.3: - version "3.14.0" - resolved "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" - integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.16" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.5.1" - through "^2.3.8" - wrap-ansi "^7.0.0" - -lodash.once@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -npm-run-path@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -ospath@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" - integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -pify@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pretty-bytes@^5.6.0: - version "5.6.0" - resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== - -proxy-from-env@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" - integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== - -psl@^1.1.28: - version "1.9.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -request-progress@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" - integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== - dependencies: - throttleit "^1.0.0" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -rfdc@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - -rimraf@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rxjs@^7.5.1: - version "7.5.6" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" - integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== - dependencies: - tslib "^2.1.0" - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -semver@^7.3.2: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -signal-exit@^3.0.2: - version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -sshpk@^1.14.1: - version "1.17.0" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -supports-color@^7.1.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.1: - version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -throttleit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" - integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g== - -through@^2.3.8: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tmp@~0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -typescript@^4.1.3: - version "4.8.2" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790" - integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw== - -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"@backstage/cypress-tests@workspace:.": + version: 0.0.0-use.local + resolution: "@backstage/cypress-tests@workspace:." + dependencies: + cypress: ^10.0.0 + typescript: ^4.1.3 + languageName: unknown + linkType: soft + +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 + languageName: node + linkType: hard + +"@cypress/request@npm:^2.88.10": + version: 2.88.10 + resolution: "@cypress/request@npm:2.88.10" + dependencies: + aws-sign2: ~0.7.0 + aws4: ^1.8.0 + caseless: ~0.12.0 + combined-stream: ~1.0.6 + extend: ~3.0.2 + forever-agent: ~0.6.1 + form-data: ~2.3.2 + http-signature: ~1.3.6 + is-typedarray: ~1.0.0 + isstream: ~0.1.2 + json-stringify-safe: ~5.0.1 + mime-types: ~2.1.19 + performance-now: ^2.1.0 + qs: ~6.5.2 + safe-buffer: ^5.1.2 + tough-cookie: ~2.5.0 + tunnel-agent: ^0.6.0 + uuid: ^8.3.2 + checksum: 69c3e3b332e9be4866a900f6bcca5d274d8cea6c99707fbcce061de8dbab11c9b1e39f4c017f6e83e6e682717781d4f6106fd6b7cf9546580fcfac353b6676cf + languageName: node + linkType: hard + +"@cypress/xvfb@npm:^1.2.4": + version: 1.2.4 + resolution: "@cypress/xvfb@npm:1.2.4" + dependencies: + debug: ^3.1.0 + lodash.once: ^4.1.1 + checksum: 7bdcdaeb1bb692ec9d9bf8ec52538aa0bead6764753f4a067a171a511807a43fab016f7285a56bef6a606c2467ff3f1365e1ad2d2d583b81beed849ee1573fd1 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 18.7.14 + resolution: "@types/node@npm:18.7.14" + checksum: 99cf28ff854100158de875cca23c7acc3cc01dfee526a52b90b7f36767c821bcbaf2be0a98a70f06f3b78f3c60639168ff949d725b61e2e124f9f71f1fb8043d + languageName: node + linkType: hard + +"@types/node@npm:^14.14.31": + version: 14.18.26 + resolution: "@types/node@npm:14.18.26" + checksum: c6ac3f9d4f6f77c0fa5ac17757779ad4d9835abfe3af5708b7552597cb5c7c1103e83499ccaf767a1cfa70040990bcf3f58761c547051dc8d5077f3c419091b1 + languageName: node + linkType: hard + +"@types/sinonjs__fake-timers@npm:8.1.1": + version: 8.1.1 + resolution: "@types/sinonjs__fake-timers@npm:8.1.1" + checksum: ca09d54d47091d87020824a73f026300fa06b17cd9f2f9b9387f28b549364b141ef194ee28db762f6588de71d8febcd17f753163cb7ea116b8387c18e80ebd5c + languageName: node + linkType: hard + +"@types/sizzle@npm:^2.3.2": + version: 2.3.3 + resolution: "@types/sizzle@npm:2.3.3" + checksum: 586a9fb1f6ff3e325e0f2cc1596a460615f0bc8a28f6e276ac9b509401039dd242fa8b34496d3a30c52f5b495873922d09a9e76c50c2ab2bcc70ba3fb9c4e160 + languageName: node + linkType: hard + +"@types/yauzl@npm:^2.9.1": + version: 2.10.0 + resolution: "@types/yauzl@npm:2.10.0" + dependencies: + "@types/node": "*" + checksum: 55d27ae5d346ea260e40121675c24e112ef0247649073848e5d4e03182713ae4ec8142b98f61a1c6cbe7d3b72fa99bbadb65d8b01873e5e605cdc30f1ff70ef2 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: ^2.0.0 + indent-string: ^4.0.0 + checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 + languageName: node + linkType: hard + +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e + languageName: node + linkType: hard + +"ansi-escapes@npm:^4.3.0": + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" + dependencies: + type-fest: ^0.21.3 + checksum: 93111c42189c0a6bed9cdb4d7f2829548e943827ee8479c74d6e0b22ee127b2a21d3f8b5ca57723b8ef78ce011fbfc2784350eb2bde3ccfccf2f575fa8489815 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard + +"arch@npm:^2.2.0": + version: 2.2.0 + resolution: "arch@npm:2.2.0" + checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f + languageName: node + linkType: hard + +"asn1@npm:~0.2.3": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: ~2.1.0 + checksum: 39f2ae343b03c15ad4f238ba561e626602a3de8d94ae536c46a4a93e69578826305366dc09fbb9b56aec39b4982a463682f259c38e59f6fa380cd72cd61e493d + languageName: node + linkType: hard + +"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": + version: 1.0.0 + resolution: "assert-plus@npm:1.0.0" + checksum: 19b4340cb8f0e6a981c07225eacac0e9d52c2644c080198765d63398f0075f83bbc0c8e95474d54224e297555ad0d631c1dcd058adb1ddc2437b41a6b424ac64 + languageName: node + linkType: hard + +"astral-regex@npm:^2.0.0": + version: 2.0.0 + resolution: "astral-regex@npm:2.0.0" + checksum: 876231688c66400473ba505731df37ea436e574dd524520294cc3bbc54ea40334865e01fa0d074d74d036ee874ee7e62f486ea38bc421ee8e6a871c06f011766 + languageName: node + linkType: hard + +"async@npm:^3.2.0": + version: 3.2.4 + resolution: "async@npm:3.2.4" + checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 7b78c451df768adba04e2d02e63e2d0bf3b07adcd6e42b4cf665cb7ce899bedd344c69a1dcbce355b5f972d597b25aaa1c1742b52cffd9caccb22f348114f6be + languageName: node + linkType: hard + +"at-least-node@npm:^1.0.0": + version: 1.0.0 + resolution: "at-least-node@npm:1.0.0" + checksum: 463e2f8e43384f1afb54bc68485c436d7622acec08b6fad269b421cb1d29cebb5af751426793d0961ed243146fe4dc983402f6d5a51b720b277818dbf6f2e49e + languageName: node + linkType: hard + +"aws-sign2@npm:~0.7.0": + version: 0.7.0 + resolution: "aws-sign2@npm:0.7.0" + checksum: b148b0bb0778098ad8cf7e5fc619768bcb51236707ca1d3e5b49e41b171166d8be9fdc2ea2ae43d7decf02989d0aaa3a9c4caa6f320af95d684de9b548a71525 + languageName: node + linkType: hard + +"aws4@npm:^1.8.0": + version: 1.11.0 + resolution: "aws4@npm:1.11.0" + checksum: 5a00d045fd0385926d20ebebcfba5ec79d4482fe706f63c27b324d489a04c68edb0db99ed991e19eda09cb8c97dc2452059a34d97545cebf591d7a2b5a10999f + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 + languageName: node + linkType: hard + +"bcrypt-pbkdf@npm:^1.0.0": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: ^0.14.3 + checksum: 4edfc9fe7d07019609ccf797a2af28351736e9d012c8402a07120c4453a3b789a15f2ee1530dc49eee8f7eb9379331a8dd4b3766042b9e502f74a68e7f662291 + languageName: node + linkType: hard + +"blob-util@npm:^2.0.2": + version: 2.0.2 + resolution: "blob-util@npm:2.0.2" + checksum: d543e6b92e4ca715ca33c78e89a07a2290d43e5b2bc897d7ec588c5c7bbf59df93e45225ac0c9258aa6ce4320358990f99c9288f1c48280f8ec5d7a2e088d19b + languageName: node + linkType: hard + +"bluebird@npm:^3.7.2": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + languageName: node + linkType: hard + +"buffer-crc32@npm:~0.2.3": + version: 0.2.13 + resolution: "buffer-crc32@npm:0.2.13" + checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c + languageName: node + linkType: hard + +"buffer@npm:^5.6.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.1.13 + checksum: e2cf8429e1c4c7b8cbd30834ac09bd61da46ce35f5c22a78e6c2f04497d6d25541b16881e30a019c6fd3154150650ccee27a308eff3e26229d788bbdeb08ab84 + languageName: node + linkType: hard + +"cachedir@npm:^2.3.0": + version: 2.3.0 + resolution: "cachedir@npm:2.3.0" + checksum: ec90cb0f2e6336e266aa748dbadf3da9e0b20e843e43f1591acab7a3f1451337dc2f26cb9dd833ae8cfefeffeeb43ef5b5ff62782a685f4e3c2305dd98482fcb + languageName: node + linkType: hard + +"caseless@npm:~0.12.0": + version: 0.12.0 + resolution: "caseless@npm:0.12.0" + checksum: b43bd4c440aa1e8ee6baefee8063b4850fd0d7b378f6aabc796c9ec8cb26d27fb30b46885350777d9bd079c5256c0e1329ad0dc7c2817e0bb466810ebb353751 + languageName: node + linkType: hard + +"chalk@npm:^4.1.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard + +"check-more-types@npm:^2.24.0": + version: 2.24.0 + resolution: "check-more-types@npm:2.24.0" + checksum: b09080ec3404d20a4b0ead828994b2e5913236ef44ed3033a27062af0004cf7d2091fbde4b396bf13b7ce02fb018bc9960b48305e6ab2304cd82d73ed7a51ef4 + languageName: node + linkType: hard + +"ci-info@npm:^3.2.0": + version: 3.3.2 + resolution: "ci-info@npm:3.3.2" + checksum: fd81f1edd2d3b0f6cb077b2e84365136d87b9db8c055928c1ad69da8a76c2c2f19cba8ea51b90238302157ca927f91f92b653e933f2398dde4867500f08d6e62 + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 + languageName: node + linkType: hard + +"cli-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "cli-cursor@npm:3.1.0" + dependencies: + restore-cursor: ^3.1.0 + checksum: 2692784c6cd2fd85cfdbd11f53aea73a463a6d64a77c3e098b2b4697a20443f430c220629e1ca3b195ea5ac4a97a74c2ee411f3807abf6df2b66211fec0c0a29 + languageName: node + linkType: hard + +"cli-table3@npm:~0.6.1": + version: 0.6.2 + resolution: "cli-table3@npm:0.6.2" + dependencies: + "@colors/colors": 1.5.0 + string-width: ^4.2.0 + dependenciesMeta: + "@colors/colors": + optional: true + checksum: 2f82391698b8a2a2a5e45d2adcfea5d93e557207f90455a8d4c1aac688e9b18a204d9eb4ba1d322fa123b17d64ea3dc5e11de8b005529f3c3e7dbeb27cb4d9be + languageName: node + linkType: hard + +"cli-truncate@npm:^2.1.0": + version: 2.1.0 + resolution: "cli-truncate@npm:2.1.0" + dependencies: + slice-ansi: ^3.0.0 + string-width: ^4.2.0 + checksum: bf1e4e6195392dc718bf9cd71f317b6300dc4a9191d052f31046b8773230ece4fa09458813bf0e3455a5e68c0690d2ea2c197d14a8b85a7b5e01c97f4b5feb5d + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"colorette@npm:^2.0.16": + version: 2.0.19 + resolution: "colorette@npm:2.0.19" + checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: ~1.0.0 + checksum: 49fa4aeb4916567e33ea81d088f6584749fc90c7abec76fd516bf1c5aa5c79f3584b5ba3de6b86d26ddd64bae5329c4c7479343250cfe71c75bb366eae53bb7c + languageName: node + linkType: hard + +"commander@npm:^5.1.0": + version: 5.1.0 + resolution: "commander@npm:5.1.0" + checksum: 0b7fec1712fbcc6230fcb161d8d73b4730fa91a21dc089515489402ad78810547683f058e2a9835929c212fead1d6a6ade70db28bbb03edbc2829a9ab7d69447 + languageName: node + linkType: hard + +"common-tags@npm:^1.8.0": + version: 1.8.2 + resolution: "common-tags@npm:1.8.2" + checksum: 767a6255a84bbc47df49a60ab583053bb29a7d9687066a18500a516188a062c4e4cd52de341f22de0b07062e699b1b8fe3cfa1cb55b241cb9301aeb4f45b4dff + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"core-util-is@npm:1.0.2": + version: 1.0.2 + resolution: "core-util-is@npm:1.0.2" + checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0": + version: 7.0.3 + resolution: "cross-spawn@npm:7.0.3" + dependencies: + path-key: ^3.1.0 + shebang-command: ^2.0.0 + which: ^2.0.1 + checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52 + languageName: node + linkType: hard + +"cypress@npm:^10.0.0": + version: 10.8.0 + resolution: "cypress@npm:10.8.0" + dependencies: + "@cypress/request": ^2.88.10 + "@cypress/xvfb": ^1.2.4 + "@types/node": ^14.14.31 + "@types/sinonjs__fake-timers": 8.1.1 + "@types/sizzle": ^2.3.2 + arch: ^2.2.0 + blob-util: ^2.0.2 + bluebird: ^3.7.2 + buffer: ^5.6.0 + cachedir: ^2.3.0 + chalk: ^4.1.0 + check-more-types: ^2.24.0 + cli-cursor: ^3.1.0 + cli-table3: ~0.6.1 + commander: ^5.1.0 + common-tags: ^1.8.0 + dayjs: ^1.10.4 + debug: ^4.3.2 + enquirer: ^2.3.6 + eventemitter2: 6.4.7 + execa: 4.1.0 + executable: ^4.1.1 + extract-zip: 2.0.1 + figures: ^3.2.0 + fs-extra: ^9.1.0 + getos: ^3.2.1 + is-ci: ^3.0.0 + is-installed-globally: ~0.4.0 + lazy-ass: ^1.6.0 + listr2: ^3.8.3 + lodash: ^4.17.21 + log-symbols: ^4.0.0 + minimist: ^1.2.6 + ospath: ^1.2.2 + pretty-bytes: ^5.6.0 + proxy-from-env: 1.0.0 + request-progress: ^3.0.0 + semver: ^7.3.2 + supports-color: ^8.1.1 + tmp: ~0.2.1 + untildify: ^4.0.0 + yauzl: ^2.10.0 + bin: + cypress: bin/cypress + checksum: c052690049980e7721e6fca563b724fde839d87d83c1478dfe26ce7d230992717c2c4028e7157bfb39ec274473e51929f49e5aab6a23c2b25cde2a439b1c3cf9 + languageName: node + linkType: hard + +"dashdash@npm:^1.12.0": + version: 1.14.1 + resolution: "dashdash@npm:1.14.1" + dependencies: + assert-plus: ^1.0.0 + checksum: 3634c249570f7f34e3d34f866c93f866c5b417f0dd616275decae08147dcdf8fccfaa5947380ccfb0473998ea3a8057c0b4cd90c875740ee685d0624b2983598 + languageName: node + linkType: hard + +"dayjs@npm:^1.10.4": + version: 1.11.5 + resolution: "dayjs@npm:1.11.5" + checksum: e3bbaa7b4883b31be4bf75a181f1447fbb19800c29b332852125aab96baeff3ac232dcba8b88c4ea17d3b636c99dac5fb9d1af4bb6ae26615698bbc4a852dffb + languageName: node + linkType: hard + +"debug@npm:^3.1.0": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: ^2.1.1 + checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c + languageName: node + linkType: hard + +"debug@npm:^4.1.1, debug@npm:^4.3.2": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 + languageName: node + linkType: hard + +"ecc-jsbn@npm:~0.1.1": + version: 0.1.2 + resolution: "ecc-jsbn@npm:0.1.2" + dependencies: + jsbn: ~0.1.0 + safer-buffer: ^2.1.0 + checksum: 22fef4b6203e5f31d425f5b711eb389e4c6c2723402e389af394f8411b76a488fa414d309d866e2b577ce3e8462d344205545c88a8143cc21752a5172818888a + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 + languageName: node + linkType: hard + +"end-of-stream@npm:^1.1.0": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: ^1.4.0 + checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b + languageName: node + linkType: hard + +"enquirer@npm:^2.3.6": + version: 2.3.6 + resolution: "enquirer@npm:2.3.6" + dependencies: + ansi-colors: ^4.1.1 + checksum: 1c0911e14a6f8d26721c91e01db06092a5f7675159f0261d69c403396a385afd13dd76825e7678f66daffa930cfaa8d45f506fb35f818a2788463d022af1b884 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 + languageName: node + linkType: hard + +"eventemitter2@npm:6.4.7": + version: 6.4.7 + resolution: "eventemitter2@npm:6.4.7" + checksum: 1b36a77e139d6965ebf3a36c01fa00c089ae6b80faa1911e52888f40b3a7057b36a2cc45dcd1ad87cda3798fe7b97a0aabcbb8175a8b96092a23bb7d0f039e66 + languageName: node + linkType: hard + +"execa@npm:4.1.0": + version: 4.1.0 + resolution: "execa@npm:4.1.0" + dependencies: + cross-spawn: ^7.0.0 + get-stream: ^5.0.0 + human-signals: ^1.1.1 + is-stream: ^2.0.0 + merge-stream: ^2.0.0 + npm-run-path: ^4.0.0 + onetime: ^5.1.0 + signal-exit: ^3.0.2 + strip-final-newline: ^2.0.0 + checksum: e30d298934d9c52f90f3847704fd8224e849a081ab2b517bbc02f5f7732c24e56a21f14cb96a08256deffeb2d12b2b7cb7e2b014a12fb36f8d3357e06417ed55 + languageName: node + linkType: hard + +"executable@npm:^4.1.1": + version: 4.1.1 + resolution: "executable@npm:4.1.1" + dependencies: + pify: ^2.2.0 + checksum: f01927ce59bccec804e171bf859a26e362c1f50aa9ebc69f7cafdcce3859d29d4b6267fd47237c18b0a1830614bd3f0ee14b7380d9bad18a4e7af9b5f0b6984f + languageName: node + linkType: hard + +"extend@npm:~3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 + languageName: node + linkType: hard + +"extract-zip@npm:2.0.1": + version: 2.0.1 + resolution: "extract-zip@npm:2.0.1" + dependencies: + "@types/yauzl": ^2.9.1 + debug: ^4.1.1 + get-stream: ^5.1.0 + yauzl: ^2.10.0 + dependenciesMeta: + "@types/yauzl": + optional: true + bin: + extract-zip: cli.js + checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 + languageName: node + linkType: hard + +"extsprintf@npm:1.3.0": + version: 1.3.0 + resolution: "extsprintf@npm:1.3.0" + checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 + languageName: node + linkType: hard + +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 + languageName: node + linkType: hard + +"fd-slicer@npm:~1.1.0": + version: 1.1.0 + resolution: "fd-slicer@npm:1.1.0" + dependencies: + pend: ~1.2.0 + checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 + languageName: node + linkType: hard + +"figures@npm:^3.2.0": + version: 3.2.0 + resolution: "figures@npm:3.2.0" + dependencies: + escape-string-regexp: ^1.0.5 + checksum: 85a6ad29e9aca80b49b817e7c89ecc4716ff14e3779d9835af554db91bac41c0f289c418923519392a1e582b4d10482ad282021330cd045bb7b80c84152f2a2b + languageName: node + linkType: hard + +"forever-agent@npm:~0.6.1": + version: 0.6.1 + resolution: "forever-agent@npm:0.6.1" + checksum: 766ae6e220f5fe23676bb4c6a99387cec5b7b62ceb99e10923376e27bfea72f3c3aeec2ba5f45f3f7ba65d6616965aa7c20b15002b6860833bb6e394dea546a8 + languageName: node + linkType: hard + +"form-data@npm:~2.3.2": + version: 2.3.3 + resolution: "form-data@npm:2.3.3" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.6 + mime-types: ^2.1.12 + checksum: 10c1780fa13dbe1ff3100114c2ce1f9307f8be10b14bf16e103815356ff567b6be39d70fc4a40f8990b9660012dc24b0f5e1dde1b6426166eb23a445ba068ca3 + languageName: node + linkType: hard + +"fs-extra@npm:^9.1.0": + version: 9.1.0 + resolution: "fs-extra@npm:9.1.0" + dependencies: + at-least-node: ^1.0.0 + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: ba71ba32e0faa74ab931b7a0031d1523c66a73e225de7426e275e238e312d07313d2da2d33e34a52aa406c8763ade5712eb3ec9ba4d9edce652bcacdc29e6b20 + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + +"get-stream@npm:^5.0.0, get-stream@npm:^5.1.0": + version: 5.2.0 + resolution: "get-stream@npm:5.2.0" + dependencies: + pump: ^3.0.0 + checksum: 8bc1a23174a06b2b4ce600df38d6c98d2ef6d84e020c1ddad632ad75bac4e092eeb40e4c09e0761c35fc2dbc5e7fff5dab5e763a383582c4a167dd69a905bd12 + languageName: node + linkType: hard + +"getos@npm:^3.2.1": + version: 3.2.1 + resolution: "getos@npm:3.2.1" + dependencies: + async: ^3.2.0 + checksum: 42fd78a66d47cebd3e09de5566cc0044e034b08f4a000a310dbd89a77b02c65d8f4002554bfa495ea5bdc4fa9d515f5ac785a7cc474ba45383cc697f865eeaf1 + languageName: node + linkType: hard + +"getpass@npm:^0.1.1": + version: 0.1.7 + resolution: "getpass@npm:0.1.7" + dependencies: + assert-plus: ^1.0.0 + checksum: ab18d55661db264e3eac6012c2d3daeafaab7a501c035ae0ccb193c3c23e9849c6e29b6ac762b9c2adae460266f925d55a3a2a3a3c8b94be2f222df94d70c046 + languageName: node + linkType: hard + +"glob@npm:^7.1.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + +"global-dirs@npm:^3.0.0": + version: 3.0.0 + resolution: "global-dirs@npm:3.0.0" + dependencies: + ini: 2.0.0 + checksum: 953c17cf14bf6ee0e2100ae82a0d779934eed8a3ec5c94a7a4f37c5b3b592c31ea015fb9a15cf32484de13c79f4a814f3015152f3e1d65976cfbe47c1bfe4a88 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0": + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad + languageName: node + linkType: hard + +"http-signature@npm:~1.3.6": + version: 1.3.6 + resolution: "http-signature@npm:1.3.6" + dependencies: + assert-plus: ^1.0.0 + jsprim: ^2.0.2 + sshpk: ^1.14.1 + checksum: 10be2af4764e71fee0281392937050201ee576ac755c543f570d6d87134ce5e858663fe999a7adb3e4e368e1e356d0d7fec6b9542295b875726ff615188e7a0c + languageName: node + linkType: hard + +"human-signals@npm:^1.1.1": + version: 1.1.1 + resolution: "human-signals@npm:1.1.1" + checksum: d587647c9e8ec24e02821b6be7de5a0fc37f591f6c4e319b3054b43fd4c35a70a94c46fc74d8c1a43c47fde157d23acd7421f375e1c1365b09a16835b8300205 + languageName: node + linkType: hard + +"ieee754@npm:^1.1.13": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: ^1.3.0 + wrappy: 1 + checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd + languageName: node + linkType: hard + +"inherits@npm:2": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"ini@npm:2.0.0": + version: 2.0.0 + resolution: "ini@npm:2.0.0" + checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e + languageName: node + linkType: hard + +"is-ci@npm:^3.0.0": + version: 3.0.1 + resolution: "is-ci@npm:3.0.1" + dependencies: + ci-info: ^3.2.0 + bin: + is-ci: bin.js + checksum: 192c66dc7826d58f803ecae624860dccf1899fc1f3ac5505284c0a5cf5f889046ffeb958fa651e5725d5705c5bcb14f055b79150ea5fcad7456a9569de60260e + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-installed-globally@npm:~0.4.0": + version: 0.4.0 + resolution: "is-installed-globally@npm:0.4.0" + dependencies: + global-dirs: ^3.0.0 + is-path-inside: ^3.0.2 + checksum: 3359840d5982d22e9b350034237b2cda2a12bac1b48a721912e1ab8e0631dd07d45a2797a120b7b87552759a65ba03e819f1bd63f2d7ab8657ec0b44ee0bf399 + languageName: node + linkType: hard + +"is-path-inside@npm:^3.0.2": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 + languageName: node + linkType: hard + +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66 + languageName: node + linkType: hard + +"is-typedarray@npm:~1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 + languageName: node + linkType: hard + +"is-unicode-supported@npm:^0.1.0": + version: 0.1.0 + resolution: "is-unicode-supported@npm:0.1.0" + checksum: a2aab86ee7712f5c2f999180daaba5f361bdad1efadc9610ff5b8ab5495b86e4f627839d085c6530363c6d6d4ecbde340fb8e54bdb83da4ba8e0865ed5513c52 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 + languageName: node + linkType: hard + +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 1eb2fe63a729f7bdd8a559ab552c69055f4f48eb5c2f03724430587c6f450783c8f1cd936c1c952d0a927925180fcc892ebd5b174236cf1065d4bd5bdb37e963 + languageName: node + linkType: hard + +"jsbn@npm:~0.1.0": + version: 0.1.1 + resolution: "jsbn@npm:0.1.1" + checksum: e5ff29c1b8d965017ef3f9c219dacd6e40ad355c664e277d31246c90545a02e6047018c16c60a00f36d561b3647215c41894f5d869ada6908a2e0ce4200c88f2 + languageName: node + linkType: hard + +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 + languageName: node + linkType: hard + +"json-stringify-safe@npm:~5.0.1": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 48ec0adad5280b8a96bb93f4563aa1667fd7a36334f79149abd42446d0989f2ddc58274b479f4819f1f00617957e6344c886c55d05a4e15ebb4ab931e4a6a8ee + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: ^4.1.6 + universalify: ^2.0.0 + dependenciesMeta: + graceful-fs: + optional: true + checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 + languageName: node + linkType: hard + +"jsprim@npm:^2.0.2": + version: 2.0.2 + resolution: "jsprim@npm:2.0.2" + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + checksum: d175f6b1991e160cb0aa39bc857da780e035611986b5492f32395411879fdaf4e513d98677f08f7352dac93a16b66b8361c674b86a3fa406e2e7af6b26321838 + languageName: node + linkType: hard + +"lazy-ass@npm:^1.6.0": + version: 1.6.0 + resolution: "lazy-ass@npm:1.6.0" + checksum: 5a3ebb17915b03452320804466345382a6c25ac782ec4874fecdb2385793896cd459be2f187dc7def8899180c32ee0ab9a1aa7fe52193ac3ff3fe29bb0591729 + languageName: node + linkType: hard + +"listr2@npm:^3.8.3": + version: 3.14.0 + resolution: "listr2@npm:3.14.0" + dependencies: + cli-truncate: ^2.1.0 + colorette: ^2.0.16 + log-update: ^4.0.0 + p-map: ^4.0.0 + rfdc: ^1.3.0 + rxjs: ^7.5.1 + through: ^2.3.8 + wrap-ansi: ^7.0.0 + peerDependencies: + enquirer: ">= 2.3.0 < 3" + peerDependenciesMeta: + enquirer: + optional: true + checksum: fdb8b2d6bdf5df9371ebd5082bee46c6d0ca3d1e5f2b11fbb5a127839855d5f3da9d4968fce94f0a5ec67cac2459766abbb1faeef621065ebb1829b11ef9476d + languageName: node + linkType: hard + +"lodash.once@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: d768fa9f9b4e1dc6453be99b753906f58990e0c45e7b2ca5a3b40a33111e5d17f6edf2f768786e2716af90a8e78f8f91431ab8435f761fef00f9b0c256f6d245 + languageName: node + linkType: hard + +"lodash@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 + languageName: node + linkType: hard + +"log-symbols@npm:^4.0.0": + version: 4.1.0 + resolution: "log-symbols@npm:4.1.0" + dependencies: + chalk: ^4.1.0 + is-unicode-supported: ^0.1.0 + checksum: fce1497b3135a0198803f9f07464165e9eb83ed02ceb2273930a6f8a508951178d8cf4f0378e9d28300a2ed2bc49050995d2bd5f53ab716bb15ac84d58c6ef74 + languageName: node + linkType: hard + +"log-update@npm:^4.0.0": + version: 4.0.0 + resolution: "log-update@npm:4.0.0" + dependencies: + ansi-escapes: ^4.3.0 + cli-cursor: ^3.1.0 + slice-ansi: ^4.0.0 + wrap-ansi: ^6.2.0 + checksum: ae2f85bbabc1906034154fb7d4c4477c79b3e703d22d78adee8b3862fa913942772e7fa11713e3d96fb46de4e3cabefbf5d0a544344f03b58d3c4bff52aa9eb2 + languageName: node + linkType: hard + +"lru-cache@npm:^6.0.0": + version: 6.0.0 + resolution: "lru-cache@npm:6.0.0" + dependencies: + yallist: ^4.0.0 + checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297 + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 6fa4dcc8d86629705cea944a4b88ef4cb0e07656ebf223fa287443256414283dd25d91c1cd84c77987f2aec5927af1a9db6085757cb43d90eb170ebf4b47f4f4 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12, mime-types@npm:~2.1.19": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 + languageName: node + linkType: hard + +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: d2421a3444848ce7f84bd49115ddacff29c15745db73f54041edc906c14b131a38d05298dae3081667627a59b2eb1ca4b436ff2e1b80f69679522410418b478a + languageName: node + linkType: hard + +"minimatch@npm:^3.1.1": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: ^1.1.7 + checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a + languageName: node + linkType: hard + +"minimist@npm:^1.2.6": + version: 1.2.6 + resolution: "minimist@npm:1.2.6" + checksum: d15428cd1e11eb14e1233bcfb88ae07ed7a147de251441d61158619dfb32c4d7e9061d09cab4825fdee18ecd6fce323228c8c47b5ba7cd20af378ca4048fb3fb + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f + languageName: node + linkType: hard + +"ms@npm:^2.1.1": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d + languageName: node + linkType: hard + +"npm-run-path@npm:^4.0.0": + version: 4.0.1 + resolution: "npm-run-path@npm:4.0.1" + dependencies: + path-key: ^3.0.0 + checksum: 5374c0cea4b0bbfdfae62da7bbdf1e1558d338335f4cacf2515c282ff358ff27b2ecb91ffa5330a8b14390ac66a1e146e10700440c1ab868208430f56b5f4d23 + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"onetime@npm:^5.1.0": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: ^2.1.0 + checksum: 2478859ef817fc5d4e9c2f9e5728512ddd1dbc9fb7829ad263765bb6d3b91ce699d6e2332eef6b7dff183c2f490bd3349f1666427eaba4469fba0ac38dfd0d34 + languageName: node + linkType: hard + +"ospath@npm:^1.2.2": + version: 1.2.2 + resolution: "ospath@npm:1.2.2" + checksum: 505f48a4f4f1c557d6c656ec985707726e3714721680139be037613e903aa8c8fa4ddd8d1342006f9b2dc0065e6e20f8b7bea2ee05354f31257044790367b347 + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: ^3.0.0 + checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-key@npm:^3.0.0, path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 + languageName: node + linkType: hard + +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d + languageName: node + linkType: hard + +"performance-now@npm:^2.1.0": + version: 2.1.0 + resolution: "performance-now@npm:2.1.0" + checksum: 534e641aa8f7cba160f0afec0599b6cecefbb516a2e837b512be0adbe6c1da5550e89c78059c7fabc5c9ffdf6627edabe23eb7c518c4500067a898fa65c2b550 + languageName: node + linkType: hard + +"pify@npm:^2.2.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba + languageName: node + linkType: hard + +"pretty-bytes@npm:^5.6.0": + version: 5.6.0 + resolution: "pretty-bytes@npm:5.6.0" + checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd + languageName: node + linkType: hard + +"proxy-from-env@npm:1.0.0": + version: 1.0.0 + resolution: "proxy-from-env@npm:1.0.0" + checksum: 292e28d1de0c315958d71d8315eb546dd3cd8c8cbc2dab7c54eeb9f5c17f421771964ad0b5e1f77011bab2305bdae42e1757ce33bdb1ccc3e87732322a8efcf1 + languageName: node + linkType: hard + +"psl@npm:^1.1.28": + version: 1.9.0 + resolution: "psl@npm:1.9.0" + checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: ^1.1.0 + once: ^1.3.1 + checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 + languageName: node + linkType: hard + +"punycode@npm:^2.1.1": + version: 2.1.1 + resolution: "punycode@npm:2.1.1" + checksum: 823bf443c6dd14f669984dea25757b37993f67e8d94698996064035edd43bed8a5a17a9f12e439c2b35df1078c6bec05a6c86e336209eb1061e8025c481168e8 + languageName: node + linkType: hard + +"qs@npm:~6.5.2": + version: 6.5.3 + resolution: "qs@npm:6.5.3" + checksum: 6f20bf08cabd90c458e50855559539a28d00b2f2e7dddcb66082b16a43188418cb3cb77cbd09268bcef6022935650f0534357b8af9eeb29bf0f27ccb17655692 + languageName: node + linkType: hard + +"request-progress@npm:^3.0.0": + version: 3.0.0 + resolution: "request-progress@npm:3.0.0" + dependencies: + throttleit: ^1.0.0 + checksum: 6ea1761dcc8a8b7b5894afd478c0286aa31bd69438d7050294bd4fd0d0b3e09b5cde417d38deef9c49809039c337d8744e4bb49d8632b0c3e4ffa5e8a687e0fd + languageName: node + linkType: hard + +"restore-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "restore-cursor@npm:3.1.0" + dependencies: + onetime: ^5.1.0 + signal-exit: ^3.0.2 + checksum: f877dd8741796b909f2a82454ec111afb84eb45890eb49ac947d87991379406b3b83ff9673a46012fca0d7844bb989f45cc5b788254cf1a39b6b5a9659de0630 + languageName: node + linkType: hard + +"rfdc@npm:^1.3.0": + version: 1.3.0 + resolution: "rfdc@npm:1.3.0" + checksum: fb2ba8512e43519983b4c61bd3fa77c0f410eff6bae68b08614437bc3f35f91362215f7b4a73cbda6f67330b5746ce07db5dd9850ad3edc91271ad6deea0df32 + languageName: node + linkType: hard + +"rimraf@npm:^3.0.0": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" + dependencies: + glob: ^7.1.3 + bin: + rimraf: bin.js + checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0 + languageName: node + linkType: hard + +"rxjs@npm:^7.5.1": + version: 7.5.6 + resolution: "rxjs@npm:7.5.6" + dependencies: + tslib: ^2.1.0 + checksum: fc05f01364a74dac57490fb3e07ea63b422af04017fae1db641a009073f902ef69f285c5daac31359620dc8d9aee7d81e42b370ca2a8573d1feae0b04329383b + languageName: node + linkType: hard + +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.2": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 + languageName: node + linkType: hard + +"safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 + languageName: node + linkType: hard + +"semver@npm:^7.3.2": + version: 7.3.7 + resolution: "semver@npm:7.3.7" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: ^3.0.0 + checksum: 6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.2": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 + languageName: node + linkType: hard + +"slice-ansi@npm:^3.0.0": + version: 3.0.0 + resolution: "slice-ansi@npm:3.0.0" + dependencies: + ansi-styles: ^4.0.0 + astral-regex: ^2.0.0 + is-fullwidth-code-point: ^3.0.0 + checksum: 5ec6d022d12e016347e9e3e98a7eb2a592213a43a65f1b61b74d2c78288da0aded781f665807a9f3876b9daa9ad94f64f77d7633a0458876c3a4fdc4eb223f24 + languageName: node + linkType: hard + +"slice-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "slice-ansi@npm:4.0.0" + dependencies: + ansi-styles: ^4.0.0 + astral-regex: ^2.0.0 + is-fullwidth-code-point: ^3.0.0 + checksum: 4a82d7f085b0e1b070e004941ada3c40d3818563ac44766cca4ceadd2080427d337554f9f99a13aaeb3b4a94d9964d9466c807b3d7b7541d1ec37ee32d308756 + languageName: node + linkType: hard + +"sshpk@npm:^1.14.1": + version: 1.17.0 + resolution: "sshpk@npm:1.17.0" + dependencies: + asn1: ~0.2.3 + assert-plus: ^1.0.0 + bcrypt-pbkdf: ^1.0.0 + dashdash: ^1.12.0 + ecc-jsbn: ~0.1.1 + getpass: ^0.1.1 + jsbn: ~0.1.0 + safer-buffer: ^2.0.2 + tweetnacl: ~0.14.0 + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: ba109f65c8e6c35133b8e6ed5576abeff8aa8d614824b7275ec3ca308f081fef483607c28d97780c1e235818b0f93ed8c8b56d0a5968d5a23fd6af57718c7597 + languageName: node + linkType: hard + +"string-width@npm:^4.1.0, string-width@npm:^4.2.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"strip-final-newline@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-final-newline@npm:2.0.0" + checksum: 69412b5e25731e1938184b5d489c32e340605bb611d6140344abc3421b7f3c6f9984b21dff296dfcf056681b82caa3bb4cc996a965ce37bcfad663e92eae9c64 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: ^4.0.0 + checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a + languageName: node + linkType: hard + +"supports-color@npm:^8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: ^4.0.0 + checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 + languageName: node + linkType: hard + +"throttleit@npm:^1.0.0": + version: 1.0.0 + resolution: "throttleit@npm:1.0.0" + checksum: 1b2db4d2454202d589e8236c07a69d2fab838876d370030ebea237c34c0a7d1d9cf11c29f994531ebb00efd31e9728291042b7754f2798a8352ec4463455b659 + languageName: node + linkType: hard + +"through@npm:^2.3.8": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd + languageName: node + linkType: hard + +"tmp@npm:~0.2.1": + version: 0.2.1 + resolution: "tmp@npm:0.2.1" + dependencies: + rimraf: ^3.0.0 + checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e + languageName: node + linkType: hard + +"tough-cookie@npm:~2.5.0": + version: 2.5.0 + resolution: "tough-cookie@npm:2.5.0" + dependencies: + psl: ^1.1.28 + punycode: ^2.1.1 + checksum: 16a8cd090224dd176eee23837cbe7573ca0fa297d7e468ab5e1c02d49a4e9a97bb05fef11320605eac516f91d54c57838a25864e8680e27b069a5231d8264977 + languageName: node + linkType: hard + +"tslib@npm:^2.1.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 + languageName: node + linkType: hard + +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: ^5.0.1 + checksum: 05f6510358f8afc62a057b8b692f05d70c1782b70db86d6a1e0d5e28a32389e52fa6e7707b6c5ecccacc031462e4bc35af85ecfe4bbc341767917b7cf6965711 + languageName: node + linkType: hard + +"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 6061daba1724f59473d99a7bb82e13f211cdf6e31315510ae9656fefd4779851cb927adad90f3b488c8ed77c106adc0421ea8055f6f976ff21b27c5c4e918487 + languageName: node + linkType: hard + +"type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: e6b32a3b3877f04339bae01c193b273c62ba7bfc9e325b8703c4ee1b32dc8fe4ef5dfa54bf78265e069f7667d058e360ae0f37be5af9f153b22382cd55a9afe0 + languageName: node + linkType: hard + +"typescript@npm:^4.1.3": + version: 4.8.2 + resolution: "typescript@npm:4.8.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 7f5b81d0d558c9067f952c7af52ab7f19c2e70a916817929e4a5b256c93990bf3178eccb1ac8a850bc75df35f6781b6f4cb3370ce20d8b1ded92ed462348f628 + languageName: node + linkType: hard + +"typescript@patch:typescript@^4.1.3#~builtin": + version: 4.8.2 + resolution: "typescript@patch:typescript@npm%3A4.8.2#~builtin::version=4.8.2&hash=a1c5e5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 5cb0f02f414f5405f4b0e7ee1fd7fa9177b6a8783c9017b6cad85f56ce4c4f93e0e6f2ce37e863cb597d44227cd009474c9fbd85bf7a50004e5557426cb58079 + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.0 + resolution: "universalify@npm:2.0.0" + checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 + languageName: node + linkType: hard + +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 + languageName: node + linkType: hard + +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + +"verror@npm:1.10.0": + version: 1.10.0 + resolution: "verror@npm:1.10.0" + dependencies: + assert-plus: ^1.0.0 + core-util-is: 1.0.2 + extsprintf: ^1.2.0 + checksum: c431df0bedf2088b227a4e051e0ff4ca54df2c114096b0c01e1cbaadb021c30a04d7dd5b41ab277bcd51246ca135bf931d4c4c796ecae7a4fef6d744ecef36ea + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: ^2.0.0 + bin: + node-which: ./bin/node-which + checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 + languageName: node + linkType: hard + +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a + languageName: node + linkType: hard + +"wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 + languageName: node + linkType: hard + +"yauzl@npm:^2.10.0": + version: 2.10.0 + resolution: "yauzl@npm:2.10.0" + dependencies: + buffer-crc32: ~0.2.3 + fd-slicer: ~1.1.0 + checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b + languageName: node + linkType: hard diff --git a/docs/auth/auth-backend.md b/docs/auth/auth-backend.md deleted file mode 100644 index 6d8b95ac9f..0000000000 --- a/docs/auth/auth-backend.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -id: auth-backend -title: Auth backend -description: Documentation on Auth backend ---- - -## TODO diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index d46aff1b06..d35e7dd593 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -16,7 +16,8 @@ are stateless, so for a production deployment you will want to set up and connect to an external PostgreSQL instance where the backend plugins can store their state, rather than using SQLite. -By default, in an app created with `@backstage/create-app`, the frontend is +This section assumes that an [app](https://backstage.io/docs/getting-started/create-an-app) +has already been created with `@backstage/create-app`, in which the frontend is bundled and served from the backend. This is done using the `@backstage/plugin-app-backend` plugin, which also injects the frontend configuration into the app. This means that you only need to build and deploy a diff --git a/docs/features/kubernetes/authentication.md b/docs/features/kubernetes/authentication.md index 7669d967ed..d44d3a4a64 100644 --- a/docs/features/kubernetes/authentication.md +++ b/docs/features/kubernetes/authentication.md @@ -18,7 +18,7 @@ The providers currently available are divided into server side and client side. ## Server Side Providers These providers authenticate your _application_ with the cluster, meaning anyone that is -logged in into your backstage app will be granted the same access to Kubernetes objects. +logged in into your backstage app will be granted the same access to Kubernetes objects, including guest users. The providers available as server side are: @@ -28,6 +28,33 @@ The providers available as server side are: - `localKubectlProxy` - `serviceAccount` +### Azure + +The Azure server side authentication provider works by authenticating on the server with +the Azure CLI, please note that [Azure AD Authentication][1] is a requirement and has to +be enabled in your AKS cluster, then follow these steps: + +- [Install the Azure CLI][2] in the environment where the backstage application will run. +- Login with your Azure/Microsoft account with `az login` in the server's terminal. +- Go to your AKS cluster's resource page in Azure Console and follow the steps in the + `Connect` tab to set the subscription and get your credentials for `kubectl` integration. +- Configure your cluster to use the `azure` auth provider like this: + +```yaml +kubernetes: + clusterLocatorMethods: + - type: 'config' + clusters: + - name: My AKS cluster + url: ${AZURE_CLUSTER_API_SERVER_ADDRESS} + authProvider: azure + skipTLSVerify: true +``` + +To get the API server address for your Azure cluster, go to the Azure console page for the +cluster resource, go to `Overview` > `Properties` tab > `Networking` section and copy paste +the API server address directly in that `url` field. + ## Client Side Providers These providers authenticate your _user_ with the cluster. Each Backstage user will be @@ -41,3 +68,6 @@ The providers available as client side are: - `google` - `oidc` + +[1]: https://docs.microsoft.com/en-us/azure/aks/managed-aad +[2]: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 0adbec6a82..ec505672b5 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -413,6 +413,7 @@ rules: - ingresses - statefulsets - limitranges + - daemonsets verbs: - get - list diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index b35ceb907d..17a9029b2d 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -156,16 +156,17 @@ export default async function createPlugin( Here is a list of Open Source custom actions that you can add to your Backstage scaffolder backend: -| Name | Package | Owner | -| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -| Yeoman | [plugin-scaffolder-backend-module-yeoman](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-yeoman) | [Backstage](https://backstage.io) | -| Cookiecutter | [plugin-scaffolder-backend-module-cookiecutter](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-cookiecutter) | [Backstage](https://backstage.io) | -| Rails | [plugin-scaffolder-backend-module-rails](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-rails) | [Backstage](https://backstage.io) | -| HTTP requests | [scaffolder-backend-module-http-request](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-http-request) | [Roadie](https://roadie.io) | -| Utility actions | [scaffolder-backend-module-utils](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-utils) | [Roadie](https://roadie.io) | -| AWS cli actions | [scaffolder-backend-module-aws](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-aws) | [Roadie](https://roadie.io) | -| Scaffolder .NET Actions | [plugin-scaffolder-dotnet-backend](https://www.npmjs.com/package/@plusultra/plugin-scaffolder-dotnet-backend) | [Alef Carlos](https://github.com/alefcarlos) | -| Scaffolder Git Actions | [plugin-scaffolder-git-actions](https://www.npmjs.com/package/@mdude2314/backstage-plugin-scaffolder-git-actions) | [Drew Hill](https://github.com/arhill05) | -| Azure Pipeline Actions | [scaffolder-backend-module-azure-pipelines](https://www.npmjs.com/package/@parfuemerie-douglas/scaffolder-backend-module-azure-pipelines) | [Parfümerie Douglas](https://github.com/Parfuemerie-Douglas) | +| Name | Package | Owner | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | +| Yeoman | [plugin-scaffolder-backend-module-yeoman](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-yeoman) | [Backstage](https://backstage.io) | +| Cookiecutter | [plugin-scaffolder-backend-module-cookiecutter](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-cookiecutter) | [Backstage](https://backstage.io) | +| Rails | [plugin-scaffolder-backend-module-rails](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend-module-rails) | [Backstage](https://backstage.io) | +| HTTP requests | [scaffolder-backend-module-http-request](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-http-request) | [Roadie](https://roadie.io) | +| Utility actions | [scaffolder-backend-module-utils](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-utils) | [Roadie](https://roadie.io) | +| AWS cli actions | [scaffolder-backend-module-aws](https://www.npmjs.com/package/@roadiehq/scaffolder-backend-module-aws) | [Roadie](https://roadie.io) | +| Scaffolder .NET Actions | [plugin-scaffolder-dotnet-backend](https://www.npmjs.com/package/@plusultra/plugin-scaffolder-dotnet-backend) | [Alef Carlos](https://github.com/alefcarlos) | +| Scaffolder Git Actions | [plugin-scaffolder-git-actions](https://www.npmjs.com/package/@mdude2314/backstage-plugin-scaffolder-git-actions) | [Drew Hill](https://github.com/arhill05) | +| Azure Pipeline Actions | [scaffolder-backend-module-azure-pipelines](https://www.npmjs.com/package/@parfuemerie-douglas/scaffolder-backend-module-azure-pipelines) | [Parfümerie Douglas](https://github.com/Parfuemerie-Douglas) | +| Azure Repository Actions | [scaffolder-backend-module-azure-repositories](https://www.npmjs.com/package/@parfuemerie-douglas/scaffolder-backend-module-azure-repositories) | [Parfümerie Douglas](https://github.com/Parfuemerie-Douglas) | Have fun! 🚀 diff --git a/docs/features/software-templates/writing-custom-step-layouts.md b/docs/features/software-templates/writing-custom-step-layouts.md new file mode 100644 index 0000000000..69d61b93dc --- /dev/null +++ b/docs/features/software-templates/writing-custom-step-layouts.md @@ -0,0 +1,89 @@ +--- +id: writing-custom-step-layouts +title: Writing custom step layouts +description: How to override the default step form layout +--- + +Every form in each step rendered in the frontend uses the default form layout from [react-json-schema-form](https://react-jsonschema-form.readthedocs.io/). It is possible to override this behaviour by supplying a `ui:ObjectFieldTemplate` property for a particular step: + +```yaml +parameters: + - title: Fill in some steps + ui:ObjectFieldTemplate: TwoColumn +``` + +This is the same [field](https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/custom-templates/#objectfieldtemplate) used by [react-json-schema-form](https://react-jsonschema-form.readthedocs.io/) but we need to add a couple of steps to ensure that the string value of `TwoColumn` above is resolved to a react component. + +## Registering a React component as a custom step layout + +The [createScaffolderLayout](https://backstage.io/docs/reference/plugin-scaffolder.createscaffolderlayout) function is used to mark a component as a custom step layout: + +```ts +import React from 'react'; +import { + createScaffolderLayout, + LayoutTemplate, + scaffolderPlugin, +} from '@backstage/plugin-scaffolder'; +import { Grid } from '@material-ui/core'; + +const TwoColumn: LayoutTemplate = ({ properties, description, title }) => { + const mid = Math.ceil(properties.length / 2); + + return ( + <> +

{title}

+

In two column layout!!

+ + {properties.slice(0, mid).map(prop => ( + + {prop.content} + + ))} + {properties.slice(mid).map(prop => ( + + {prop.content} + + ))} + + {description} + + ); +}; + +export const TwoColumnLayout = scaffolderPlugin.provide( + createScaffolderLayout({ + name: 'TwoColumn', + component: TwoColumn, + }), +); +``` + +After you have registered your component as a custom layout then you need to provide the `layouts` to the `ScaffolderPage`: + +```tsx +import { MyCustomFieldExtension } from './scaffolder/MyCustomExtension'; +import { TwoColumnLayout } from './components/scaffolder/customScaffolderLayouts'; + +const routes = ( + + ... + }> + + + + + ... + +); +``` + +## Using the custom step layout + +Any component that has been passed to the `ScaffolderPage` as children of the `ScaffolderLayouts` component can be used as a `ui:ObjectFieldTemplate` in your template file: + +```yaml +parameters: + - title: Fill in some steps + ui:ObjectFieldTemplate: TwoColumn +``` diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index a1cd50f271..826121bd02 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -258,6 +258,10 @@ use `ui:widget: password` or set some properties of `ui:backstage`: show: false # won't print any info about 'hidden' property on Review Step ``` +### Custom step layouts + +If you find that the default layout of the form used in a particular step does not meet your needs then you can supply your own [custom step layout](./writing-custom-step-layouts.md). + ### Remove sections or fields based on feature flags Based on feature flags you can hide sections or even only fields of your diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md index 3413f32875..9ce65294ad 100644 --- a/docs/getting-started/configuration.md +++ b/docs/getting-started/configuration.md @@ -165,7 +165,7 @@ Open `packages/app/src/App.tsx` and below the last `import` line, add: ```typescript import { githubAuthApiRef } from '@backstage/core-plugin-api'; -import { SignInProviderConfig, SignInPage } from '@backstage/core-components'; +import { SignInPage } from '@backstage/core-components'; ``` Search for `const app = createApp({` in this file, and below `apis,` add: diff --git a/docs/permissions/frontend-integration.md b/docs/permissions/frontend-integration.md new file mode 100644 index 0000000000..d7302f9083 --- /dev/null +++ b/docs/permissions/frontend-integration.md @@ -0,0 +1,38 @@ +--- +id: frontend-integration +title: Frontend Integration +description: How to place your Backstage frontend components behind authorization +--- + +Now that we understand how to [author a permission policy](./writing-a-policy.md), let's consider cases where we'll need to supplement our policy with authorization checks on the frontend. + +In most cases, actual functionality that live within various plugins will already have been placed behind authorization by the plugin authors. The permission backend will use your permission policy to return an authorization result, and the plugin frontend will correspondingly show/hide/disable the relevant UI component. + +However, there are some cases where the integrator needs to supplement the policy on the frontend. One example is app level routing. + +If your Backstage permission policy may return a `DENY` for users requesting the `catalogEntityCreatePermission`, it may make sense, for example, to remove access to the `/catalog-import` page entirely: + +```diff +// packages/app/src/App.tsx + +... + ++ import { RequirePermission } from '@backstage/plugin-permission-react'; ++ import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common'; + +... + +- } /> ++ ++ ++ ++ } ++ /> +... + +``` + +With this change, users who are denied the `catalogEntityCreatePermission` should now be unable to access the `/catalog-import` page. diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index c73fe800b4..124605cb66 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -20,15 +20,18 @@ Let's navigate to the file `plugins/todo-list-common/src/permissions.ts` and add - export const tempExamplePermission = createPermission({ - name: 'temp.example.noop', - attributes: {}, -+ export const todoListCreate = createPermission({ ++ export const todoListCreatePermission = createPermission({ + name: 'todo.list.create', + attributes: { action: 'create' }, }); + +- export const todoListPermissions = [tempExamplePermission]; ++ export const todoListPermissions = [todoListCreatePermission]; ``` For this tutorial, we've automatically exported all permissions from this file (see `plugins/todo-list-common/src/index.ts`). -> Note: All permissions authorized by your plugin should be exported from a ["common-library" package](https://backstage.io/docs/local-dev/cli-build-system#package-roles). This allows Backstage integrators to reference them in frontend components and permission policies. +> Note: We use a separate `todo-list-common` package since all permissions authorized by your plugin should be exported from a ["common-library" package](https://backstage.io/docs/local-dev/cli-build-system#package-roles). This allows Backstage integrators to reference them in frontend components and permission policies. ## Authorizing using the new permission @@ -47,7 +50,7 @@ Edit `plugins/todo-list-backend/src/service/router.ts`: - import { InputError } from '@backstage/errors'; + import { InputError, NotAllowedError } from '@backstage/errors'; + import { PermissionEvaluator, AuthorizeResult } from '@backstage/plugin-permission-common'; -+ import { todoListCreate } from '@internal/plugin-todo-list-common'; ++ import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; ... @@ -72,7 +75,7 @@ Edit `plugins/todo-list-backend/src/service/router.ts`: const user = token ? await identity.authenticate(token) : undefined; author = user?.identity.userEntityRef; + const decision = ( -+ await permissions.authorize([{ permission: todoListCreate }], { ++ await permissions.authorize([{ permission: todoListCreatePermission }], { + token, + }) + )[0]; @@ -135,7 +138,7 @@ In order to test the logic above, the integrators of your backstage instance nee + PolicyQuery, } from '@backstage/plugin-permission-node'; + import { isPermission } from '@backstage/plugin-permission-common'; -+ import { todoListCreate } from '@internal/plugin-todo-list-common'; ++ import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; class TestPermissionPolicy implements PermissionPolicy { - async handle(): Promise { @@ -143,7 +146,7 @@ In order to test the logic above, the integrators of your backstage instance nee + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { -+ if (isPermission(request.permission, todoListCreate)) { ++ if (isPermission(request.permission, todoListCreatePermission)) { + return { + result: AuthorizeResult.DENY, + }; @@ -160,7 +163,7 @@ Now the frontend should show an error whenever you try to create a new Todo item Let's flip the result back to `ALLOW` before moving on. ```diff - if (isPermission(request.permission, todoListCreate)) { + if (isPermission(request.permission, todoListCreatePermission)) { return { - result: AuthorizeResult.DENY, + result: AuthorizeResult.ALLOW, diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index a76518b202..5bace9ad7b 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -15,27 +15,30 @@ Let's add a new permission to the file `plugins/todo-list-common/src/permissions + export const TODO_LIST_RESOURCE_TYPE = 'todo-item'; + - export const todoListCreate = createPermission({ + export const todoListCreatePermission = createPermission({ name: 'todo.list.create', attributes: { action: 'create' }, }); + -+ export const todoListUpdate = createPermission({ ++ export const todoListUpdatePermission = createPermission({ + name: 'todo.list.update', + attributes: { action: 'update' }, + resourceType: TODO_LIST_RESOURCE_TYPE, + }); + +- export const todoListPermissions = [todoListCreatePermission]; ++ export const todoListPermissions = [todoListCreatePermission, todoListUpdatePermission]; ``` -Notice that unlike `todoListCreate`, the `todoListUpdate` permission contains a `resourceType` field. This field indicates to the permission framework that this permission is intended to be authorized in the context of a resource with type `'todo-item'`. You can use whatever string you like as the resource type, as long as you use the same value consistently for each type of resource. +Notice that unlike `todoListCreatePermission`, the `todoListUpdatePermission` permission contains a `resourceType` field. This field indicates to the permission framework that this permission is intended to be authorized in the context of a resource with type `'todo-item'`. You can use whatever string you like as the resource type, as long as you use the same value consistently for each type of resource. ## Setting up authorization for the update permission To start, let's edit `plugins/todo-list-backend/src/service/router.ts` in the same manner as we did in the previous section: ```diff -- import { todoListCreate } from '@internal/plugin-todo-list-common'; -+ import { todoListCreate, todoListUpdate } from '@internal/plugin-todo-list-common'; +- import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; ++ import { todoListCreatePermission, todoListUpdatePermission } from '@internal/plugin-todo-list-common'; ... @@ -49,7 +52,7 @@ To start, let's edit `plugins/todo-list-backend/src/service/router.ts` in the sa } + const decision = ( + await permissions.authorize( -+ [{ permission: todoListUpdate, resourceRef: req.body.id }], ++ [{ permission: todoListUpdatePermission, resourceRef: req.body.id }], + { + token, + }, @@ -119,6 +122,7 @@ Now, let's create the new endpoint by editing `plugins/todo-list-backend/src/ser - `getResources`: a function that accepts an array of `resourceRefs` in the same format you expect to be passed to `authorize`, and returns an array of the corresponding resources. - `resourceType`: the same value used in the permission rule above. +- `permissions`: the list of permissions that your plugin accepts. - `rules`: an array of all the permission rules you want to support in conditional decisions. ```diff @@ -127,7 +131,7 @@ Now, let's create the new endpoint by editing `plugins/todo-list-backend/src/ser - import { add, getAll, update } from './todos'; + import { add, getAll, getTodo, update } from './todos'; + import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; -+ import { TODO_LIST_RESOURCE_TYPE } from '@internal/plugin-todo-list-common'; ++ import { TODO_LIST_RESOURCE_TYPE, todoListPermissions } from '@internal/plugin-todo-list-common'; + import { rules } from './rules'; export async function createRouter( @@ -140,6 +144,7 @@ Now, let's create the new endpoint by editing `plugins/todo-list-backend/src/ser + return resourceRefs.map(getTodo); + }, + resourceType: TODO_LIST_RESOURCE_TYPE, ++ permissions: todoListPermissions, + rules: Object.values(rules), + }); @@ -196,10 +201,10 @@ Let's go back to the permission policy's handle function and try to authorize ou PolicyQuery, } from '@backstage/plugin-permission-node'; import { isPermission } from '@backstage/plugin-permission-common'; -- import { todoListCreate } from '@internal/plugin-todo-list-common'; +- import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; + import { -+ todoListCreate, -+ todoListUpdate, ++ todoListCreatePermission, ++ todoListUpdatePermission, + TODO_LIST_RESOURCE_TYPE, + } from '@internal/plugin-todo-list-common'; + import { @@ -209,13 +214,13 @@ Let's go back to the permission policy's handle function and try to authorize ou ... - if (isPermission(request.permission, todoListCreate)) { + if (isPermission(request.permission, todoListCreatePermission)) { return { result: AuthorizeResult.ALLOW, }; } -+ if (isPermission(request.permission, todoListUpdate)) { ++ if (isPermission(request.permission, todoListUpdatePermission)) { + return createTodoListConditionalDecision( + request.permission, + todoListConditions.isOwner(user?.identity.userEntityRef), diff --git a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md index 582553832b..c813ba594c 100644 --- a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md +++ b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md @@ -42,12 +42,12 @@ Let's add another permission to the plugin. export const TODO_LIST_RESOURCE_TYPE = 'todo-item'; - export const todoListCreate = createPermission({ + export const todoListCreatePermission = createPermission({ name: 'todo.list.create', attributes: { action: 'create' }, }); - export const todoListUpdate = createPermission({ + export const todoListUpdatePermission = createPermission({ name: 'todo.list.update', attributes: { action: 'update' }, resourceType: TODO_LIST_RESOURCE_TYPE, @@ -58,6 +58,9 @@ Let's add another permission to the plugin. + attributes: { action: 'read' }, + resourceType: TODO_LIST_RESOURCE_TYPE, + }); + +- export const todoListPermissions = [todoListCreatePermission, todoListUpdatePermission]; ++ export const todoListPermissions = [todoListCreatePermission, todoListUpdatePermission, todoListReadPermission]; ``` ## Using conditional policy decisions @@ -126,18 +129,18 @@ Let's update our permission policy to return a conditional result whenever a `to ... import { - todoListCreate, - todoListUpdate, -+ todoListRead, + todoListCreatePermission, + todoListUpdatePermission, ++ todoListReadPermission, TODO_LIST_RESOURCE_TYPE, } from '@internal/plugin-todo-list-common'; ... -- if (isPermission(request.permission, todoListUpdate)) { +- if (isPermission(request.permission, todoListUpdatePermission)) { + if ( -+ isPermission(request.permission, todoListUpdate) || -+ isPermission(request.permission, todoListRead) ++ isPermission(request.permission, todoListUpdatePermission) || ++ isPermission(request.permission, todoListReadPermission) + ) { return createTodoListConditionalDecision( request.permission, diff --git a/docs/permissions/plugin-authors/05-frontend-authorization.md b/docs/permissions/plugin-authors/05-frontend-authorization.md new file mode 100644 index 0000000000..dc4df7839c --- /dev/null +++ b/docs/permissions/plugin-authors/05-frontend-authorization.md @@ -0,0 +1,167 @@ +--- +id: 05-frontend-authorization +title: 5. Frontend Components with Authorization +description: Placing frontend components behind authorization +--- + +In the previous sections, we learned how to protect our plugin's backend API routes with the permission framework. Most routes that return some data to be displayed (such as our `GET /todos` route) need no additional changes on the frontend, as the backend will simply return an empty list or a `404`. However, for UI elements that trigger a mutative action, it's common practice to hide or disable them when a user doesn't have permission. + +Take, for example, the "Add" button in our todo list application. When a user clicks this button, the frontend makes a `POST` request to the `/todos` route of our backend. If a user tries to add a todo but is not authorized, they will have no way of knowing this until they perform the action and are faced with an error. This is a poor user experience. We can do better by disabling the add button. + +> Note: Placing frontend components behind authorization cannot take the place of placing your backend routes behind authorization. Authorization checks on the frontend should be used in _addition_ to the corresponding backend authorization, as an improvement to the user experience. If you do not place your backend route behind authorization, a malicious actor can still send a request to the route even if you disabled the corresponding frontend component. + +## Using `usePermission` + +Let's start by adding the packages we will need: + +``` +$ yarn workspace @internal/plugin-todo-list \ + add @backstage/plugin-permission-react @internal/plugin-todo-list-common +``` + +Let's make the following changes in `plugins/todo-list/src/components/TodoListPage/TodoListPage.tsx`: + +```diff +... + + import { + alertApiRef, + discoveryApiRef, + fetchApiRef, + useApi, + } from '@backstage/core-plugin-api'; ++ import { usePermission } from '@backstage/plugin-permission-react'; ++ import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; + +... + + function AddTodo({ onAdd }: { onAdd: (title: string) => any }) { + const title = useRef(''); ++ const { loading: loadingPermission, allowed: canAddTodo } = usePermission({ permission: todoListCreatePermission }); + + return ( + <> + Add todo + + (title.current = e.target.value)} + /> +- ++ {!loadingPermission && ( ++ ++ )} + + + ); + } + +... +``` + +Here we are using the [`usePermission` hook](https://backstage.io/docs/reference/plugin-permission-react.usepermission) to communicate with the permission policy and receive a decision on whether this user is authorized to create a todo list item. + +It's really that simple! Let's change our policy to test the disabled button: + +```diff +// packages/backend/src/plugins/permission.ts + +... + + if (isPermission(request.permission, todoListCreatePermission)) { + return { +- result: AuthorizeResult.ALLOW, ++ result: AuthorizeResult.DENY, + }; + } + +... +``` + +And now you should see that you are not able to create a todo item from the frontend! + +## Using `RequirePermission` + +Providing a disabled state can be a helpful signal to users, but there may be cases where hiding the element is preferred. For such cases, you can use the provided [`RequirePermission` component](https://backstage.io/docs/reference/plugin-permission-react.requirepermission): + +```diff +// plugins/todo-list/src/components/TodoListPage/TodoListPage.tsx + +... + + import { + alertApiRef, + discoveryApiRef, + fetchApiRef, + useApi, + } from '@backstage/core-plugin-api'; +- import { usePermission } from '@backstage/plugin-permission-react'; ++ import { RequirePermission } from '@backstage/plugin-permission-react'; + import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; + +... + + export const TodoListPage = () => { + +... + + +- +- +- ++ ++ ++ ++ ++ + + + + + +... + + + function AddTodo({ onAdd }: { onAdd: (title: string) => any }) { + const title = useRef(''); +- const { loading: loadingPermission, allowed: canAddTodo } = usePermission({ permission: todoListCreatePermission }); + + return ( + <> + Add todo + + (title.current = e.target.value)} + /> +- {!loadingPermission && ( +- +- )} ++ + + + ); + } + +... +``` + +Now you should find that the component for adding a todo list item does not render at all. Success! diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index c6f4a50e60..3eddb9e1c2 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -13,10 +13,10 @@ A new, bare-bones backend plugin package can be created by issuing the following command in your Backstage repository root: ```sh -yarn create-plugin --backend +yarn new --select backend-plugin ``` -Please also see the `--help` flag for the `create-plugin` command for some +Please also see the `--help` flag for the `new` command for some further options that are available, notably the `--scope` and `--no-private` flags that control naming and publishing of the newly created package. Your repo root `package.json` will probably also have some default values already set up diff --git a/docs/releases/v1.6.0-changelog.md b/docs/releases/v1.6.0-changelog.md new file mode 100644 index 0000000000..4172c19244 --- /dev/null +++ b/docs/releases/v1.6.0-changelog.md @@ -0,0 +1,3550 @@ +# Release v1.6.0 + +## @backstage/catalog-client@1.1.0 + +### Minor Changes + +- 65d1d4343f: Adding `validateEntity` method that calls `/validate-entity` endpoint. + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/cli@0.19.0 + +### Minor Changes + +- 1fe6823bb5: Updated dependency `eslint-plugin-jest` to `^27.0.0`. + + Note that this major update to the Jest plugin contains some breaking changes. + This means that some of your tests may start seeing some new lint errors. [Read + about them + here](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md#2700-2022-08-28). + + These are mostly possible to fix automatically. You can try to run `yarn backstage-cli repo lint --fix` in your repo root to have most or all of them + corrected. + +### Patch Changes + +- 8d886dd33e: The `create-plugin` and `create` commands have both been deprecated in favor of a new `new` command. The `new` command is functionally identical to `create`, but the new naming makes it possible to use as yarn script, since `yarn create` is reserved. + +- cc63eb8611: Sort entries in skeleton.tar.gz for better docker layer caching + +- 548053614a: Deprecated the `plugin:diff` command. If you wish to keep running similar checks in your project we recommend using bespoke scripts. A useful utility for such scripts is `@manypkg/get-packages`, which helps you enumerate all packages in a monorepo. + +- 513b4dd4ef: The `versions:bump` command will now update dependency ranges in `package.json`, even if the new version is within the current range. + +- 221e951298: Added support for custom certificate for webpack dev server. + +- 934cc34563: Avoid validating the backend configuration schema when loading static configuration for building the frontend. + +- 3d4f5daadf: Remove use of deprecated trimLeft/trimRight + +- 817f3196f6: Added a new `migrate react-router-deps` command to aid in the migration to React Router v6 stable. + +- 742cb4f3d7: Fix issue when using `.jsx` files inside tests + +- e7600bdb04: Tweaked workspace packaging to not rewrite existing `package.json` files. + +- 6ae0f6a719: Switch out `sucrase` for `swc` for transpilation. + + `sucrase` is a little more relaxed when it comes to supporting the ways of mocking in `jest`. You might have to make some changes to your tests to meet the `jest` standard and spec if your tests seems to start failing. + + Mocks that look like this are invalid, and they will throw a reference error in line with the `jest` documentation [here on example 3](https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter) + + ```ts + const mockCommandExists = jest.fn(); + jest.mock('command-exists', () => mockCommandExists); + ``` + + You might need to update these mocks to look a little like the following to defer the call to the `jest.fn()` spy until the mock is called. + + ```ts + const mockCommandExists = jest.fn(); + jest.mock( + 'command-exists', + () => + (...args: any[]) => + commandExists(...args), + ); + ``` + + Also, imports are immutable. So it means that you might get some errors when trying to use `jest.spyOn` with starred imports. You might see an error like this: + + ```log + TypeError: Cannot redefine property: executeFrameHandlerStrategy + at Function.defineProperty () + + 20 | import { AuthResolverContext } from '../types'; + 21 | + > 22 | const mockFrameHandler = jest.spyOn( + | ^ + 23 | helpers, + 24 | 'executeFrameHandlerStrategy', + 25 | ) as unknown as jest.MockedFunction< + ``` + + This happens when you try to do `import * as something from './something'` and then `jest.spyOn(something, 'test)`. You will need to add a `jest.mock` call to mock out the required starred import to return `jest.fn()` functions from the start. Something like this fixes the above test: + + ```ts + jest.mock('../../helpers', () => ({ + executeFrameHandlerStrategy: jest.fn(), + })); + ``` + + You can also remove any occurrence of `hot(App)` and any import of `react-hot-loader` if you're using the that package locally, as all this has now been replaced with [React Refresh](https://www.npmjs.com/package/react-refresh) which you will get out of the box with the new CLI. + + **Note** If you're experiencing difficulties with running tests after the migration, please reach out to us on Discord to see if we can help, or raise an issue. But in the meantime you can switch back to the existing behaviour by using the following config in your root `package.json`. + + ```json + "jest": { + "transform": { + "\\.(js|jsx|ts|tsx|mjs|cjs)$": "@backstage/cli/config/jestSucraseTransform.js", + "\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg|eot|woff|woff2|ttf)$": "@backstage/cli/config/jestFileTransform.js", + "\\.(yaml)$": "jest-transform-yaml" + } + } + ``` + +- 1cb078ad9f: Fixed a misconfiguration where all modules where treated as ESM by the React Refresh plugin for Webpack. + +- 1fd4f2746f: Removed internal dependencies on Lerna. It is now no longer necessary to have Lerna installed in a project to use all features of the Backstage CLI. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- 33fbd9f9a4: Updated dependency `@types/minimatch` to `^5.0.0`. + +- 68c2697077: Added a new `backstage-cli repo clean` command that cleans the repo root and runs the clean script in all packages. + +- 7d47def9c4: Added dependency on `@types/jest` v27. The `@types/jest` dependency has also been removed from the plugin template and should be removed from any of your own internal packages. If you wish to override the version of `@types/jest` or `jest`, use Yarn resolutions. + +- a7e82c9b01: Updated `versions:bump` command to be compatible with Yarn 3. + +- Updated dependencies + - @backstage/config-loader@1.1.4 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/release-manifests@0.0.6 + +## @backstage/core-app-api@1.1.0 + +### Minor Changes + +- a448fea691: Updated the routing system to be compatible with React Router v6 stable. + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. + +- f9ec4e46e3: When using React Router v6 stable, it is now possible for components within the `Route` element tree to have `path` props, although they will be ignored. + +- 7d47def9c4: Removed dependency on `@types/jest`. + +- 744fea158b: Added `getSystemIcons()` function to the `AppContext` available through `useApp` that will pull a list of all the icons that have been registered in the App. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- 8448b53dd6: Clarify that the `WebStorage` observable returns `JsonValue` items. + +- 70299c99d5: Updated `FlatRoutes` to be compatible with React Router v6 stable. + +- e9d40ebf54: If you'd like to send analytics events to multiple implementations, you may now + do so using the `MultipleAnalyticsApi` implementation provided by this package. + + ```tsx + import { MultipleAnalyticsApi } from '@backstage/core-app-api'; + import { + analyticsApiRef, + configApiRef, + storageApiRef, + identityApiRef, + } from '@internal/backstage/core-plugin-api'; + import { CustomAnalyticsApi } from '@internal/analytics'; + import { VendorAnalyticsApi } from '@vendor/analytics'; + + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef, identityApi: identityApiRef, storageApi: storageApiRef }, + factory: ({ configApi, identityApi, storageApi }) => + MultipleAnalyticsApi.fromApis([ + VendorAnalyticsApi.fromConfig(configApi, { identityApi }), + CustomAnalyticsApi.fromConfig(configApi, { identityApi, storageApi }), + ]), + }), + ``` + +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + +## @backstage/test-utils@1.2.0 + +### Minor Changes + +- 817f3196f6: Elements rendered in a test app are no longer wrapped in a `Routes` and `Route` element, as this is not compatible with React Router v6 stable. + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- d9e39544be: Add missing peer dependencies +- Updated dependencies + - @backstage/core-app-api@1.1.0 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/plugin-auth-backend@0.16.0 + +### Minor Changes + +- 2fc41ebf07: Removed the previously deprecated class `AtlassianAuthProvider`. Please use `providers.atlassian.create(...)` instead. +- a291688bc5: Renamed the `RedirectInfo` type to `OAuthStartResponse` +- 8600855fbf: The auth0 integration is updated to use the `passport-auth0` library. The configuration under `auth.providers.auth0.\*` now supports an optional `audience` parameter; providing that allows you to connect to the correct API to get permissions, access tokens, and full profile information. + + [What is an Audience](https://community.auth0.com/t/what-is-the-audience/71414) + +### Patch Changes + +- 5b011fb2e6: Allow adding misc claims to JWT +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- e1ebaeb332: Cloudflare Access Provider: Add JWT to CloudflareAccessResult +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-azure-devops@0.2.0 + +### Minor Changes + +- 6c1c59b96e: Added README card `EntityAzureReadmeCard` for Azure Devops. + + To get the README component working you'll need to do the following two steps: + + 1. First we need to add the @backstage/plugin-azure-devops package to your frontend app: + + ```bash + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-azure-devops + ``` + + 2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app: + + ```tsx + // In packages/app/src/components/catalog/EntityPage.tsx + import { + EntityAzureReadmeCard, + isAzureDevOpsAvailable, + } from '@backstage/plugin-azure-devops'; + + // As it is a card, you can customize it the way you prefer + // For example in the Service section + + const overviewContent = ( + + + + + ... + + + + + + + + ); + ``` + + **Notes:** + + - You'll need to add the `EntitySwitch.Case` above from step 2 to all the entity sections you want to see Readme in. For example if you wanted to see Readme when looking at Website entities then you would need to add this to the `websiteEntityPage` section. + - The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation + - The `maxHeight` property on the `EntityAzureReadmeCard` will set the maximum screen size you would like to see, if not set it will default to 100% + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-azure-devops-common@0.3.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-azure-devops-common@0.3.0 + +### Minor Changes + +- 6c1c59b96e: Added README card `EntityAzureReadmeCard` for Azure Devops. + + To get the README component working you'll need to do the following two steps: + + 1. First we need to add the @backstage/plugin-azure-devops package to your frontend app: + + ```bash + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-azure-devops + ``` + + 2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app: + + ```tsx + // In packages/app/src/components/catalog/EntityPage.tsx + import { + EntityAzureReadmeCard, + isAzureDevOpsAvailable, + } from '@backstage/plugin-azure-devops'; + + // As it is a card, you can customize it the way you prefer + // For example in the Service section + + const overviewContent = ( + + + + + ... + + + + + + + + ); + ``` + + **Notes:** + + - You'll need to add the `EntitySwitch.Case` above from step 2 to all the entity sections you want to see Readme in. For example if you wanted to see Readme when looking at Website entities then you would need to add this to the `websiteEntityPage` section. + - The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation + - The `maxHeight` property on the `EntityAzureReadmeCard` will set the maximum screen size you would like to see, if not set it will default to 100% + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup + +## @backstage/plugin-catalog-backend@1.4.0 + +### Minor Changes + +- dd395335bc: Allow unknown typed location from being registered via the location service by configuration settings +- 651c9d6800: The search index now does retain fields that have a very long value, but in the form of just a null. This makes it possible to at least filter for their existence. +- 6e63bc43f2: Added the `refresh` function to the Connection of the entity providers. + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 07dda0b746: Add optional value to `hasAnnotation` permission rule +- 243533ecdc: Added support to mysql on some raw queries +- ce77e78c93: Fixes a bug to be able to utilize refresh keys after the entity is loaded from cache +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 679f7c5e95: Include entity ref into error message when catalog policies fail +- 06e2b077a1: Limit the length of error messages that get written to the database and logs - to prevent performance issues +- 62788b2ee8: The experimental `CatalogProcessingExtensionPoint` now accepts multiple providers and processors at once. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-scaffolder-common@1.2.0 + - @backstage/plugin-catalog-common@1.0.6 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-catalog-node@1.1.0 + +### Minor Changes + +- 9743bc788c: Added refresh function to the `EntityProviderConnection` to be able to schedule refreshes from entity providers. + +### Patch Changes + +- 7d7d947352: Adds experimental `catalogServiceRef` for obtaining a `CatalogClient` in the new backend system. +- 409ed984e8: Updated usage of experimental backend service APIs. +- 62788b2ee8: The experimental `CatalogProcessingExtensionPoint` now accepts multiple providers and processors at once. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-dynatrace@0.2.0 + +### Minor Changes + +- e44c0b3811: New features: + + - Some visual improvements to the table that displays Problems + - Added support for viewing recent Synthetics results using + - Added some additional linking to the configured Dynatrace instance + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-playlist@0.1.0 + +### Minor Changes + +- d3737da337: Implement playlist plugin, check out the `README.md` for more details! + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-playlist-common@0.1.0 + - @backstage/plugin-catalog-common@1.0.6 + +## @backstage/plugin-playlist-backend@0.1.0 + +### Minor Changes + +- d3737da337: Implement playlist plugin, check out the `README.md` for more details! + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/backend-test-utils@0.1.28 + - @backstage/plugin-playlist-common@0.1.0 + +## @backstage/plugin-playlist-common@0.1.0 + +### Minor Changes + +- d3737da337: Implement playlist plugin, check out the `README.md` for more details! + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/plugin-scaffolder@1.6.0 + +### Minor Changes + +- 3424a8075d: Added support for `async` validation for the `next` version of the plugin +- ad036784e9: Ability to modify the layout of the step form +- 192d856495: Implementing review step for the scaffolder under `create/next` + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 9ffb75616d: Fix bug with empty strings in `EntityPicker` +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- a66d44b72b: Fixing bug when the workspace would not be automatically saved when using `allowedOwners` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 6522e459aa: Support displaying and ordering by counts in `EntityTagPicker` field. Add the `showCounts` option to enable this. Also support configuring `helperText`. +- f0510a20b5: Addition of a dismissible Error Banner in Scaffolder page +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- 9097278df2: Updated dependency `json-schema-library` to `^7.0.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- de336de9cd: fix uiSchema generation when using complex dependencies +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-scaffolder-common@1.2.0 + - @backstage/plugin-catalog-common@1.0.6 + +## @backstage/plugin-scaffolder-backend@1.6.0 + +### Minor Changes + +- ea2eee9e6a: Add the option for a homepage when using the `github:publish` action + +- 8872cc735d: Fixed a bug in plugin-scaffolder-backend where it ignores the skip migration database options. + + To use this new implementation you need to create the instance of `DatabaseTaskStore` using the `PluginDatabaseManager` instead of `Knex`; + + import { DatabaseManager, getRootLogger, loadBackendConfig } from '@backstage/backend-common'; + import { DatabaseTaskStore } from '@backstage/plugin-scaffolder-backend'; + + const config = await loadBackendConfig({ argv: process.argv, logger: getRootLogger() }); + const databaseManager = DatabaseManager.fromConfig(config, { migrations: { skip: true } }); + const databaseTaskStore = await DatabaseTaskStore.create(databaseManager); + +- 7db9613671: Added `projectId` for gitlab projects to be displayed in the `gitlab:publish` output + +- d1f7ba58e3: Added `repositoryId` output when create a repository in Azure + +- 1ff817b3f0: add entity metadata to the template info type + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- de8ee4afe3: Provide information about the user into scaffolder template action's context +- 096631e571: Added support for handling broken symlinks within the scaffolder backend. This is intended for templates that may hold a symlink that is invalid at build time but valid within the destination repo. +- 0d8d650e32: Applied the fix from version 1.5.1 of this package, which is part of the v1.5.1 release of Backstage. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 2df9955f4a: Removed the depreacated `publish:file` action, use the template editor to test templates instead. +- 0ecc9a6784: Properly set `ctx.isDryRun` when running actions in dry run mode. Also always log action inputs for debugging purposes when running in dry run mode. +- 6b9f6c0a4d: Added alpha `scaffolderPlugin` to be used with experimental backend system. +- 83c037cd46: Disable octokit throttling in publish:github:pull-request +- 2cbd533426: Uptake the `IdentityApi` change to use `getIdentity` instead of `authenticate` for retrieving the logged in users identity. +- ef9ab322de: Minor API signatures cleanup +- 50467bc15b: The number of task workers used to execute templates now default to 3, rather than 1. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-scaffolder-common@1.2.0 + +## @backstage/plugin-scaffolder-common@1.2.0 + +### Minor Changes + +- 1ff817b3f0: add entity metadata to the template info type + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-search-backend-module-pg@0.4.0 + +### Minor Changes + +- 8872cc735d: Fixed a bug in search-backend-module-pg where it ignores the skip migration database options when using the database. + + To use this new implementation you need to create the instance of `DatabaseDocumentStore` using the `PluginDatabaseManager` instead of `Knex`; + + import { DatabaseManager, getRootLogger, loadBackendConfig } from '@backstage/backend-common'; + import { DatabaseDocumentStore } from '@backstage/plugin-search-backend-module-pg'; + + const config = await loadBackendConfig({ argv: process.argv, logger: getRootLogger() }); + const databaseManager = DatabaseManager.fromConfig(config, { migrations: { skip: true } }); + const databaseDocumentStore = await DatabaseDocumentStore.create(databaseManager); + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-search-backend-node@1.0.2 + - @backstage/config@1.0.2 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-search-react@1.1.0 + +### Minor Changes + +- 97f2b8f3fd: The `` component now accepts a optional `query` prop to request results from the search api: + + > Note: If a query prop is not defined, the results will by default be consumed from the context. + + Example: + + ```jsx + import React, { useState, useCallback } from 'react'; + + import { Grid, List, Paper } from '@material-ui/core'; + + import { Page, Header, Content, Lifecycle } from '@backstage/core-components'; + import { + DefaultResultListItem, + SearchBarBase, + SearchResult, + } from '@backstage/plugin-search-react'; + + const SearchPage = () => { + const [query, setQuery] = useState({ + term: '', + types: [], + filters: {}, + }); + + const handleChange = useCallback( + (term: string) => { + setQuery(prevQuery => ({ ...prevQuery, term })); + }, + [setQuery], + ); + + return ( + +
} /> + + + + + + + + + + {({ results }) => ( + + {results.map(({ document }) => ( + + ))} + + )} + + + + + + ); + }; + ``` + + Additionally, a search page can also be composed using these two new results layout components: + + ```jsx + // Example rendering results as list + + {({ results }) => ( + { + switch (type) { + case 'custom-result-item': + return ( + + ); + default: + return ( + + ); + } + }} + /> + )} + + ``` + + ```jsx + // Example rendering results as groups + + {({ results }) => ( + <> + } + title="Custom" + link="See all custom results" + resultItems={results.filter( + ({ type }) => type === 'custom-result-item', + )} + renderResultItem={({ document }) => ( + + )} + /> + } + title="Default" + resultItems={results.filter( + ({ type }) => type !== 'custom-result-item', + )} + renderResultItem={({ document }) => ( + + )} + /> + + )} + + ``` + + A `SearchResultList` and `SearchResultGroup` components were also created for users who have search pages with multiple queries, both are specializations of `SearchResult` and also accept a `query` as a prop as well: + + ```jsx + // Example using the + const SearchPage = () => { + const query = { + term: 'example', + }; + + return ( + { + switch (type) { + case 'custom': + return ( + } + result={document} + highlight={highlight} + rank={rank} + /> + ); + default: + return ( + + ); + } + }} + /> + ); + }; + ``` + + ```jsx + // Example using the for creating a component that search and group software catalog results + import React, { useState, useCallback } from 'react'; + + import { MenuItem } from '@material-ui/core'; + + import { JsonValue } from '@backstage/types'; + import { CatalogIcon } from '@backstage/core-components'; + import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; + import { + SearchResultGroup, + SearchResultGroupTextFilterField, + SearchResultGroupSelectFilterField, + } from @backstage/plugin-search-react; + import { SearchQuery } from '@backstage/plugin-search-common'; + + const CatalogResultsGroup = () => { + const [query, setQuery] = useState>({ + types: ['software-catalog'], + }); + + const filterOptions = [ + { + label: 'Lifecycle', + value: 'lifecycle', + }, + { + label: 'Owner', + value: 'owner', + }, + ]; + + const handleFilterAdd = useCallback( + (key: string) => () => { + setQuery(prevQuery => { + const { filters: prevFilters, ...rest } = prevQuery; + const newFilters = { ...prevFilters, [key]: undefined }; + return { ...rest, filters: newFilters }; + }); + }, + [], + ); + + const handleFilterChange = useCallback( + (key: string) => (value: JsonValue) => { + setQuery(prevQuery => { + const { filters: prevFilters, ...rest } = prevQuery; + const newFilters = { ...prevFilters, [key]: value }; + return { ...rest, filters: newFilters }; + }); + }, + [], + ); + + const handleFilterDelete = useCallback( + (key: string) => () => { + setQuery(prevQuery => { + const { filters: prevFilters, ...rest } = prevQuery; + const newFilters = { ...prevFilters }; + delete newFilters[key]; + return { ...rest, filters: newFilters }; + }); + }, + [], + ); + + return ( + } + title="Software Catalog" + link="See all software catalog results" + filterOptions={filterOptions} + renderFilterOption={({ label, value }) => ( + + {label} + + )} + renderFilterField={(key: string) => { + switch (key) { + case 'lifecycle': + return ( + + Production + Experimental + + ); + case 'owner': + return ( + + ); + default: + return null; + } + } + renderResultItem={({ document, highlight, rank }) => ( + + )} + /> + ); + }; + ``` + +- 18f60427f2: Provides search autocomplete functionality through a `SearchAutocomplete` component. + A `SearchAutocompleteDefaultOption` can also be used to render options with icons, primary texts, and secondary texts. + Example: + + ```jsx + import React, { ChangeEvent, useState, useCallback } from 'react'; + import useAsync from 'react-use/lib/useAsync'; + + import { Grid, Paper } from '@material-ui/core'; + + import { Page, Content } from '@backstage/core-components'; + import { SearchAutocomplete, SearchAutocompleteDefaultOption} from '@backstage/plugin-search-react'; + + const OptionsIcon = () => + + const SearchPage = () => { + const [inputValue, setInputValue] = useState(''); + + const options = useAsync(async () => { + // Gets and returns autocomplete options + }, [inputValue]) + + const useCallback((_event: ChangeEvent<{}>, newInputValue: string) => { + setInputValue(newInputValue); + }, [setInputValue]) + + return ( + + + + + + option.title} + renderOption={option => ( + } + primaryText={option.title} + secondaryText={option.text} + /> + )} + /> + + + + {'/* Filters and results are omitted */'} + + + ); + }; + ``` + +- ca8d5a6eae: We noticed a repeated check for the existence of a parent context before creating a child search context in more the one component such as Search Modal and Search Bar and to remove code duplication we extract the conditional to the context provider, now you can use it passing an `inheritParentContextIfAvailable` prop to the `SearchContextProvider`. + + Note: This added property does not create a local context if there is a parent context and in this case, you cannot use it together with `initialState`, it will result in a type error because the parent context is already initialized. + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- d3737da337: Reset page cursor on search filter change +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-tech-insights@0.3.0 + +### Minor Changes + +- f7e99ac1d8: Added the possibility to display check results of different types on a single scorecard. + + - **BREAKING** Removed the `getScorecardsDefinition` method from the `TechInsightsApi` interface. Added the `getCheckResultRenderers` method that returns rendering components for given types. + - **BREAKING** The `CheckResultRenderer` type now exposes the `component` factory method that creates a React component used to display a result of a provided check result. + - The `TechInsightsClient` constructor accepts now the optional `renderers` parameter that can be used to inject a custom renderer. + - **BREAKING** The `title` parameter in the `EntityTechInsightsScorecardContent` and `EntityTechInsightsScorecardCard` components is now mandatory. + - The `jsonRulesEngineCheckResultRenderer` used to render `json-rules-engine` check results is exported. + - The `BooleanCheck` component that can be used to render other check results types is also exported. + + If you were overriding the `getScorecardsDefinition` method to adjust the rendering of check results, you should now provide a custom renderer using `renderers` parameter in the `TechInsightsClient` class. + + See the [README](https://github.com/backstage/backstage/tree/master/plugins/tech-insights/README.md) for more details. + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-techdocs-backend@1.3.0 + +### Minor Changes + +- aa524a5377: Add `projectId` config option to GCP Cloud Storage techdocs publisher. This will allow users to override the project ID, instead of implicitly using the same one as found in a credentials bundle. + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-techdocs-node@1.4.0 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-catalog-common@1.0.6 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-techdocs-node@1.4.0 + +### Minor Changes + +- aa524a5377: Add `projectId` config option to GCP Cloud Storage techdocs publisher. This will allow users to override the project ID, instead of implicitly using the same one as found in a credentials bundle. + +### Patch Changes + +- 33ac0c0546: Fix Techdocs S3 and GCS publisher to include bucketRootPath in requests +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-user-settings-backend@0.1.0 + +### Minor Changes + +- 108cdc3912: Added new plugin `@backstage/plugin-user-settings-backend` to store user related + settings in the database. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/app-defaults@1.0.6 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d9e39544be: Add missing peer dependencies +- Updated dependencies + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-permission-react@0.4.5 + +## @backstage/backend-app-api@0.2.1 + +### Patch Changes + +- 2c57c0c499: Made `ApiRef.defaultFactory` internal. +- 854ba37357: Updated to support new `ServiceFactory` formats. +- af6bb42c68: Updated `ServiceRegistry` to not initialize factories more than once. +- 409ed984e8: Updated service implementations and backend wiring to support scoped service. +- de3347ca74: Updated usages of `ServiceFactory`. +- 1f384c5644: Improved error messaging when failing to instantiate services. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/backend-tasks@0.3.5 + - @backstage/errors@1.1.1 + +## @backstage/backend-common@0.15.1 + +### Patch Changes + +- 399286d7dd: Workaround support for `swc` instead of `sucrase` + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` + +- c3c90280be: Added a keep-alive refresh loop to the `DatabaseManager`, keeping the connection + pool up. + +- c3c90280be: The options part of `DatabaseManager.fromConfig` now accepts an optional logger + field. + +- 3d4f5daadf: Remove use of deprecated trimLeft/trimRight + +- bf3cc134eb: Implemented KubernetesContainerRunner: a ContainerRunner implementation that leverages Jobs on a kubernetes cluster + + ```ts + const kubeConfig = new KubeConfig(); + kubeConfig.loadFromDefault(); + + const options: KubernetesContainerRunnerOptions = { + kubeConfig, + // namespace where Jobs will be created + namespace: 'default', + // Jobs name will be prefixed with this name + name: 'my-runner', + // An existing Kubernetes volume that will be used + // as base for mounts + mountBase: { + volumeName: 'workdir', + // Every mount must start with the base path + // see example below + basePath: '/workdir', + }, + // Define a Pod template for the Jobs. It has to include + // a volume definition named as the mountBase volumeName + podTemplate: { + spec: { + containers: [], + volumes: [ + { + name: 'workdir', + persistentVolumeClaim: { + claimName: 'workdir-claim', + }, + }, + ], + }, + }, + }; + const containerRunner = new KubernetesContainerRunner(options); + + const runOptions: RunContainerOptions = { + imageName: 'golang:1.17', + args: ['echo', 'hello world'], + mountDirs: { + '/workdir/app': '/app', + }, + }; + containerRunner.runContainer(runOptions); + ``` + +- e3b1993788: Added port ranges in allowed hosts: + + ```yaml + reading: + allow: + - host: *.examples.org:900-1000 + ``` + +- 2f52e74b49: Got rid of usages of the uppercase String type + +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- 709f468330: The `branch` command has been added to the `isomorphic-git` wrapper. + +- 0c780278e0: Fix for entries being skipped or incomplete when reading large zip archives. + +- 96689fbdcb: Workaround for a rare race condition in tests. + +- Updated dependencies + - @backstage/integration@1.3.1 + - @backstage/config-loader@1.1.4 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/backend-defaults@0.1.1 + +### Patch Changes + +- 854ba37357: Updated to support new `ServiceFactory` formats. +- de3347ca74: Updated usages of `ServiceFactory`. +- Updated dependencies + - @backstage/backend-app-api@0.2.1 + - @backstage/backend-plugin-api@0.1.2 + +## @backstage/backend-plugin-api@0.1.2 + +### Patch Changes + +- 2c57c0c499: Made `ApiRef.defaultFactory` internal. +- 91eed37a39: Updated `createBackendPlugin` and `createBackendModule` to properly forward lack of options. +- 409ed984e8: Service are now scoped to either `'plugin'` or `'root'` scope. Service factories have been updated to provide dependency instances directly rather than factory functions. +- eef91a2558: Simplified the `ServiceFactory` type and removed `AnyServiceFactory`. +- 854ba37357: The `createServiceFactory` method has been updated to return a higher-order factory that can accept options. +- 68513f169a: When defining a new `ServiceRef` you can now also include a `defaultFactory`, which will be used to construct instances of the service in case there is no explicit factory defined. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/backend-tasks@0.3.5 + +### Patch Changes + +- 243533ecdc: Added support to mysql on some raw queries +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/backend-test-utils@0.1.28 + +### Patch Changes + +- 854ba37357: Updated to support new `ServiceFactory` formats. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- de3347ca74: Updated usages of `ServiceFactory`. +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/backend-app-api@0.2.1 + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + +## @backstage/catalog-model@1.1.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/cli-common@0.1.10 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + +## @backstage/codemods@0.1.39 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## @backstage/config@1.0.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + +## @backstage/config-loader@1.1.4 + +### Patch Changes + +- 5ecca7e44b: No longer log when reloading remote config. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/core-components@0.11.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- d3737da337: Export `HeaderActionMenu` and expose default `Table` icons via `Table.icons` +- 7d47def9c4: Removed dependency on `@types/jest`. +- b29c44d895: Adds code to generate ids for headers parsed through the MarkdownContent component. +- 37e8c5e128: The `RoutedTabs` component has been updated to be compatible with React Router v6 stable. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 13f23f6510: Allow custom images in the empty state component +- 023d14c52b: Fix overflow bug on MicDrop image for 404 page by moving the image and making it relative rather than absolute +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/core-plugin-api@1.0.6 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 744fea158b: Added `getSystemIcons()` function to the `AppContext` available through `useApp` that will pull a list of all the icons that have been registered in the App. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/config@1.0.2 + +## @backstage/create-app@0.4.31 + +### Patch Changes + +- 6ff94d60d5: Removed usage of the deprecated `diff` command in the root `package.json`. + + To make this change in an existing app, make the following change in the root `package.json`: + + ```diff + - "diff": "lerna run diff --", + ``` + +- c1f1a4c760: The Backstage packages and plugins have all been updated to support React Router v6 stable. The `create-app` template has not been migrated yet, but if you want to migrate your own app or plugins, check out the [migration guide](https://backstage.io/docs/tutorials/react-router-stable-migration). + +- e83de28e36: Fix typo in the documentation + +- 7d47def9c4: Removed dependency on `@types/jest`. + +- 208d6780c9: The `packages/backend/Dockerfile` received a couple of updates, it now looks as follows: + + ```Dockerfile + FROM node:16-bullseye-slim + + # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, + # in which case you should also move better-sqlite3 to "devDependencies" in package.json. + RUN apt-get update && \ + apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ + rm -rf /var/lib/apt/lists/* && \ + yarn config set python /usr/bin/python3 + + # From here on we use the least-privileged `node` user to run the backend. + USER node + WORKDIR /app + + # This switches many Node.js dependencies to production mode. + ENV NODE_ENV production + + # Copy repo skeleton first, to avoid unnecessary docker cache invalidation. + # The skeleton contains the package.json of each package in the monorepo, + # and along with yarn.lock and the root package.json, that's enough to run yarn install. + COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ + RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz + + RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" + + # Then copy the rest of the backend bundle, along with any other files we might want. + COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ + RUN tar xzf bundle.tar.gz && rm bundle.tar.gz + + CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] + ``` + + The two notable changes are that a `USER node` instruction has been added and the ordering of instructions has been changed accordingly. This means that the app will now be running using the least-privileged `node` user. In order for this to work we now need to make sure that all app files are owned by the `node` user, which we do by adding the `--chown=node:node` option to the `COPY` instructions. + + The second change is the addition of `ENV NODE_ENV production`, which ensured that all Node.js modules run in production mode. If you apply this change to an existing app, note that one of the more significant changes is that this switches the log formatting to use the default production format, JSON. Rather than your log lines looking like this: + + ```log + 2022-08-10T11:36:05.478Z catalog info Performing database migration type=plugin + ``` + + They will now look like this: + + ```log + {"level":"info","message":"Performing database migration","plugin":"catalog","service":"backstage","type":"plugin"} + ``` + + If you wish to keep the existing format, you can override this change by applying the following change to `packages/backend/src/index.ts`: + + ```diff + getRootLogger, + + setRootLogger, + + createRootLogger, + + coloredFormat, + useHotMemoize, + ... + ServerTokenManager, + } from '@backstage/backend-common'; + + ... + + async function main() { + + setRootLogger(createRootLogger({ format: coloredFormat })); + + + const config = await loadBackendConfig({ + ``` + +- 49416194e8: Adds `IdentityApi` configuration to `create-app` scaffolding templates. + + To migrate to the new `IdentityApi`, edit the `packages/backend/src/index.ts` adding the following import: + + ```typescript + import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; + ``` + + Use the factory function to create an `IdentityApi` in the `makeCreateEnv` function and return it from the + function as follows: + + ```typescript + function makeCreateEnv(config: Config) { + ... + const identity = DefaultIdentityClient.create({ + discovery, + }); + ... + + return { + ..., + identity + } + } + ``` + + Backend plugins can be upgraded to work with this new `IdentityApi`. + + Add `identity` to the `RouterOptions` type. + + ```typescript + export interface RouterOptions { + ... + identity: IdentityApi; + } + ``` + + Then you can use the `IdentityApi` from the plugin. + + ```typescript + export async function createRouter( + options: RouterOptions, + ): Promise { + const { identity } = options; + + router.get('/user', async (req, res) => { + const user = await identity.getIdentity({ request: req }); + ... + ``` + +- 8d886dd33e: Added `yarn new` as one of the scripts installed by default, which calls `backstage-cli new`. This script replaces `create-plugin`, which you can now remove if you want to. It is kept in the `create-app` template for backwards compatibility. + + The `remove-plugin` command has been removed, as it has been removed from the Backstage CLI. + + To apply these changes to an existing app, make the following change to the root `package.json`: + + ```diff + - "remove-plugin": "backstage-cli remove-plugin" + + "new": "backstage-cli new --scope internal" + ``` + +- c3c90280be: The options part of `DatabaseManager.fromConfig` now accepts an optional logger + field. You may want to supply that logger in your backend initialization code to + ensure that you can get relevant logging data when things happen related to the + connection pool. + + In `packages/backend/src/index.ts`: + + ```diff + function makeCreateEnv(config: Config) { + const root = getRootLogger(); + ... + - const databaseManager = DatabaseManager.fromConfig(config); + + const databaseManager = DatabaseManager.fromConfig(config, { logger: root }); + ``` + +- a578558180: Updated the root `package.json` to use the new `backstage-cli repo clean` command. + + To apply this change to an existing project, make the following change to the root `package.json`: + + ```diff + - "clean": "backstage-cli clean && lerna run clean", + + "clean": "backstage-cli repo clean", + ``` + +- c0a08fd08c: Added `EntityLinksCard` to the system `EntityPage`. + + For an existing installation where you want to display the links card for entity pages of kind `system` you should make the following adjustment to `packages/app/src/components/catalog/EntityPage.tsx` + + ```diff + const systemPage = ( + ... + + + + + + + + + + - + + + + + ... + ); + ``` + +- Updated dependencies + - @backstage/cli-common@0.1.10 + +## @backstage/dev-utils@1.0.6 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 329ed2b9c7: Fixed routing when using React Router v6 stable. +- 4c5e85ddf3: Removed the dependency and setup of `react-hot-loader`, since the `@backstage/cli` now uses `swc` with `React Refresh` instead. +- Updated dependencies + - @backstage/app-defaults@1.0.6 + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/integration-react@1.1.4 + +## @backstage/errors@1.1.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + +## @backstage/integration@1.3.1 + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 42918e085c: Fixed bug in the `bitbucketServer` integration where token did not take precedence over supplied username and password which is described in the documentation. +- f76f22c649: Improved caching around github app tokens. + Tokens are now cached for 50 minutes, not 10. + Calls to get app installations are also included in this cache. + If you have more than one github app configured, consider adding `allowedInstallationOwners` to your apps configuration to gain the most benefit from these performance changes. +- Updated dependencies + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/integration-react@1.1.4 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/integration@1.3.1 + - @backstage/config@1.0.2 + +## @backstage/release-manifests@0.0.6 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +## @techdocs/cli@1.2.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-techdocs-node@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + +## @backstage/plugin-adr@0.2.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- b489de83b1: Fix parsing of ADR location which includes a trailing slash +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-adr-common@0.2.1 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-adr-backend@0.2.1 + +### Patch Changes + +- b489de83b1: Continue processing subsequent entities when an error occurs in collator +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-adr-common@0.2.1 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-adr-common@0.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-airbrake@0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/dev-utils@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-airbrake-backend@0.2.9 + +### Patch Changes + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-allure@0.1.25 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-analytics-module-ga@0.1.20 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + +## @backstage/plugin-apache-airflow@0.2.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 2f52e74b49: Got rid of usages of the uppercase String type +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-api-docs@0.8.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-catalog@1.5.1 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-apollo-explorer@0.1.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-app-backend@0.3.36 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup + +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config-loader@1.1.4 + - @backstage/config@1.0.2 + +## @backstage/plugin-auth-node@0.2.5 + +### Patch Changes + +- 9212439d15: Minor update to tests +- 2cbd533426: `IdentityClient` is now deprecated. Please migrate to `IdentityApi` and `DefaultIdentityClient` instead. The authenticate function on `DefaultIdentityClient` is also deprecated. Please use `getIdentity` instead. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-azure-devops-backend@0.3.15 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- cb1cfc018b: `createRouter` now requires an additional reader: `UrlReader` argument + + ```diff + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return createRouter({ + logger: env.logger, + config: env.config, + + reader: env.reader, + }); + } + ``` + + Remember to check if you have already provided these settings previously. + + #### [Azure DevOps] + + ```yaml + # app-config.yaml + azureDevOps: + host: dev.azure.com + token: my-token + organization: my-company + ``` + + #### [Azure Integrations] + + ```yaml + # app-config.yaml + integrations: + azure: + - host: dev.azure.com + token: ${AZURE_TOKEN} + ``` + +- ef9ab322de: Minor API signatures cleanup + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-azure-devops-common@0.3.0 + - @backstage/config@1.0.2 + +## @backstage/plugin-badges@0.2.33 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-badges-backend@0.1.30 + +### Patch Changes + +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-bazaar@0.1.24 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 1dd12349d1: Fixed broken routing by removing the wrapping `Router` from the `RoutedTabs` children. +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-catalog@1.5.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-bazaar-backend@0.1.20 + +### Patch Changes + +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/backend-test-utils@0.1.28 + +## @backstage/plugin-bitbucket-cloud-common@0.1.3 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/integration@1.3.1 + +## @backstage/plugin-bitrise@0.1.36 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-catalog@1.5.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-catalog-common@1.0.6 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-catalog-backend-module-aws@0.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-azure@0.1.7 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.3 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.3 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.3 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + - @backstage/plugin-bitbucket-cloud-common@0.1.3 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.1 + +### Patch Changes + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.4 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-github@0.1.7 + +### Patch Changes + +- 3c4a388537: New experimental alpha exports for use with the upcoming backend system. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 3a62594a11: Add support for including (or excluding) Github repositories by topic +- 287a64bf97: Added the ability to configure the host for the `GitHubEntityProvider` to use against GitHub Enterprise +- 91e2abbd46: Remove the duplicated `scheduleFn` initialization in `GitHubEntityProvider`. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.7 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.3 + +### Patch Changes + +- 7fa54c3c77: Added ae-dir LDAP vendor to correctly extract DN. +- c54fcea9af: Now the `searchStream` method in LDAP client awaits the callbacks +- Updated dependencies + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.4.2 + +### Patch Changes + +- c1d32d2b76: Fixed a bug in the `MicrosoftGraphEntityProvider` that ignored the `userExpand` and `groupExpand` configuration parameters +- d80aab31ae: Added $select attribute to user query +- a246d5a9b8: Read `queryMode` from the `microsoftGraphOrg` config +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 4c82b955fc: Fix typo +- Updated dependencies + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-catalog-common@1.0.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-catalog-graph@0.2.21 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-catalog-graphql@0.3.13 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-catalog-import@0.8.12 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + +## @backstage/plugin-catalog-react@1.1.4 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f6033d1121: humanizeEntityRef function can now be forced to include default namespace +- c86741a052: Support showing counts in option labels of the `EntityTagPicker`. You can enable this by adding the `showCounts` property +- 7d47def9c4: Removed dependency on `@types/jest`. +- a6d551fad9: Properly handle free-text entity filtering in the case of empty tag arrays +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-catalog-common@1.0.6 + +## @backstage/plugin-cicd-statistics@0.1.11 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.5 + +### Patch Changes + +- 71b7464a7c: Fixing typo in the Readme file: `'@backstage plugin-cicd-statistics-module-gitlab';` -> `'@backstage/plugin-cicd-statistics-module-gitlab';` +- a98f5d4c21: add the ability to add CICD default options to createGitlabApi constructor +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + - @backstage/plugin-cicd-statistics@0.1.11 + +## @backstage/plugin-circleci@0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-cloudbuild@0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-code-climate@0.1.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-code-coverage@0.2.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-code-coverage-backend@0.2.2 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-codescene@0.1.4 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-config-schema@0.1.32 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-cost-insights@0.11.31 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 9318bf15d4: The `CostInsightsHeader`component now uses group names if available +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-explore@0.3.40 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/plugin-explore-react@0.0.21 + +## @backstage/plugin-explore-react@0.0.21 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-firehydrant@0.1.26 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + +## @backstage/plugin-fossa@0.2.41 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-gcalendar@0.3.5 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- a69ac5b63a: Upgrade `react-query:3` to `@tanstack/react-query:4` +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/errors@1.1.1 + +## @backstage/plugin-gcp-projects@0.3.28 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-git-release-manager@0.3.22 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/integration@1.3.1 + +## @backstage/plugin-github-actions@0.5.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 1bdb0bf24b: Modify RecentWorkflowRunsCard use constructed route instead of hardcoded route. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-github-deployments@0.1.40 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + +## @backstage/plugin-github-issues@0.1.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-github-pull-requests-board@0.1.3 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 2665ee4ed4: Clarified GitHub app permissions required by plugin +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 723113296b: Add optional `pullRequestLimit` prop to `EntityTeamPullRequestsCard` and `EntityTeamPullRequestsContent` to limit the number of PRs shown per repository. Excluding this prop will default the number of pull requests shown to 10 per repository (the existing functionality). +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-gitops-profiles@0.3.27 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-gocd@0.1.15 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-graphiql@0.2.41 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-graphql-backend@0.1.26 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup + +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/plugin-catalog-graphql@0.3.13 + +## @backstage/plugin-home@0.4.25 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/plugin-stack-overflow@0.1.5 + +## @backstage/plugin-ilert@0.1.35 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-jenkins@0.7.8 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/plugin-jenkins-common@0.1.8 + +## @backstage/plugin-jenkins-backend@0.1.26 + +### Patch Changes + +- 618c06f838: Add example on how to pass through permissions +- a966ed8385: Unwrap error message when getting projects +- 47952621dc: Extend configuration so that extra headers can be sent to jenkins instance +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- 69ec1cf39f: Updated dependency `jenkins` to `^1.0.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- e55d855a6b: Jenkins plugin works again +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-jenkins-common@0.1.8 + +## @backstage/plugin-jenkins-common@0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-catalog-common@1.0.6 + +## @backstage/plugin-kafka@0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f6be17460d: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-kafka-backend@0.2.29 + +### Patch Changes + +- f6be17460d: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-kubernetes@0.7.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 0768d6dece: add new kubernetes backend endpoints to kubernetes backend client +- 19a27929fb: Reset error state on success +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/plugin-kubernetes-common@0.4.2 + +## @backstage/plugin-kubernetes-backend@0.7.2 + +### Patch Changes + +- 8902c2e39d: chore: Exporting KubernetesClientProvider and everything in kubernetes-auth-translator as requested in issue #10457 + +- a57d29d572: Adds skipMetricsLookup to the kubernetes-backend schema + +- 0768d6dece: add new kubernetes backend endpoints to kubernetes backend client + +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-kubernetes-common@0.4.2 + +## @backstage/plugin-kubernetes-common@0.4.2 + +### Patch Changes + +- 0768d6dece: add new kubernetes backend endpoints to kubernetes backend client +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-lighthouse@0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f6be17460d: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-newrelic@0.3.27 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-newrelic-dashboard@0.2.2 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-org@0.5.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- ab6650ede9: Added an edit button to the `UserProfileCard` that is enabled when the `backstage.io/edit-url` is present, this matches how the `GroupProfileCard` works +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-pagerduty@0.5.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-periskop@0.1.7 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-periskop-backend@0.1.7 + +### Patch Changes + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-permission-backend@0.5.11 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 2cbd533426: Uptake the `IdentityApi` change to use `getIdentity` instead of `authenticate` for retrieving the logged in users identity. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/plugin-permission-common@0.6.4 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-permission-node@0.6.5 + +### Patch Changes + +- 9212439d15: Minor update to tests +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/plugin-permission-react@0.4.5 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. + +- 7d47def9c4: Removed dependency on `@types/jest`. + +- 817f3196f6: **DEPRECATION**: The `PermissionedRoute` component has been deprecated in favor of the new `RequirePermission` component. This is because the usage pattern of `PermissionedRoute` is not compatible with React Router v6 stable. + + Embed the type from `react-router` instead of exporting it directly. + +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/plugin-proxy-backend@0.2.30 + +### Patch Changes + +- f6be17460d: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-rollbar@0.4.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-rollbar-backend@0.1.33 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.11 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/integration@1.3.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.4 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/integration@1.3.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.9 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/config@1.0.2 + +## @backstage/plugin-search@1.0.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 18f60427f2: Use the new `inheritParentContextIfAvailable` search context property in `SearchModal` instead of manually checking if a parent context exists, this conditional statement was previously duplicated in more than one component like in `SearchBar` as well and is now only done in ` SearchContextProvider`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-search-backend@1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/plugin-search-backend-node@1.0.2 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-search-backend-module-elasticsearch@1.0.2 + +### Patch Changes + +- 1bea488bd4: Updated dependency `aws-os-connection` to `^0.2.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/plugin-search-backend-node@1.0.2 + - @backstage/config@1.0.2 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-search-backend-node@1.0.2 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- a1124adf5e: Fixed bug in LunrSearchEngine where stopwords in title is causing in-doc search not working +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-search-common@1.0.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + +## @backstage/plugin-sentry@0.4.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-shortcuts@0.3.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 8a57ffc0fa: Ensure that a stable observable is used in the shortcuts API +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-sonarqube@0.4.1 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-sonarqube-backend@0.1.1 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-splunk-on-call@0.3.33 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + +## @backstage/plugin-stack-overflow@0.1.5 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- b8190af939: Create a front end API. +- a720c44070: Support showing HTML entity references from the API response before rendering the question title to the list component. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-home@0.4.25 + - @backstage/config@1.0.2 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-stack-overflow-backend@0.1.5 + +### Patch Changes + +- 79040f73f7: Now requests all questions available using pagination. Default max page is set to 100, with a configurable `maxPage` option on the collator. +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/config@1.0.2 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-tech-insights-backend@0.5.2 + +### Patch Changes + +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- 12c6a07c2a: Changed the description of the `techdocsAnnotationFactName` fact. +- 2e0689e536: Support for timeout in FactRetrieverRegistrationOptions +- 9e8e9f5243: Modify Tech insight initialization to expose FactRetrieverEngine. Enables users to trigger fact retrieval manually or reschedule retrievers on runtime. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-tech-insights-node@0.3.4 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.20 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-tech-insights-node@0.3.4 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-tech-insights-node@0.3.4 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- 2e0689e536: Support for timeout in FactRetrieverRegistrationOptions +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + +## @backstage/plugin-tech-radar@0.5.16 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @backstage/plugin-techdocs@1.3.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 817f3196f6: Updated the `TechDocsReaderPage` to be compatible with React Router v6 stable. +- 7a95c705fa: Fixed a bug where addons wouldn't render on sub pages when using React Route v6 stable. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ca8d5a6eae: Use the new `SearchAutocomplete` component in the `TechDocsSearch` component to maintain consistency across search experiences and avoid code duplication. +- 829f14a9b0: Always update the title and sub-title when the location changes on a `TechDocs` reader page. +- e97d616f08: Fixed a bug where scrolling for anchors where the id starts with number didn't work for the current page. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-search-common@1.0.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.4 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog@1.5.1 + - @backstage/plugin-search-react@1.1.0 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/plugin-techdocs@1.3.2 + - @backstage/integration-react@1.1.4 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.4 + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/integration@1.3.1 + - @backstage/integration-react@1.1.4 + +## @backstage/plugin-techdocs-react@1.0.4 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + +## @backstage/plugin-todo@0.2.11 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-todo-backend@0.1.33 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @backstage/plugin-user-settings@0.4.8 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 8448b53dd6: Added a `UserSettingsStorage` implementation of the `StorageApi` for use as + drop-in replacement for the `WebStorage`, in conjunction with the newly created + `@backstage/plugin-user-settings-backend`. +- Updated dependencies + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/errors@1.1.1 + +## @backstage/plugin-vault@0.1.3 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + +## @backstage/plugin-vault-backend@0.2.2 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages + +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. + +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/backend-test-utils@0.1.28 + +## @backstage/plugin-xcmetrics@0.2.29 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/errors@1.1.1 + +## example-app@0.2.75 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/app-defaults@1.0.6 + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-airbrake@0.3.9 + - @backstage/plugin-api-docs@0.8.9 + - @backstage/plugin-azure-devops@0.2.0 + - @backstage/plugin-badges@0.2.33 + - @backstage/plugin-catalog-graph@0.2.21 + - @backstage/plugin-catalog-import@0.8.12 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-circleci@0.3.9 + - @backstage/plugin-cloudbuild@0.3.9 + - @backstage/plugin-code-coverage@0.2.2 + - @backstage/plugin-cost-insights@0.11.31 + - @backstage/plugin-explore@0.3.40 + - @backstage/plugin-gcp-projects@0.3.28 + - @backstage/plugin-github-actions@0.5.9 + - @backstage/plugin-graphiql@0.2.41 + - @backstage/plugin-home@0.4.25 + - @backstage/plugin-jenkins@0.7.8 + - @backstage/plugin-kafka@0.3.9 + - @backstage/plugin-kubernetes@0.7.2 + - @backstage/plugin-lighthouse@0.3.9 + - @backstage/plugin-org@0.5.9 + - @backstage/plugin-pagerduty@0.5.2 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/plugin-rollbar@0.4.9 + - @backstage/plugin-scaffolder@1.6.0 + - @backstage/plugin-search-react@1.1.0 + - @backstage/plugin-search@1.0.2 + - @backstage/plugin-sentry@0.4.2 + - @backstage/plugin-shortcuts@0.3.1 + - @backstage/plugin-tech-insights@0.3.0 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/plugin-techdocs@1.3.2 + - @backstage/plugin-todo@0.2.11 + - @backstage/plugin-user-settings@0.4.8 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.4 + - @backstage/plugin-newrelic-dashboard@0.2.2 + - @backstage/plugin-dynatrace@0.2.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-apache-airflow@0.2.2 + - @backstage/plugin-gcalendar@0.3.5 + - @backstage/plugin-gocd@0.1.15 + - @backstage/plugin-newrelic@0.3.27 + - @backstage/plugin-stack-overflow@0.1.5 + - @backstage/plugin-tech-radar@0.5.16 + - @backstage/plugin-playlist@0.1.0 + - @backstage/plugin-catalog-common@1.0.6 + - @internal/plugin-catalog-customized@0.0.2 + - @backstage/plugin-search-common@1.0.1 + +## example-backend@0.2.75 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/plugin-kubernetes-backend@0.7.2 + - @backstage/plugin-kafka-backend@0.2.29 + - @backstage/plugin-proxy-backend@0.2.30 + - @backstage/plugin-auth-backend@0.16.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/plugin-azure-devops-backend@0.3.15 + - @backstage/plugin-search-backend-node@1.0.2 + - @backstage/plugin-tech-insights-node@0.3.4 + - @backstage/backend-tasks@0.3.5 + - @backstage/plugin-techdocs-backend@1.3.0 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.4 + - @backstage/plugin-search-backend-module-pg@0.4.0 + - @backstage/plugin-jenkins-backend@0.1.26 + - @backstage/plugin-playlist-backend@0.1.0 + - @backstage/plugin-app-backend@0.3.36 + - @backstage/plugin-graphql-backend@0.1.26 + - @backstage/plugin-rollbar-backend@0.1.33 + - @backstage/plugin-code-coverage-backend@0.2.2 + - @backstage/plugin-permission-backend@0.5.11 + - @backstage/plugin-todo-backend@0.1.33 + - @backstage/plugin-search-backend-module-elasticsearch@1.0.2 + - @backstage/plugin-tech-insights-backend@0.5.2 + - @backstage/plugin-badges-backend@0.1.30 + - example-app@0.2.75 + - @backstage/plugin-search-backend@1.0.2 + - @backstage/plugin-search-common@1.0.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.20 + +## example-backend-next@0.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-defaults@0.1.1 + +## techdocs-cli-embedded-app@0.2.74 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/app-defaults@1.0.6 + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog@1.5.1 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/plugin-techdocs@1.3.2 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/integration-react@1.1.4 + +## @internal/plugin-catalog-customized@0.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-catalog@1.5.1 + +## @internal/plugin-todo-list@1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + +## @internal/plugin-todo-list-backend@1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## @internal/plugin-todo-list-common@1.0.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 diff --git a/docs/releases/v1.6.0.md b/docs/releases/v1.6.0.md new file mode 100644 index 0000000000..a1ece6bf94 --- /dev/null +++ b/docs/releases/v1.6.0.md @@ -0,0 +1,60 @@ +--- +id: v1.6.0 +title: v1.6.0 +description: Backstage Release v1.6.0 +--- + +These are the release notes for the v1.6.0 release of [Backstage](https://backstage.io/). + +A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for the hard work in getting this release developed and done. + +## Highlights + +### Moved to `swc` for transpilation + +We’ve replaced `sucrase` with [`swc`](https://swc.rs/) for transpilation in the Backstage CLI. While `swc` has slightly slower transpilation times, it’s a library backed by a larger community, and allows us to take advantage of [React Refresh](https://www.npmjs.com/package/react-refresh) out of the box. There’s a few things that could possibly break installations of Backstage and compilation, you can read more about it in the [changelog](https://github.com/backstage/backstage/blob/515aadf8840591860e4bbdcc7d99cef8f9d7ac3c/docs/releases/v1.6.0-changelog.md#patch-changes-1) + +### React Router Stable Compatibility + +Backstage has for a long time been using React Router version `6.0.0-beta.0`. We adopted this unstable version because v6 had some new features that fit really well with Backstage, particularly relative routing. Because we jumped on this early and unstable version, we knew that we would at some point need a breaking migration to the stable version of React Router v6, which is the point we're at now! + +The migration is controlled by each app, meaning this release will not force you to migrate straight away, you can do so at your own pace. Check out the [migration guide](https://backstage.io/docs/tutorials/react-router-stable-migration) for all you need to know! + +### Yarn 3 Support + +It is now possible to migrate Backstage projects to use Yarn 3. See the [migration guide](https://backstage.io/docs/tutorials/yarn-migration) for more information. Migrating to Yarn 3 is optional, and Backstage projects created with `@backstage/create-app` will still use classic Yarn by default. + +### New plugin: `@backstage/plugin-user-settings-backend` + +The `user-settings` plugin now has an associated backend. This allows for the persistence of settings in your database, essentially in the form of a basic per-user key-value JSON store. + +As this backend was added, `user-settings` also gained a `UserSettingsStore` class that implements the `storageApiRef` Utility API. If you install the backend as well as this frontend API, your starred entities and other storage-API-based features will no longer just be persisted in your browser’s local storage, but centrally so that all your devices can leverage them. + +Contributed by [@dschwank](https://github.com/dschwank) in [#13570](https://github.com/backstage/backstage/pull/13570) + +### New plugin: `@backstage/plugin-playlist` + +This plugin can be used to create custom collections of Entities that can be shared throughout Backstage or for private usage. + +Contributed by [@kuangp](https://github.com/kuangp) in [#12870](https://github.com/backstage/backstage/pull/12870) + +## Security Fixes + +Be sure to upgrade to the latest version of `@backstage/plugin-scaffolder-backend`, as it contains an explicit bump of a transitive dependency where a vulnerability was discovered. If you subscribe to CVE notifications you will already have received this update. + +## Upgrade path + +We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated). + +## Links and References + +Below you can find a list of links and references to help you learn about and start using this new release. + +- [Backstage official website](https://backstage.io/), [documentation](https://backstage.io/docs/), and [getting started guide](https://backstage.io/docs/getting-started/) +- [GitHub repository](https://github.com/backstage/backstage) +- Backstage's [versioning and support policy](https://backstage.io/docs/overview/versioning-policy) +- [Community Discord](https://discord.gg/bFESRKVt) for discussions and support +- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.6.0-changelog.md) +- Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap) and [Plugins](https://backstage.io/plugins) + +Sign up for our [newsletter](https://mailchi.mp/spotify/backstage-community) if you want to be informed about what is happening in the world of Backstage. diff --git a/microsite/data/plugins/playlist.yaml b/microsite/data/plugins/playlist.yaml new file mode 100644 index 0000000000..3d69103d69 --- /dev/null +++ b/microsite/data/plugins/playlist.yaml @@ -0,0 +1,9 @@ +--- +title: Playlist +author: Phil Kuang +authorUrl: https://github.com/kuangp +category: Discovery +description: Create, share, and follow custom collections of entities available in the Backstage catalog. +documentation: https://github.com/backstage/backstage/tree/master/plugins/playlist +iconUrl: img/playlist-logo.png +npmPackageName: '@backstage/plugin-playlist' diff --git a/microsite/pages/en/live.js b/microsite/pages/en/live.js index 2483b2f8bf..43b5a0eb67 100644 --- a/microsite/pages/en/live.js +++ b/microsite/pages/en/live.js @@ -34,7 +34,7 @@ const Background = props => { diff --git a/microsite/sidebars.json b/microsite/sidebars.json index ba35f8b76b..f00b7b2ee1 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -1,6 +1,7 @@ { "releases": { "Release Notes": [ + "releases/v1.6.0", "releases/v1.5.0", "releases/v1.4.0", "releases/v1.3.0", @@ -96,6 +97,7 @@ "features/software-templates/builtin-actions", "features/software-templates/writing-custom-actions", "features/software-templates/writing-custom-field-extensions", + "features/software-templates/writing-custom-step-layouts", "features/software-templates/migrating-from-v1beta2-to-v1beta3" ] }, @@ -275,7 +277,6 @@ ] }, "auth/identity-resolver", - "auth/auth-backend", "auth/oauth", "auth/add-auth-provider", "auth/troubleshooting", @@ -286,6 +287,7 @@ "permissions/concepts", "permissions/getting-started", "permissions/writing-a-policy", + "permissions/frontend-integration", "permissions/custom-rules", { "type": "subcategory", @@ -294,7 +296,8 @@ "permissions/plugin-authors/01-setup", "permissions/plugin-authors/02-adding-a-basic-permission-check", "permissions/plugin-authors/03-adding-a-resource-permission-check", - "permissions/plugin-authors/04-authorizing-access-to-paginated-data" + "permissions/plugin-authors/04-authorizing-access-to-paginated-data", + "permissions/plugin-authors/05-frontend-authorization" ] } ], diff --git a/microsite/static/img/playlist-logo.png b/microsite/static/img/playlist-logo.png new file mode 100644 index 0000000000..333a9f832a Binary files /dev/null and b/microsite/static/img/playlist-logo.png differ diff --git a/mkdocs.yml b/mkdocs.yml index 0d386e63fa..8c86ae095d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -64,6 +64,7 @@ nav: - Writing Templates: 'features/software-templates/writing-templates.md' - Builtin Actions: 'features/software-templates/builtin-actions.md' - Writing Custom Actions: 'features/software-templates/writing-custom-actions.md' + - Writing Custom Step Layouts: 'features/software-templates/writing-custom-step-layouts.md' - Writing Templates (Legacy): 'features/software-templates/legacy.md' - Migrating from v1alpha1 to v1beta2 templates: 'features/software-templates/migrating-from-v1alpha1-to-v1beta2.md' - Backstage Search: @@ -161,7 +162,6 @@ nav: - OAuth2Proxy: 'auth/oauth2-proxy/provider.md' - Bitbucket: 'auth/bitbucket/provider.md' - Sign in resolvers: 'auth/identity-resolver.md' - - Auth backend: 'auth/auth-backend.md' - OAuth and OpenID Connect: 'auth/oauth.md' - Contributing New Providers: 'auth/add-auth-provider.md' - Troubleshooting Auth: 'auth/troubleshooting.md' diff --git a/package.json b/package.json index a0de99bfad..31b0c194d2 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "tsc": "tsc", "tsc:full": "backstage-cli repo clean && tsc --skipLibCheck false --incremental false", "clean": "backstage-cli repo clean", - "test": "backstage-cli test", - "test:all": "lerna run test -- --coverage", + "test": "backstage-cli repo test", + "test:all": "backstage-cli repo test --coverage", "lint": "backstage-cli repo lint --since origin/master", "lint:docs": "node ./scripts/check-docs-quality", "lint:all": "backstage-cli repo lint", @@ -26,7 +26,6 @@ "create-plugin": "echo \"use 'yarn new' instead\"", "release": "node scripts/prepare-release.js && changeset version && yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}' '.changeset/*.json' && yarn install --no-immutable", "prettier:check": "prettier --check .", - "lerna": "lerna", "storybook": "yarn ./storybook run start", "snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false", "snyk:test:package": "yarn snyk:test --include", @@ -45,7 +44,7 @@ "@types/react": "^17", "@types/react-dom": "^17" }, - "version": "1.6.0-next.3", + "version": "1.6.0", "dependencies": { "@manypkg/get-packages": "^1.1.3", "@microsoft/api-documenter": "^7.17.11", @@ -71,7 +70,6 @@ "eslint-plugin-notice": "^0.9.10", "fs-extra": "10.1.0", "husky": "^8.0.0", - "lerna": "~5.0.0", "lint-staged": "^13.0.0", "minimist": "^1.2.5", "prettier": "^2.2.1", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index c9216bf2a4..6d9d3fc029 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/app-defaults +## 1.0.6 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d9e39544be: Add missing peer dependencies +- Updated dependencies + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-permission-react@0.4.5 + ## 1.0.6-next.2 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index c83e31b057..0742d2cb13 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "1.0.6-next.2", + "version": "1.0.6", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -32,11 +32,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-permission-react": "^0.4.5-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/core-app-api": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1" }, @@ -47,8 +47,8 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@types/node": "^16.11.26", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index f763210b27..e5c37fb1d9 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,66 @@ # example-app +## 0.2.75 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/app-defaults@1.0.6 + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-airbrake@0.3.9 + - @backstage/plugin-api-docs@0.8.9 + - @backstage/plugin-azure-devops@0.2.0 + - @backstage/plugin-badges@0.2.33 + - @backstage/plugin-catalog-graph@0.2.21 + - @backstage/plugin-catalog-import@0.8.12 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-circleci@0.3.9 + - @backstage/plugin-cloudbuild@0.3.9 + - @backstage/plugin-code-coverage@0.2.2 + - @backstage/plugin-cost-insights@0.11.31 + - @backstage/plugin-explore@0.3.40 + - @backstage/plugin-gcp-projects@0.3.28 + - @backstage/plugin-github-actions@0.5.9 + - @backstage/plugin-graphiql@0.2.41 + - @backstage/plugin-home@0.4.25 + - @backstage/plugin-jenkins@0.7.8 + - @backstage/plugin-kafka@0.3.9 + - @backstage/plugin-kubernetes@0.7.2 + - @backstage/plugin-lighthouse@0.3.9 + - @backstage/plugin-org@0.5.9 + - @backstage/plugin-pagerduty@0.5.2 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/plugin-rollbar@0.4.9 + - @backstage/plugin-scaffolder@1.6.0 + - @backstage/plugin-search-react@1.1.0 + - @backstage/plugin-search@1.0.2 + - @backstage/plugin-sentry@0.4.2 + - @backstage/plugin-shortcuts@0.3.1 + - @backstage/plugin-tech-insights@0.3.0 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/plugin-techdocs@1.3.2 + - @backstage/plugin-todo@0.2.11 + - @backstage/plugin-user-settings@0.4.8 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.4 + - @backstage/plugin-newrelic-dashboard@0.2.2 + - @backstage/plugin-dynatrace@0.2.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-apache-airflow@0.2.2 + - @backstage/plugin-gcalendar@0.3.5 + - @backstage/plugin-gocd@0.1.15 + - @backstage/plugin-newrelic@0.3.27 + - @backstage/plugin-stack-overflow@0.1.5 + - @backstage/plugin-tech-radar@0.5.16 + - @backstage/plugin-playlist@0.1.0 + - @backstage/plugin-catalog-common@1.0.6 + - @internal/plugin-catalog-customized@0.0.2 + - @backstage/plugin-search-common@1.0.1 + ## 0.2.75-next.3 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index df68e1032c..ad24f23c45 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,67 +1,68 @@ { "name": "example-app", - "version": "0.2.75-next.3", + "version": "0.2.75", "private": true, "backstage": { "role": "frontend" }, "bundled": true, "dependencies": { - "@backstage/app-defaults": "^1.0.6-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-airbrake": "^0.3.9-next.3", - "@backstage/plugin-apache-airflow": "^0.2.2-next.3", - "@backstage/plugin-api-docs": "^0.8.9-next.3", - "@backstage/plugin-azure-devops": "^0.2.0-next.3", - "@backstage/plugin-badges": "^0.2.33-next.3", - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-catalog-graph": "^0.2.21-next.2", - "@backstage/plugin-catalog-import": "^0.8.12-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-circleci": "^0.3.9-next.3", - "@backstage/plugin-cloudbuild": "^0.3.9-next.3", - "@backstage/plugin-code-coverage": "^0.2.2-next.3", - "@backstage/plugin-cost-insights": "^0.11.31-next.3", - "@backstage/plugin-dynatrace": "^0.2.0-next.3", - "@backstage/plugin-explore": "^0.3.40-next.3", - "@backstage/plugin-gcalendar": "^0.3.5-next.3", - "@backstage/plugin-gcp-projects": "^0.3.28-next.3", - "@backstage/plugin-github-actions": "^0.5.9-next.3", - "@backstage/plugin-gocd": "^0.1.15-next.2", - "@backstage/plugin-graphiql": "^0.2.41-next.3", - "@backstage/plugin-home": "^0.4.25-next.3", - "@backstage/plugin-jenkins": "^0.7.8-next.3", - "@backstage/plugin-kafka": "^0.3.9-next.3", - "@backstage/plugin-kubernetes": "^0.7.2-next.3", - "@backstage/plugin-lighthouse": "^0.3.9-next.3", - "@backstage/plugin-newrelic": "^0.3.27-next.3", - "@backstage/plugin-newrelic-dashboard": "^0.2.2-next.2", - "@backstage/plugin-org": "^0.5.9-next.3", - "@backstage/plugin-pagerduty": "0.5.2-next.3", - "@backstage/plugin-permission-react": "^0.4.5-next.2", - "@backstage/plugin-rollbar": "^0.4.9-next.3", - "@backstage/plugin-scaffolder": "^1.6.0-next.3", - "@backstage/plugin-search": "^1.0.2-next.3", - "@backstage/plugin-search-common": "^1.0.1-next.0", - "@backstage/plugin-search-react": "^1.1.0-next.2", - "@backstage/plugin-sentry": "^0.4.2-next.3", - "@backstage/plugin-shortcuts": "^0.3.1-next.3", - "@backstage/plugin-stack-overflow": "^0.1.5-next.3", - "@backstage/plugin-tech-insights": "^0.3.0-next.3", - "@backstage/plugin-tech-radar": "^0.5.16-next.3", - "@backstage/plugin-techdocs": "^1.3.2-next.3", - "@backstage/plugin-techdocs-module-addons-contrib": "^1.0.4-next.2", - "@backstage/plugin-techdocs-react": "^1.0.4-next.2", - "@backstage/plugin-todo": "^0.2.11-next.3", - "@backstage/plugin-user-settings": "^0.4.8-next.3", - "@backstage/theme": "^0.2.16", - "@internal/plugin-catalog-customized": "0.0.2-next.0", + "@backstage/app-defaults": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-airbrake": "workspace:^", + "@backstage/plugin-apache-airflow": "workspace:^", + "@backstage/plugin-api-docs": "workspace:^", + "@backstage/plugin-azure-devops": "workspace:^", + "@backstage/plugin-badges": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-graph": "workspace:^", + "@backstage/plugin-catalog-import": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-circleci": "workspace:^", + "@backstage/plugin-cloudbuild": "workspace:^", + "@backstage/plugin-code-coverage": "workspace:^", + "@backstage/plugin-cost-insights": "workspace:^", + "@backstage/plugin-dynatrace": "workspace:^", + "@backstage/plugin-explore": "workspace:^", + "@backstage/plugin-gcalendar": "workspace:^", + "@backstage/plugin-gcp-projects": "workspace:^", + "@backstage/plugin-github-actions": "workspace:^", + "@backstage/plugin-gocd": "workspace:^", + "@backstage/plugin-graphiql": "workspace:^", + "@backstage/plugin-home": "workspace:^", + "@backstage/plugin-jenkins": "workspace:^", + "@backstage/plugin-kafka": "workspace:^", + "@backstage/plugin-kubernetes": "workspace:^", + "@backstage/plugin-lighthouse": "workspace:^", + "@backstage/plugin-newrelic": "workspace:^", + "@backstage/plugin-newrelic-dashboard": "workspace:^", + "@backstage/plugin-org": "workspace:^", + "@backstage/plugin-pagerduty": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/plugin-playlist": "workspace:^", + "@backstage/plugin-rollbar": "workspace:^", + "@backstage/plugin-scaffolder": "workspace:^", + "@backstage/plugin-search": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "@backstage/plugin-search-react": "workspace:^", + "@backstage/plugin-sentry": "workspace:^", + "@backstage/plugin-shortcuts": "workspace:^", + "@backstage/plugin-stack-overflow": "workspace:^", + "@backstage/plugin-tech-insights": "workspace:^", + "@backstage/plugin-tech-radar": "workspace:^", + "@backstage/plugin-techdocs": "workspace:^", + "@backstage/plugin-techdocs-module-addons-contrib": "workspace:^", + "@backstage/plugin-techdocs-react": "workspace:^", + "@backstage/plugin-todo": "workspace:^", + "@backstage/plugin-user-settings": "workspace:^", + "@backstage/theme": "workspace:^", + "@internal/plugin-catalog-customized": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -80,7 +81,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/test-utils": "workspace:^", "@rjsf/core": "^3.2.1", "@testing-library/cypress": "^8.0.2", "@testing-library/jest-dom": "^5.10.1", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 54eac880ec..a6c613ded7 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -64,6 +64,7 @@ import { ScaffolderPage, NextScaffolderPage, scaffolderPlugin, + ScaffolderLayouts, } from '@backstage/plugin-scaffolder'; import { SearchPage } from '@backstage/plugin-search'; import { TechRadarPage } from '@backstage/plugin-tech-radar'; @@ -103,6 +104,8 @@ import { techDocsPage } from './components/techdocs/TechDocsPage'; import { ApacheAirflowPage } from '@backstage/plugin-apache-airflow'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common'; +import { PlaylistIndexPage } from '@backstage/plugin-playlist'; +import { TwoColumnLayout } from './components/scaffolder/customScaffolderLayouts'; const app = createApp({ apis, @@ -145,7 +148,7 @@ const AppRouter = app.getRouter(); const routes = ( - + } /> {/* TODO(rubenl): Move this to / once its more mature and components exist */} }> {homePage} @@ -217,6 +220,9 @@ const routes = ( + + + } /> } /> + } /> ); diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 30da01841c..8b5f0932fb 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -22,6 +22,7 @@ import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; import MapIcon from '@material-ui/icons/MyLocation'; import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import PlaylistPlayIcon from '@material-ui/icons/PlaylistPlay'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import SearchIcon from '@material-ui/icons/Search'; import MenuIcon from '@material-ui/icons/Menu'; @@ -105,6 +106,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( /> + {/* End global nav */} diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index e15df4bc6c..c2f297a2d7 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -104,6 +104,7 @@ import { EntityPagerDutyCard, isPagerDutyAvailable, } from '@backstage/plugin-pagerduty'; +import { EntityPlaylistDialog } from '@backstage/plugin-playlist'; import { EntityRollbarContent, isRollbarAvailable, @@ -114,6 +115,7 @@ import { EntityTechInsightsScorecardCard } from '@backstage/plugin-tech-insights import { EntityTodoContent } from '@backstage/plugin-todo'; import { Button, Grid } from '@material-ui/core'; import BadgeIcon from '@material-ui/icons/CallToAction'; +import PlaylistAddIcon from '@material-ui/icons/PlaylistAdd'; import { EntityGithubInsightsContent, @@ -155,6 +157,7 @@ const customEntityFilterKind = ['Component', 'API', 'System']; const EntityLayoutWrapper = (props: { children?: ReactNode }) => { const [badgesDialogOpen, setBadgesDialogOpen] = useState(false); + const [playlistDialogOpen, setPlaylistDialogOpen] = useState(false); const extraMenuItems = useMemo(() => { return [ @@ -163,6 +166,11 @@ const EntityLayoutWrapper = (props: { children?: ReactNode }) => { Icon: BadgeIcon, onClick: () => setBadgesDialogOpen(true), }, + { + title: 'Add to playlist', + Icon: PlaylistAddIcon, + onClick: () => setPlaylistDialogOpen(true), + }, ]; }, []); @@ -180,6 +188,10 @@ const EntityLayoutWrapper = (props: { children?: ReactNode }) => { open={badgesDialogOpen} onClose={() => setBadgesDialogOpen(false)} /> + setPlaylistDialogOpen(false)} + /> ); }; diff --git a/packages/app/src/components/scaffolder/customScaffolderLayouts.tsx b/packages/app/src/components/scaffolder/customScaffolderLayouts.tsx new file mode 100644 index 0000000000..b23d6e4073 --- /dev/null +++ b/packages/app/src/components/scaffolder/customScaffolderLayouts.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2022 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 { + createScaffolderLayout, + LayoutTemplate, + scaffolderPlugin, +} from '@backstage/plugin-scaffolder'; +import { Grid } from '@material-ui/core'; + +const TwoColumn: LayoutTemplate = ({ properties, description, title }) => { + const mid = Math.ceil(properties.length / 2); + const left = properties.slice(0, mid); + const right = properties.slice(mid); + return ( + <> +

{title}

+ + {left.map(prop => ( + + {prop.content} + + ))} + {right.map(prop => ( + + {prop.content} + + ))} + + {description} + + ); +}; + +export const TwoColumnLayout = scaffolderPlugin.provide( + createScaffolderLayout({ + name: 'TwoColumn', + component: TwoColumn, + }), +); diff --git a/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts b/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts index 2e23b08e53..56f34779de 100644 --- a/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts +++ b/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts @@ -17,6 +17,7 @@ export const defaultPreviewTemplate = `# Edit the template parameters below to see how they will render in the scaffolder form UI parameters: - title: Fill in some steps + ui:ObjectFieldTemplate: TwoColumn required: - name properties: diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index a9ab990f92..801ec9aba6 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/backend-app-api +## 0.2.1 + +### Patch Changes + +- 2c57c0c499: Made `ApiRef.defaultFactory` internal. +- 854ba37357: Updated to support new `ServiceFactory` formats. +- af6bb42c68: Updated `ServiceRegistry` to not initialize factories more than once. +- 409ed984e8: Updated service implementations and backend wiring to support scoped service. +- de3347ca74: Updated usages of `ServiceFactory`. +- 1f384c5644: Improved error messaging when failing to instantiate services. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/backend-tasks@0.3.5 + - @backstage/errors@1.1.1 + ## 0.2.1-next.2 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index a028af090f..2878de5aa3 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-app-api", "description": "Core API used by Backstage backend apps", - "version": "0.2.1-next.2", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -33,17 +33,17 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-plugin-api": "^0.1.2-next.2", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-permission-node": "^0.6.5-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", "express": "^4.17.1", "express-promise-router": "^4.1.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index aaa90ac704..4e51ad3b8f 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,93 @@ # @backstage/backend-common +## 0.15.1 + +### Patch Changes + +- 399286d7dd: Workaround support for `swc` instead of `sucrase` +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- c3c90280be: Added a keep-alive refresh loop to the `DatabaseManager`, keeping the connection + pool up. +- c3c90280be: The options part of `DatabaseManager.fromConfig` now accepts an optional logger + field. +- 3d4f5daadf: Remove use of deprecated trimLeft/trimRight +- bf3cc134eb: Implemented KubernetesContainerRunner: a ContainerRunner implementation that leverages Jobs on a kubernetes cluster + + ```ts + const kubeConfig = new KubeConfig(); + kubeConfig.loadFromDefault(); + + const options: KubernetesContainerRunnerOptions = { + kubeConfig, + // namespace where Jobs will be created + namespace: 'default', + // Jobs name will be prefixed with this name + name: 'my-runner', + // An existing Kubernetes volume that will be used + // as base for mounts + mountBase: { + volumeName: 'workdir', + // Every mount must start with the base path + // see example below + basePath: '/workdir', + }, + // Define a Pod template for the Jobs. It has to include + // a volume definition named as the mountBase volumeName + podTemplate: { + spec: { + containers: [], + volumes: [ + { + name: 'workdir', + persistentVolumeClaim: { + claimName: 'workdir-claim', + }, + }, + ], + }, + }, + }; + const containerRunner = new KubernetesContainerRunner(options); + + const runOptions: RunContainerOptions = { + imageName: 'golang:1.17', + args: ['echo', 'hello world'], + mountDirs: { + '/workdir/app': '/app', + }, + }; + containerRunner.runContainer(runOptions); + ``` + +- e3b1993788: Added port ranges in allowed hosts: + + ```yaml + reading: + allow: + - host: *.examples.org:900-1000 + ``` + +- 2f52e74b49: Got rid of usages of the uppercase String type +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 709f468330: The `branch` command has been added to the `isomorphic-git` wrapper. +- 0c780278e0: Fix for entries being skipped or incomplete when reading large zip archives. +- 96689fbdcb: Workaround for a rare race condition in tests. +- Updated dependencies + - @backstage/integration@1.3.1 + - @backstage/config-loader@1.1.4 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.15.1-next.3 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 81dc37caca..35e1ccdf10 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.15.1-next.3", + "version": "0.15.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -34,12 +34,12 @@ "test:kubernetes": "backstage-cli package test -t KubernetesContainerRunner --no-watch" }, "dependencies": { - "@backstage/cli-common": "^0.1.10-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/config-loader": "^1.1.4-next.2", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/types": "^1.0.0", + "@backstage/cli-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/config-loader": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/types": "workspace:^", "@google-cloud/storage": "^6.0.0", "@keyv/redis": "^2.2.3", "@kubernetes/client-node": "^0.17.0", @@ -94,8 +94,8 @@ } }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/archiver": "^5.1.0", "@types/base64-stream": "^1.0.2", "@types/compression": "^1.7.0", diff --git a/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts b/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts index 427c237802..e45a25db66 100644 --- a/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts +++ b/packages/backend-common/src/reading/tree/ZipArchiveResponse.test.ts @@ -16,6 +16,8 @@ import fs from 'fs-extra'; import mockFs from 'mock-fs'; +import { Readable } from 'stream'; +import { create as createArchive } from 'archiver'; import { resolve as resolvePath } from 'path'; import { ZipArchiveResponse } from './ZipArchiveResponse'; @@ -161,6 +163,48 @@ describe('ZipArchiveResponse', () => { ).resolves.toBe(false); }); + it('should extract a large archive', async () => { + const fileCount = 10; + const fileSize = 1000 * 1000; + const filePath = await new Promise((resolve, reject) => { + const outFile = '/large-archive.zip'; + const archive = createArchive('zip'); + + archive.on('error', reject); + archive.on('end', () => resolve(outFile)); + archive.pipe(fs.createWriteStream(outFile)); + archive.on('warning', w => console.warn('WARN', w)); + + for (let i = 0; i < fileCount; i++) { + // Workaround for https://github.com/archiverjs/node-archiver/issues/542 + // TODO(Rugvip): Without this workaround the archive entries end up with an uncompressed size of 0. + // That in turn causes yauzl to hang on extraction, because the internal transform error is ignored. + const stream = new Readable(); + stream.push(Buffer.alloc(fileSize, i)); + stream.push(null); + archive.append(stream, { name: `file-${i}.data` }); + } + + archive.finalize(); + }); + + const stream = fs.createReadStream(filePath); + + const res = new ZipArchiveResponse(stream, '', '/tmp', 'etag'); + const dir = await res.dir({ + targetDir: '/out', + }); + + expect(dir).toBe('/out'); + const files = await fs.readdir(dir); + expect(files).toHaveLength(fileCount); + + for (const file of files) { + const stat = await fs.stat(resolvePath(dir, file)); + expect(stat.size).toBe(fileSize); + } + }); + it('should throw on invalid archive', async () => { const stream = fs.createReadStream('/test-archive-corrupted.zip'); diff --git a/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts b/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts index 4100af3d57..5a1c5f9688 100644 --- a/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/ZipArchiveResponse.ts @@ -124,9 +124,11 @@ export class ZipArchiveResponse implements ReadTreeResponse { } await callback(entry, readStream); + zipfile.readEntry(); }); + } else { + zipfile.readEntry(); } - zipfile.readEntry(); }); zipfile.once('end', () => resolve()); zipfile.on('error', e => reject(e)); diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 9684395ce0..2e8ef65792 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-defaults +## 0.1.1 + +### Patch Changes + +- 854ba37357: Updated to support new `ServiceFactory` formats. +- de3347ca74: Updated usages of `ServiceFactory`. +- Updated dependencies + - @backstage/backend-app-api@0.2.1 + - @backstage/backend-plugin-api@0.1.2 + ## 0.1.1-next.1 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index e754b74c4d..23652120e2 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.1.1-next.1", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -32,11 +32,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-app-api": "^0.2.1-next.2", - "@backstage/backend-plugin-api": "^0.1.2-next.2" + "@backstage/backend-app-api": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index e74e609f00..a37fc48dac 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,14 @@ # example-backend-next +## 0.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-defaults@0.1.1 + ## 0.0.3-next.1 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 175a945c31..4e989c81cf 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.3-next.1", + "version": "0.0.3", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,12 +25,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-defaults": "^0.1.1-next.0", - "@backstage/plugin-catalog-backend": "^1.4.0-next.1", - "@backstage/plugin-scaffolder-backend": "^1.6.0-next.1" + "@backstage/backend-defaults": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index b0dadbf61c..33e3b36268 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-plugin-api +## 0.1.2 + +### Patch Changes + +- 2c57c0c499: Made `ApiRef.defaultFactory` internal. +- 91eed37a39: Updated `createBackendPlugin` and `createBackendModule` to properly forward lack of options. +- 409ed984e8: Service are now scoped to either `'plugin'` or `'root'` scope. Service factories have been updated to provide dependency instances directly rather than factory functions. +- eef91a2558: Simplified the `ServiceFactory` type and removed `AnyServiceFactory`. +- 854ba37357: The `createServiceFactory` method has been updated to return a higher-order factory that can accept options. +- 68513f169a: When defining a new `ServiceRef` you can now also include a `defaultFactory`, which will be used to construct instances of the service in case there is no explicit factory defined. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + ## 0.1.2-next.2 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index e9593dec3d..18c6c842fc 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-api", "description": "Core API used by Backstage backend plugins", - "version": "0.1.2-next.2", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -33,17 +33,17 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/config": "^1.0.2-next.0", - "@backstage/plugin-permission-common": "^0.6.4-next.2", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "winston": "^3.2.1", "winston-transport": "^4.5.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist", diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 2307fd5d57..0510a54825 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/backend-tasks +## 0.3.5 + +### Patch Changes + +- 243533ecdc: Added support to mysql on some raw queries +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.3.5-next.1 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 444616660a..88b8cf0345 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.3.5-next.1", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -32,10 +32,10 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/types": "workspace:^", "@types/luxon": "^3.0.0", "cron": "^2.0.0", "knex": "^2.0.0", @@ -47,8 +47,8 @@ "zod": "^3.9.5" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/cron": "^2.0.0", "wait-for-expect": "^3.0.2" }, diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts index 16550b2d7d..d61948a929 100644 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts @@ -25,8 +25,6 @@ import { PluginTaskSchedulerImpl, } from './PluginTaskSchedulerImpl'; -jest.useFakeTimers(); - function defer() { let resolve = () => {}; const promise = new Promise(_resolve => { @@ -40,6 +38,15 @@ describe('PluginTaskManagerImpl', () => { ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], }); + beforeAll(async () => { + // Make sure all databases are running before mocking timers, in case of testcontainers + await Promise.all( + databases.eachSupportedId().map(([id]) => databases.init(id)), + ); + + jest.useFakeTimers(); + }, 60_000); + async function init(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); await migrateBackendTasks(knex); diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 5ec06645b0..27e41be568 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-test-utils +## 0.1.28 + +### Patch Changes + +- 854ba37357: Updated to support new `ServiceFactory` formats. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- de3347ca74: Updated usages of `ServiceFactory`. +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/backend-app-api@0.2.1 + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + ## 0.1.28-next.3 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 374860a387..b1006b8464 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.28-next.3", + "version": "0.1.28", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -34,11 +34,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-app-api": "^0.2.1-next.2", - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-plugin-api": "^0.1.2-next.2", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/config": "^1.0.2-next.0", + "@backstage/backend-app-api": "workspace:^", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^", "better-sqlite3": "^7.5.0", "knex": "^2.0.0", "msw": "^0.47.0", @@ -48,7 +48,7 @@ "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist", diff --git a/packages/backend-test-utils/src/database/TestDatabases.ts b/packages/backend-test-utils/src/database/TestDatabases.ts index 76d12ce8f1..6ff1a2e6f7 100644 --- a/packages/backend-test-utils/src/database/TestDatabases.ts +++ b/packages/backend-test-utils/src/database/TestDatabases.ts @@ -28,6 +28,13 @@ import { TestDatabaseProperties, } from './types'; +const LARGER_POOL_CONFIG = { + pool: { + min: 0, + max: 50, + }, +}; + /** * Encapsulates the creation of ephemeral test database instances for use * inside unit or integration tests. @@ -164,6 +171,9 @@ export class TestDatabases { new ConfigReader({ backend: { database: { + knexConfig: properties.driver.includes('sqlite') + ? {} + : LARGER_POOL_CONFIG, client: properties.driver, connection: connectionString, }, @@ -203,6 +213,7 @@ export class TestDatabases { new ConfigReader({ backend: { database: { + knexConfig: LARGER_POOL_CONFIG, client: 'pg', connection: { host, port, user, password }, }, @@ -228,6 +239,7 @@ export class TestDatabases { new ConfigReader({ backend: { database: { + knexConfig: LARGER_POOL_CONFIG, client: 'mysql2', connection: { host, port, user, password }, }, diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 7b20372856..b196b1df9e 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,47 @@ # example-backend +## 0.2.75 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/plugin-kubernetes-backend@0.7.2 + - @backstage/plugin-kafka-backend@0.2.29 + - @backstage/plugin-proxy-backend@0.2.30 + - @backstage/plugin-auth-backend@0.16.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/plugin-azure-devops-backend@0.3.15 + - @backstage/plugin-search-backend-node@1.0.2 + - @backstage/plugin-tech-insights-node@0.3.4 + - @backstage/backend-tasks@0.3.5 + - @backstage/plugin-techdocs-backend@1.3.0 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.4 + - @backstage/plugin-search-backend-module-pg@0.4.0 + - @backstage/plugin-jenkins-backend@0.1.26 + - @backstage/plugin-playlist-backend@0.1.0 + - @backstage/plugin-app-backend@0.3.36 + - @backstage/plugin-graphql-backend@0.1.26 + - @backstage/plugin-rollbar-backend@0.1.33 + - @backstage/plugin-code-coverage-backend@0.2.2 + - @backstage/plugin-permission-backend@0.5.11 + - @backstage/plugin-todo-backend@0.1.33 + - @backstage/plugin-search-backend-module-elasticsearch@1.0.2 + - @backstage/plugin-tech-insights-backend@0.5.2 + - @backstage/plugin-badges-backend@0.1.30 + - example-app@0.2.75 + - @backstage/plugin-search-backend@1.0.2 + - @backstage/plugin-search-common@1.0.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.20 + ## 0.2.75-next.3 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index a8ae29ee18..54bf8c586e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.75-next.3", + "version": "0.2.75", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -26,40 +26,41 @@ "build-image": "docker build ../.. -f Dockerfile --tag example-backend" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-app-backend": "^0.3.36-next.3", - "@backstage/plugin-auth-backend": "^0.16.0-next.3", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/plugin-azure-devops-backend": "^0.3.15-next.2", - "@backstage/plugin-badges-backend": "^0.1.30-next.1", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/plugin-code-coverage-backend": "^0.2.2-next.2", - "@backstage/plugin-graphql-backend": "^0.1.26-next.3", - "@backstage/plugin-jenkins-backend": "^0.1.26-next.3", - "@backstage/plugin-kafka-backend": "^0.2.29-next.1", - "@backstage/plugin-kubernetes-backend": "^0.7.2-next.3", - "@backstage/plugin-permission-backend": "^0.5.11-next.2", - "@backstage/plugin-permission-common": "^0.6.4-next.2", - "@backstage/plugin-permission-node": "^0.6.5-next.3", - "@backstage/plugin-proxy-backend": "^0.2.30-next.2", - "@backstage/plugin-rollbar-backend": "^0.1.33-next.3", - "@backstage/plugin-scaffolder-backend": "^1.6.0-next.3", - "@backstage/plugin-scaffolder-backend-module-rails": "^0.4.4-next.1", - "@backstage/plugin-search-backend": "^1.0.2-next.1", - "@backstage/plugin-search-backend-module-elasticsearch": "^1.0.2-next.2", - "@backstage/plugin-search-backend-module-pg": "^0.4.0-next.2", - "@backstage/plugin-search-backend-node": "^1.0.2-next.2", - "@backstage/plugin-search-common": "^1.0.1-next.0", - "@backstage/plugin-tech-insights-backend": "^0.5.2-next.2", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.20-next.1", - "@backstage/plugin-tech-insights-node": "^0.3.4-next.1", - "@backstage/plugin-techdocs-backend": "^1.3.0-next.2", - "@backstage/plugin-todo-backend": "^0.1.33-next.2", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-app-backend": "workspace:^", + "@backstage/plugin-auth-backend": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-azure-devops-backend": "workspace:^", + "@backstage/plugin-badges-backend": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-code-coverage-backend": "workspace:^", + "@backstage/plugin-graphql-backend": "workspace:^", + "@backstage/plugin-jenkins-backend": "workspace:^", + "@backstage/plugin-kafka-backend": "workspace:^", + "@backstage/plugin-kubernetes-backend": "workspace:^", + "@backstage/plugin-permission-backend": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", + "@backstage/plugin-playlist-backend": "workspace:^", + "@backstage/plugin-proxy-backend": "workspace:^", + "@backstage/plugin-rollbar-backend": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/plugin-scaffolder-backend-module-rails": "workspace:^", + "@backstage/plugin-search-backend": "workspace:^", + "@backstage/plugin-search-backend-module-elasticsearch": "workspace:^", + "@backstage/plugin-search-backend-module-pg": "workspace:^", + "@backstage/plugin-search-backend-node": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "@backstage/plugin-tech-insights-backend": "workspace:^", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "workspace:^", + "@backstage/plugin-tech-insights-node": "workspace:^", + "@backstage/plugin-techdocs-backend": "workspace:^", + "@backstage/plugin-todo-backend": "workspace:^", "@gitbeaker/node": "^35.1.0", "@octokit/rest": "^19.0.3", "azure-devops-node-api": "^11.0.1", @@ -76,7 +77,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/dockerode": "^3.3.0", "@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 de3f435508..cb9c9bf580 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -57,6 +57,7 @@ import app from './plugins/app'; import badges from './plugins/badges'; import jenkins from './plugins/jenkins'; import permission from './plugins/permission'; +import playlist from './plugins/playlist'; import { PluginEnvironment } from './types'; import { ServerPermissionClient } from '@backstage/plugin-permission-node'; import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; @@ -139,6 +140,7 @@ async function main() { createEnv('tech-insights'), ); const permissionEnv = useHotMemoize(module, () => createEnv('permission')); + const playlistEnv = useHotMemoize(module, () => createEnv('playlist')); const apiRouter = Router(); apiRouter.use('/catalog', await catalog(catalogEnv)); @@ -158,6 +160,7 @@ async function main() { apiRouter.use('/badges', await badges(badgesEnv)); apiRouter.use('/jenkins', await jenkins(jenkinsEnv)); apiRouter.use('/permission', await permission(permissionEnv)); + apiRouter.use('/playlist', await playlist(playlistEnv)); apiRouter.use(notFoundHandler()); const service = createServiceBuilder(module) diff --git a/packages/backend/src/plugins/permission.ts b/packages/backend/src/plugins/permission.ts index 0d3dcc588d..7192a1ddec 100644 --- a/packages/backend/src/plugins/permission.ts +++ b/packages/backend/src/plugins/permission.ts @@ -14,17 +14,34 @@ * limitations under the License. */ +import { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; import { createRouter } from '@backstage/plugin-permission-backend'; import { AuthorizeResult, PolicyDecision, } from '@backstage/plugin-permission-common'; -import { PermissionPolicy } from '@backstage/plugin-permission-node'; +import { + PermissionPolicy, + PolicyQuery, +} from '@backstage/plugin-permission-node'; +import { + DefaultPlaylistPermissionPolicy, + isPlaylistPermission, +} from '@backstage/plugin-playlist-backend'; import { Router } from 'express'; import { PluginEnvironment } from '../types'; -class AllowAllPermissionPolicy implements PermissionPolicy { - async handle(): Promise { +class ExamplePermissionPolicy implements PermissionPolicy { + private playlistPermissionPolicy = new DefaultPlaylistPermissionPolicy(); + + async handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { + if (isPlaylistPermission(request.permission)) { + return this.playlistPermissionPolicy.handle(request, user); + } + return { result: AuthorizeResult.ALLOW, }; @@ -38,7 +55,7 @@ export default async function createPlugin( config: env.config, logger: env.logger, discovery: env.discovery, - policy: new AllowAllPermissionPolicy(), + policy: new ExamplePermissionPolicy(), identity: env.identity, }); } diff --git a/packages/backend/src/plugins/playlist.ts b/packages/backend/src/plugins/playlist.ts new file mode 100644 index 0000000000..6cae6a30ab --- /dev/null +++ b/packages/backend/src/plugins/playlist.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2022 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 { createRouter } from '@backstage/plugin-playlist-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return await createRouter({ + database: env.database, + discovery: env.discovery, + identity: env.identity, + logger: env.logger, + permissions: env.permissions, + }); +} diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index 19da3b3ae0..d079b64c28 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -33,5 +33,6 @@ export default async function createPlugin( catalogClient: catalogClient, reader: env.reader, identity: env.identity, + scheduler: env.scheduler, }); } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 831eaebb66..895581c702 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -24,11 +24,8 @@ import { UrlReader, } from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; -import { - PermissionAuthorizer, - PermissionEvaluator, -} from '@backstage/plugin-permission-common'; import { IdentityApi } from '@backstage/plugin-auth-node'; +import { PermissionEvaluator } from '@backstage/plugin-permission-common'; export type PluginEnvironment = { logger: Logger; @@ -38,7 +35,7 @@ export type PluginEnvironment = { reader: UrlReader; discovery: PluginEndpointDiscovery; tokenManager: TokenManager; - permissions: PermissionEvaluator | PermissionAuthorizer; + permissions: PermissionEvaluator; scheduler: PluginTaskScheduler; identity: IdentityApi; }; diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 6cbcab0a4e..774325ff44 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/catalog-client +## 1.1.0 + +### Minor Changes + +- 65d1d4343f: Adding `validateEntity` method that calls `/validate-entity` endpoint. + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 1.1.0-next.2 ### Minor Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 3a06074a4d..fb1de71062 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-client", "description": "An isomorphic client for the catalog backend", - "version": "1.1.0-next.2", + "version": "1.1.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", "cross-fetch": "^3.1.5" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "msw": "^0.47.0" }, "files": [ diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 25d5866002..d3f9fe4af8 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/catalog-model +## 1.1.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 1.1.1-next.0 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 28df6e58fc..ce2abee20a 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-model", "description": "Types and validators that help describe the model of a Backstage Catalog", - "version": "1.1.1-next.0", + "version": "1.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,16 +33,16 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/types": "^1.0.0", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/types": "workspace:^", "ajv": "^8.10.0", "json-schema": "^0.4.0", "lodash": "^4.17.21", "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/json-schema": "^7.0.5", "@types/lodash": "^4.14.151", "yaml": "^2.0.0" diff --git a/packages/catalog-model/src/kinds/util.ts b/packages/catalog-model/src/kinds/util.ts index c77b77cb97..40c88b7066 100644 --- a/packages/catalog-model/src/kinds/util.ts +++ b/packages/catalog-model/src/kinds/util.ts @@ -22,9 +22,12 @@ import { KindValidator } from './types'; // exported kind validators have the `KindValidator` signature which is // different. So let's postpone that change until a later time. export function ajvCompiledJsonSchemaValidator(schema: unknown): KindValidator { - const validator = entityKindSchemaValidator(schema); + let validator: undefined | ((data: unknown) => any); return { async check(data) { + if (!validator) { + validator = entityKindSchemaValidator(schema); + } return validator(data) === data; }, }; diff --git a/packages/cli-common/CHANGELOG.md b/packages/cli-common/CHANGELOG.md index 05362b7a31..30a8fd70bb 100644 --- a/packages/cli-common/CHANGELOG.md +++ b/packages/cli-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/cli-common +## 0.1.10 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + ## 0.1.10-next.0 ### Patch Changes diff --git a/packages/cli-common/package.json b/packages/cli-common/package.json index 965c62ce38..3b229071cf 100644 --- a/packages/cli-common/package.json +++ b/packages/cli-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-common", "description": "Common functionality used by cli, backend, and create-app", - "version": "0.1.10-next.0", + "version": "0.1.10", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -32,7 +32,7 @@ "start": "backstage-cli package start" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/node": "^16.0.0" }, "files": [ diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 52ba0a24c6..0f36ba5e56 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,107 @@ # @backstage/cli +## 0.19.0 + +### Minor Changes + +- 1fe6823bb5: Updated dependency `eslint-plugin-jest` to `^27.0.0`. + + Note that this major update to the Jest plugin contains some breaking changes. + This means that some of your tests may start seeing some new lint errors. [Read + about them + here](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md#2700-2022-08-28). + + These are mostly possible to fix automatically. You can try to run `yarn backstage-cli repo lint --fix` in your repo root to have most or all of them + corrected. + +### Patch Changes + +- 8d886dd33e: The `create-plugin` and `create` commands have both been deprecated in favor of a new `new` command. The `new` command is functionally identical to `create`, but the new naming makes it possible to use as yarn script, since `yarn create` is reserved. +- cc63eb8611: Sort entries in skeleton.tar.gz for better docker layer caching +- 548053614a: Deprecated the `plugin:diff` command. If you wish to keep running similar checks in your project we recommend using bespoke scripts. A useful utility for such scripts is `@manypkg/get-packages`, which helps you enumerate all packages in a monorepo. +- 513b4dd4ef: The `versions:bump` command will now update dependency ranges in `package.json`, even if the new version is within the current range. +- 221e951298: Added support for custom certificate for webpack dev server. +- 934cc34563: Avoid validating the backend configuration schema when loading static configuration for building the frontend. +- 3d4f5daadf: Remove use of deprecated trimLeft/trimRight +- 817f3196f6: Added a new `migrate react-router-deps` command to aid in the migration to React Router v6 stable. +- 742cb4f3d7: Fix issue when using `.jsx` files inside tests +- e7600bdb04: Tweaked workspace packaging to not rewrite existing `package.json` files. +- 6ae0f6a719: Switch out `sucrase` for `swc` for transpilation. + + `sucrase` is a little more relaxed when it comes to supporting the ways of mocking in `jest`. You might have to make some changes to your tests to meet the `jest` standard and spec if your tests seems to start failing. + + Mocks that look like this are invalid, and they will throw a reference error in line with the `jest` documentation [here on example 3](https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter) + + ```ts + const mockCommandExists = jest.fn(); + jest.mock('command-exists', () => mockCommandExists); + ``` + + You might need to update these mocks to look a little like the following to defer the call to the `jest.fn()` spy until the mock is called. + + ```ts + const mockCommandExists = jest.fn(); + jest.mock( + 'command-exists', + () => + (...args: any[]) => + commandExists(...args), + ); + ``` + + Also, imports are immutable. So it means that you might get some errors when trying to use `jest.spyOn` with starred imports. You might see an error like this: + + ```log + TypeError: Cannot redefine property: executeFrameHandlerStrategy + at Function.defineProperty () + + 20 | import { AuthResolverContext } from '../types'; + 21 | + > 22 | const mockFrameHandler = jest.spyOn( + | ^ + 23 | helpers, + 24 | 'executeFrameHandlerStrategy', + 25 | ) as unknown as jest.MockedFunction< + ``` + + This happens when you try to do `import * as something from './something'` and then `jest.spyOn(something, 'test)`. You will need to add a `jest.mock` call to mock out the required starred import to return `jest.fn()` functions from the start. Something like this fixes the above test: + + ```ts + jest.mock('../../helpers', () => ({ + executeFrameHandlerStrategy: jest.fn(), + })); + ``` + + You can also remove any occurrence of `hot(App)` and any import of `react-hot-loader` if you're using the that package locally, as all this has now been replaced with [React Refresh](https://www.npmjs.com/package/react-refresh) which you will get out of the box with the new CLI. + + **Note** If you're experiencing difficulties with running tests after the migration, please reach out to us on Discord to see if we can help, or raise an issue. But in the meantime you can switch back to the existing behaviour by using the following config in your root `package.json`. + + ```json + "jest": { + "transform": { + "\\.(js|jsx|ts|tsx|mjs|cjs)$": "@backstage/cli/config/jestSucraseTransform.js", + "\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg|eot|woff|woff2|ttf)$": "@backstage/cli/config/jestFileTransform.js", + "\\.(yaml)$": "jest-transform-yaml" + } + } + ``` + +- 1cb078ad9f: Fixed a misconfiguration where all modules where treated as ESM by the React Refresh plugin for Webpack. +- 1fd4f2746f: Removed internal dependencies on Lerna. It is now no longer necessary to have Lerna installed in a project to use all features of the Backstage CLI. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 33fbd9f9a4: Updated dependency `@types/minimatch` to `^5.0.0`. +- 68c2697077: Added a new `backstage-cli repo clean` command that cleans the repo root and runs the clean script in all packages. +- 7d47def9c4: Added dependency on `@types/jest` v27. The `@types/jest` dependency has also been removed from the plugin template and should be removed from any of your own internal packages. If you wish to override the version of `@types/jest` or `jest`, use Yarn resolutions. +- a7e82c9b01: Updated `versions:bump` command to be compatible with Yarn 3. +- Updated dependencies + - @backstage/config-loader@1.1.4 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/release-manifests@0.0.6 + ## 0.19.0-next.3 ### Patch Changes diff --git a/packages/cli/cli-report.md b/packages/cli/cli-report.md index 9a67e8245d..9afd12fb48 100644 --- a/packages/cli/cli-report.md +++ b/packages/cli/cli-report.md @@ -276,7 +276,7 @@ Usage: backstage-cli [--config=] [TestPathPattern] Options: -h, --help - -v, --version + --version --all --automock -b, --bail @@ -289,7 +289,6 @@ Options: --clearMocks --collectCoverage --collectCoverageFrom - --collectCoverageOnlyFrom --color --colors -c, --config @@ -312,6 +311,7 @@ Options: --globalTeardown --globals --haste + --ignoreProjects --init --injectGlobals --json @@ -348,6 +348,7 @@ Options: --selectProjects --setupFiles --setupFilesAfterEnv + --shard --showConfig --silent --skipFilter @@ -365,8 +366,6 @@ Options: --testRunner --testSequencer --testTimeout - --testURL - --timers --transform --transformIgnorePatterns --unmockedModulePathPatterns @@ -391,6 +390,7 @@ Commands: build [options] lint [options] clean + test [options] help [command] ``` @@ -426,6 +426,17 @@ Options: -h, --help ``` +### `backstage-cli repo test` + +``` +Usage: backstage-cli repo test [options] + +Options: + --since + --jest-help + -h, --help +``` + ### `backstage-cli test` ``` @@ -433,7 +444,7 @@ Usage: backstage-cli [--config=] [TestPathPattern] Options: -h, --help - -v, --version + --version --all --automock -b, --bail @@ -446,7 +457,6 @@ Options: --clearMocks --collectCoverage --collectCoverageFrom - --collectCoverageOnlyFrom --color --colors -c, --config @@ -469,6 +479,7 @@ Options: --globalTeardown --globals --haste + --ignoreProjects --init --injectGlobals --json @@ -505,6 +516,7 @@ Options: --selectProjects --setupFiles --setupFilesAfterEnv + --shard --showConfig --silent --skipFilter @@ -522,8 +534,6 @@ Options: --testRunner --testSequencer --testTimeout - --testURL - --timers --transform --transformIgnorePatterns --unmockedModulePathPatterns diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 9d113989ff..8b6c37bda4 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -27,7 +27,6 @@ const envOptions = { const transformIgnorePattern = [ '@material-ui', - '@rjsf', 'ajv', 'core-js', 'jest-.*', @@ -189,13 +188,13 @@ async function getProjectConfig(targetPath, displayName) { ], '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg|eot|woff|woff2|ttf)$': require.resolve('./jestFileTransform.js'), - '\\.(yaml)$': require.resolve('jest-transform-yaml'), + '\\.(yaml)$': require.resolve('./jestYamlTransform'), }, // A bit more opinionated testMatch: ['**/*.test.{js,jsx,ts,tsx,mjs,cjs}'], - moduleLoader: envOptions.nextTests + runtime: envOptions.nextTests ? require.resolve('./jestCachingModuleLoader') : undefined, @@ -210,16 +209,16 @@ async function getProjectConfig(targetPath, displayName) { const config = Object.assign(options, ...pkgJsonConfigs); - // The config name is a cache key that lets us share the jest cache across projects. - // If no explicit name was configured, generated one based on the configuration. - if (!config.name) { + // The config id is a cache key that lets us share the jest cache across projects. + // If no explicit id was configured, generated one based on the configuration. + if (!config.id) { const configHash = crypto .createHash('md5') .update(version) .update(Buffer.alloc(1)) .update(JSON.stringify(config.transform)) .digest('hex'); - config.name = `backstage_cli_${configHash}`; + config.id = `backstage_cli_${configHash}`; } return config; diff --git a/packages/cli/config/jestFileTransform.js b/packages/cli/config/jestFileTransform.js index bdd29fc296..d003c5b567 100644 --- a/packages/cli/config/jestFileTransform.js +++ b/packages/cli/config/jestFileTransform.js @@ -21,7 +21,8 @@ module.exports = { const assetFilename = JSON.stringify(path.basename(filename)); if (filename.match(/\.icon\.svg$/)) { - return `const React = require('react'); + return { + code: `const React = require('react'); const SvgIcon = require('@material-ui/core/SvgIcon').default; module.exports = { __esModule: true, @@ -34,9 +35,10 @@ module.exports = { children: ${assetFilename} }) }) - };`; + };`, + }; } - return `module.exports = ${assetFilename};`; + return { code: `module.exports = ${assetFilename};` }; }, }; diff --git a/packages/cli/config/jestSucraseTransform.js b/packages/cli/config/jestSucraseTransform.js index d0400f4f3b..99ebc04877 100644 --- a/packages/cli/config/jestSucraseTransform.js +++ b/packages/cli/config/jestSucraseTransform.js @@ -64,10 +64,10 @@ function createTransformer(config) { } // We only return the `map` result if source maps are enabled, as they // have a negative impact on the coverage accuracy. - return code; + return { code }; } - return source; + return { code: source }; }; // TODO: contribute something like this to @sucrase/jest-plugin diff --git a/packages/cli/config/jestSwcTransform.js b/packages/cli/config/jestSwcTransform.js index c87948978d..c44d0fe4b0 100644 --- a/packages/cli/config/jestSwcTransform.js +++ b/packages/cli/config/jestSwcTransform.js @@ -21,7 +21,7 @@ function createTransformer(config) { const swcTransformer = createSwcTransformer(config); const process = (source, filePath, jestOptions) => { if (filePath.endsWith('.js') && !ESM_REGEX.test(source)) { - return source; + return { code: source }; } return swcTransformer.process(source, filePath, jestOptions); diff --git a/packages/cli/config/jestYamlTransform.js b/packages/cli/config/jestYamlTransform.js new file mode 100644 index 0000000000..1235f8a4b7 --- /dev/null +++ b/packages/cli/config/jestYamlTransform.js @@ -0,0 +1,40 @@ +/* + * Copyright 2022 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. + */ + +const yaml = require('yaml'); +const crypto = require('crypto'); + +function createTransformer(config) { + const process = source => { + const json = JSON.stringify(yaml.parse(source), null, 2); + return { code: `module.exports = ${json}`, map: null }; + }; + + const getCacheKey = sourceText => { + return crypto + .createHash('md5') + .update(sourceText) + .update(Buffer.alloc(1)) + .update(JSON.stringify(config)) + .update(Buffer.alloc(1)) + .update('1') // increment whenever the transform logic in this file changes + .digest('hex'); + }; + + return { process, getCacheKey }; +} + +module.exports = { createTransformer }; diff --git a/packages/cli/package.json b/packages/cli/package.json index 9a412d401f..b07f2a0263 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.19.0-next.3", + "version": "0.19.0", "publishConfig": { "access": "public" }, @@ -30,12 +30,12 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { - "@backstage/cli-common": "^0.1.10-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/config-loader": "^1.1.4-next.2", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/release-manifests": "^0.0.6-next.2", - "@backstage/types": "^1.0.0", + "@backstage/cli-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/config-loader": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/release-manifests": "workspace:^", + "@backstage/types": "workspace:^", "@manypkg/get-packages": "^1.1.3", "@octokit/request": "^6.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -55,7 +55,7 @@ "@swc/core": "^1.2.239", "@swc/helpers": "^0.4.7", "@swc/jest": "^0.2.22", - "@types/jest": "^27", + "@types/jest": "^29.0.0", "@types/webpack-env": "^1.15.2", "@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/parser": "^5.9.0", @@ -89,10 +89,10 @@ "handlebars": "^4.7.3", "html-webpack-plugin": "^5.3.1", "inquirer": "^8.2.0", - "jest": "^27.5.1", + "jest": "^29.0.2", "jest-css-modules": "^2.1.0", - "jest-runtime": "^27.5.1", - "jest-transform-yaml": "^1.0.0", + "jest-environment-jsdom": "^29.0.2", + "jest-runtime": "^29.0.2", "json-schema": "^0.4.0", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.4.2", @@ -129,14 +129,14 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@backstage/theme": "workspace:^", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", "@types/fs-extra": "^9.0.1", diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 8f3317c437..266b4fd9f1 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -72,6 +72,20 @@ export function registerRepoCommand(program: Command) { .action( lazy(() => import('./repo/list-deprecations').then(m => m.command)), ); + + command + .command('test') + .allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args + .option( + '--since ', + 'Only test packages that changed since the specified ref', + ) + .option( + '--jest-help', + 'Show help for Jest CLI options, which are passed through', + ) + .description('Run tests, forwarding args to Jest, defaulting to watch mode') + .action(lazy(() => import('./repo/test').then(m => m.command))); } export function registerScriptCommand(program: Command) { diff --git a/packages/cli/src/commands/repo/test.ts b/packages/cli/src/commands/repo/test.ts new file mode 100644 index 0000000000..578cb15e83 --- /dev/null +++ b/packages/cli/src/commands/repo/test.ts @@ -0,0 +1,134 @@ +/* + * 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 { Command, OptionValues } from 'commander'; +import { PackageGraph } from '../../lib/monorepo'; +import { paths } from '../../lib/paths'; +import { runCheck } from '../../lib/run'; + +function includesAnyOf(hayStack: string[], ...needles: string[]) { + for (const needle of needles) { + if (hayStack.includes(needle)) { + return true; + } + } + return false; +} + +function removeOptionArg(args: string[], option: string) { + let changed = false; + do { + changed = false; + + const index = args.indexOf(option); + if (index >= 0) { + changed = true; + args.splice(index, 2); + } + const indexEq = args.findIndex(arg => arg.startsWith(`${option}=`)); + if (indexEq >= 0) { + changed = true; + args.splice(indexEq, 1); + } + } while (changed); +} + +export async function command(opts: OptionValues, cmd: Command): Promise { + // all args are forwarded to jest + let parent = cmd; + while (parent.parent) { + parent = parent.parent; + } + const allArgs = parent.args as string[]; + const args = allArgs.slice(allArgs.indexOf('test') + 1); + + // Only include our config if caller isn't passing their own config + if (!includesAnyOf(args, '-c', '--config')) { + args.push('--config', paths.resolveOwn('config/jest.js')); + } + + if (!includesAnyOf(args, '--no-passWithNoTests', '--passWithNoTests=false')) { + args.push('--passWithNoTests'); + } + + // Run in watch mode unless in CI, coverage mode, or running all tests + if ( + !process.env.CI && + !args.includes('--coverage') && + // explicitly no watching + !includesAnyOf(args, '--no-watch', '--watch=false', '--watchAll=false') && + // already watching + !includesAnyOf(args, '--watch', '--watchAll') + ) { + const isGitRepo = () => + runCheck('git', 'rev-parse', '--is-inside-work-tree'); + const isMercurialRepo = () => runCheck('hg', '--cwd', '.', 'root'); + + if ((await isGitRepo()) || (await isMercurialRepo())) { + args.push('--watch'); + } else { + args.push('--watchAll'); + } + } + + if (opts.since) { + removeOptionArg(args, '--since'); + } + + if (opts.since && !args.some(arg => arg.startsWith('--selectProjects'))) { + const packages = await PackageGraph.listTargetPackages(); + const graph = PackageGraph.fromPackages(packages); + const changedPackages = await graph.listChangedPackages({ + ref: opts.since, + }); + + const packageNames = Array.from( + graph.collectPackageNames( + changedPackages.map(pkg => pkg.name), + pkg => pkg.allLocalDependents.keys(), + ), + ); + + if (packageNames.length === 0) { + console.log(`No packages changed since ${opts.since}`); + return; + } + + args.push('--selectProjects', ...packageNames); + } + + // This is the only thing that is not implemented by jest.run(), so we do it here instead + // https://github.com/facebook/jest/blob/cd8828f7bbec6e55b4df5e41e853a5133c4a3ee1/packages/jest-cli/bin/jest.js#L12 + if (!process.env.NODE_ENV) { + (process.env as any).NODE_ENV = 'test'; + } + + // This is to have a consistent timezone for when running tests that involve checking + // the formatting of date/times. + // https://stackoverflow.com/questions/56261381/how-do-i-set-a-timezone-in-my-jest-config + if (!process.env.TZ) { + process.env.TZ = 'UTC'; + } + + // This ensures that the process doesn't exit too early before stdout is flushed + if (args.includes('--jest-help')) { + removeOptionArg(args, '--jest-help'); + args.push('--help'); + (process.stdout as any)._handle.setBlocking(true); + } + + await require('jest').run(args); +} diff --git a/packages/cli/src/commands/test.ts b/packages/cli/src/commands/test.ts index ec1bf8a7a0..b55919b2ce 100644 --- a/packages/cli/src/commands/test.ts +++ b/packages/cli/src/commands/test.ts @@ -78,6 +78,7 @@ export default async (_opts: OptionValues, cmd: Command) => { process.env.TZ = 'UTC'; } + // This ensures that the process doesn't exit too early before stdout is flushed if (args.includes('--help')) { (process.stdout as any)._handle.setBlocking(true); } diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 0d4649fa3f..1974153f02 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -267,7 +267,7 @@ export async function createBackendConfig( paths.targetRunFile ? paths.targetRunFile : paths.targetEntry, ], resolve: { - extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json'], + extensions: ['.ts', '.mjs', '.js', '.json'], mainFields: ['main'], modules: [paths.rootNodeModules, ...moduleDirs], plugins: [ diff --git a/packages/cli/src/lib/packager/createDistWorkspace.ts b/packages/cli/src/lib/packager/createDistWorkspace.ts index 53e00ab6d9..5451a873f7 100644 --- a/packages/cli/src/lib/packager/createDistWorkspace.ts +++ b/packages/cli/src/lib/packager/createDistWorkspace.ts @@ -22,7 +22,7 @@ import { relative as relativePath, } from 'path'; import { tmpdir } from 'os'; -import tar, { CreateOptions } from 'tar'; +import tar, { CreateOptions, FileOptions } from 'tar'; import partition from 'lodash/partition'; import { paths } from '../paths'; import { run } from '../run'; @@ -214,10 +214,12 @@ export async function createDistWorkspace( } if (options.skeleton) { - const skeletonFiles = targets.map(target => { - const dir = relativePath(paths.targetRoot, target.dir); - return joinPath(dir, 'package.json'); - }); + const skeletonFiles = targets + .map(target => { + const dir = relativePath(paths.targetRoot, target.dir); + return joinPath(dir, 'package.json'); + }) + .sort(); await tar.create( { @@ -226,7 +228,7 @@ export async function createDistWorkspace( portable: true, noMtime: true, gzip: options.skeleton.endsWith('.gz'), - } as CreateOptions & { noMtime: boolean }, + } as CreateOptions & FileOptions & { noMtime: boolean }, skeletonFiles, ); } diff --git a/packages/cli/src/lib/version.ts b/packages/cli/src/lib/version.ts index 1cddf773f4..5a199236f5 100644 --- a/packages/cli/src/lib/version.ts +++ b/packages/cli/src/lib/version.ts @@ -81,6 +81,14 @@ export function createPackageVersionProvider(lockfile?: Lockfile) { ) { return '*'; } + + for (const specifier of ['^', '~', '*']) { + const range = `workspace:${specifier}`; + if (lockfileEntries?.some(entry => entry.range === range)) { + return range; + } + } + const validRanges = lockfileEntries?.filter(entry => semver.satisfies(targetVersion, entry.range), ); diff --git a/packages/cli/src/lib/versioning/Lockfile.test.ts b/packages/cli/src/lib/versioning/Lockfile.test.ts index bad7ab6719..4621b43372 100644 --- a/packages/cli/src/lib/versioning/Lockfile.test.ts +++ b/packages/cli/src/lib/versioning/Lockfile.test.ts @@ -170,7 +170,7 @@ a@^1: integrity: sha512-xyz resolved: "https://my-registry/a-1.0.01.tgz#abc123" -b@2.0.x: +"b@2.0.x, b@^2.0.1": version: 2.0.1 b@^2: @@ -185,7 +185,7 @@ a@^1: integrity: sha512-xyz resolved: "https://my-registry/a-1.0.01.tgz#abc123" -b@2.0.x: +"b@2.0.x, b@^2.0.1": version: 2.0.1 b@^2: @@ -206,6 +206,7 @@ describe('New Lockfile', () => { expect(lockfile.get('a')).toEqual([{ range: '^1', version: '1.0.1' }]); expect(lockfile.get('b')).toEqual([ { range: '2.0.x', version: '2.0.1' }, + { range: '^2.0.1', version: '2.0.1' }, { range: '^2', version: '2.0.0' }, ]); expect(lockfile.toString()).toBe(mockANew); diff --git a/packages/cli/src/lib/versioning/Lockfile.ts b/packages/cli/src/lib/versioning/Lockfile.ts index 278778b6a6..016593ea8d 100644 --- a/packages/cli/src/lib/versioning/Lockfile.ts +++ b/packages/cli/src/lib/versioning/Lockfile.ts @@ -122,7 +122,7 @@ export class Lockfile { for (const [key, value] of Object.entries(data)) { if (SPECIAL_OBJECT_KEYS.includes(key)) continue; - const [, name, range] = ENTRY_PATTERN.exec(key) ?? []; + const [, name, ranges] = ENTRY_PATTERN.exec(key) ?? []; if (!name) { throw new Error(`Failed to parse yarn.lock entry '${key}'`); } @@ -132,7 +132,15 @@ export class Lockfile { queries = []; packages.set(name, queries); } - queries.push({ range, version: value.version }); + for (let range of ranges.split(/\s*,\s*/)) { + if (range.startsWith(`${name}@`)) { + range = range.slice(`${name}@`.length); + } + if (range.startsWith('npm:')) { + range = range.slice('npm:'.length); + } + queries.push({ range, version: value.version }); + } } return new Lockfile(path, packages, data, legacy); diff --git a/packages/cli/src/tests/yaml-fixture.yaml b/packages/cli/src/tests/yaml-fixture.yaml new file mode 100644 index 0000000000..e93744d8dc --- /dev/null +++ b/packages/cli/src/tests/yaml-fixture.yaml @@ -0,0 +1,2 @@ +x: + y: 'z' diff --git a/packages/cli/src/tests/yaml.test.ts b/packages/cli/src/tests/yaml.test.ts new file mode 100644 index 0000000000..cb96fdf954 --- /dev/null +++ b/packages/cli/src/tests/yaml.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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 yaml from './yaml-fixture.yaml'; + +describe('tests', () => { + it('should load yaml', () => { + expect(yaml).toEqual({ x: { y: 'z' } }); + }); +}); diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index fcc903b148..0a72a5d415 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -30,18 +30,18 @@ "dependencies": { "@backstage/backend-common": "{{versionQuery '@backstage/backend-common'}}", "@backstage/config": "{{versionQuery '@backstage/config'}}", - "@types/express": "{{versionQuery '@types/express' '4.17.6'}}", - "express": "{{versionQuery 'express' '4.17.1'}}", + "@types/express": "{{versionQuery '@types/express' '4.17.13'}}", + "express": "{{versionQuery 'express' '4.18.1'}}", "express-promise-router": "{{versionQuery 'express-promise-router' '4.1.0'}}", - "winston": "{{versionQuery 'winston' '3.2.1'}}", + "winston": "{{versionQuery 'winston' '3.8.1'}}", "node-fetch": "{{versionQuery 'node-fetch' '2.6.7'}}", "yn": "{{versionQuery 'yn' '4.0.0'}}" }, "devDependencies": { "@backstage/cli": "{{versionQuery '@backstage/cli'}}", - "@types/supertest": "{{versionQuery '@types/supertest' '2.0.8'}}", - "supertest": "{{versionQuery 'supertest' '4.0.2'}}", - "msw": "{{versionQuery 'msw' '0.46.0'}}" + "@types/supertest": "{{versionQuery '@types/supertest' '2.0.12'}}", + "supertest": "{{versionQuery 'supertest' '6.2.4'}}", + "msw": "{{versionQuery 'msw' '0.47.0'}}" }, "files": [ "dist" diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index a1c1245b38..c60ba63f2b 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -48,7 +48,7 @@ "@testing-library/react": "{{versionQuery '@testing-library/react' '12.1.3'}}", "@testing-library/user-event": "{{versionQuery '@testing-library/user-event' '14.0.0'}}", "@types/node": "{{versionQuery '@types/node' '16.11.26'}}", - "msw": "{{versionQuery 'msw' '0.46.0'}}", + "msw": "{{versionQuery 'msw' '0.47.0'}}", "cross-fetch": "{{versionQuery 'cross-fetch' '3.1.5'}}" }, "files": [ diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index 43b2bb2d8a..957910660a 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/codemods +## 0.1.39 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + ## 0.1.39-next.0 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 2e7bd09dbc..4be7e5a31c 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.39-next.0", + "version": "0.1.39", "publishConfig": { "access": "public", "main": "dist/index.cjs.js" @@ -33,13 +33,13 @@ "backstage-codemods": "bin/backstage-codemods" }, "dependencies": { - "@backstage/cli-common": "^0.1.10-next.0", + "@backstage/cli-common": "workspace:^", "chalk": "^4.0.0", "jscodeshift": "^0.13.0", "jscodeshift-add-imports": "^1.0.10" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/jscodeshift": "^0.11.0", "@types/node": "^16.11.26", "commander": "^9.1.0", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index c46763bccd..f3cc482178 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/config-loader +## 1.1.4 + +### Patch Changes + +- 5ecca7e44b: No longer log when reloading remote config. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 1.1.4-next.2 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 3f0644e3d6..15d622cf5d 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": "1.1.4-next.2", + "version": "1.1.4", "publishConfig": { "access": "public", "main": "dist/index.cjs.js", @@ -33,10 +33,10 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/cli-common": "^0.1.10-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/types": "^1.0.0", + "@backstage/cli-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/types": "workspace:^", "@types/json-schema": "^7.0.6", "ajv": "^8.10.0", "chokidar": "^3.5.2", @@ -50,7 +50,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/json-schema-merge-allof": "^0.6.0", "@types/mock-fs": "^4.10.0", "@types/node": "^16.11.26", diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 9b9ea96dd1..7774d1aa5c 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/config +## 1.0.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + ## 1.0.2-next.0 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index 9610495455..fec1fa88ad 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "1.0.2-next.0", + "version": "1.0.2", "publishConfig": { "access": "public", "main": "dist/index.cjs.js", @@ -32,12 +32,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/types": "^1.0.0", + "@backstage/types": "workspace:^", "lodash": "^4.17.21" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", "@types/node": "^16.0.0" }, "files": [ diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 47740e9e7b..5ec66460c6 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/core-app-api +## 1.1.0 + +### Minor Changes + +- a448fea691: Updated the routing system to be compatible with React Router v6 stable. + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f9ec4e46e3: When using React Router v6 stable, it is now possible for components within the `Route` element tree to have `path` props, although they will be ignored. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 744fea158b: Added `getSystemIcons()` function to the `AppContext` available through `useApp` that will pull a list of all the icons that have been registered in the App. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 8448b53dd6: Clarify that the `WebStorage` observable returns `JsonValue` items. +- 70299c99d5: Updated `FlatRoutes` to be compatible with React Router v6 stable. +- e9d40ebf54: If you'd like to send analytics events to multiple implementations, you may now + do so using the `MultipleAnalyticsApi` implementation provided by this package. + + ```tsx + import { MultipleAnalyticsApi } from '@backstage/core-app-api'; + import { + analyticsApiRef, + configApiRef, + storageApiRef, + identityApiRef, + } from '@internal/backstage/core-plugin-api'; + import { CustomAnalyticsApi } from '@internal/analytics'; + import { VendorAnalyticsApi } from '@vendor/analytics'; + + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef, identityApi: identityApiRef, storageApi: storageApiRef }, + factory: ({ configApi, identityApi, storageApi }) => + MultipleAnalyticsApi.fromApis([ + VendorAnalyticsApi.fromConfig(configApi, { identityApi }), + CustomAnalyticsApi.fromConfig(configApi, { identityApi, storageApi }), + ]), + }), + ``` + +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + ## 1.1.0-next.3 ### Patch Changes diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index f2bc4b6665..4d1db6fde1 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -556,7 +556,9 @@ export class WebStorage implements StorageApi { // (undocumented) get(key: string): T | undefined; // (undocumented) - observe$(key: string): Observable>; + observe$( + key: string, + ): Observable>; // (undocumented) remove(key: string): Promise; // (undocumented) diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 8532997477..d3f8f04fc2 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": "1.1.0-next.3", + "version": "1.1.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -32,10 +32,10 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/types": "^1.0.0", - "@backstage/version-bridge": "^1.0.1", + "@backstage/config": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/types": "workspace:^", + "@backstage/version-bridge": "workspace:^", "@types/prop-types": "^15.7.3", "prop-types": "^15.7.2", "react-use": "^17.2.4", @@ -48,8 +48,8 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts b/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts index ac3d3f20f2..b0cbfbd883 100644 --- a/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts +++ b/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts @@ -86,7 +86,9 @@ export class WebStorage implements StorageApi { this.notifyChanges(key); } - observe$(key: string): Observable> { + observe$( + key: string, + ): Observable> { return this.observable.filter(({ key: messageKey }) => messageKey === key); } diff --git a/packages/core-app-api/src/apis/system/ApiProvider.test.tsx b/packages/core-app-api/src/apis/system/ApiProvider.test.tsx index c08674adfe..a6118f0f68 100644 --- a/packages/core-app-api/src/apis/system/ApiProvider.test.tsx +++ b/packages/core-app-api/src/apis/system/ApiProvider.test.tsx @@ -118,9 +118,10 @@ describe('ApiProvider', () => { }).toThrow(/^API context is not available/); }).error, ).toEqual([ - expect.stringMatching( - /^Error: Uncaught \[Error: API context is not available/, - ), + expect.objectContaining({ + detail: new Error('API context is not available'), + type: 'unhandled exception', + }), expect.stringMatching( /^The above error occurred in the component/, ), @@ -133,9 +134,10 @@ describe('ApiProvider', () => { }).toThrow(/^API context is not available/); }).error, ).toEqual([ - expect.stringMatching( - /^Error: Uncaught \[Error: API context is not available/, - ), + expect.objectContaining({ + detail: new Error('API context is not available'), + type: 'unhandled exception', + }), expect.stringMatching( /^The above error occurred in the component/, ), @@ -154,9 +156,10 @@ describe('ApiProvider', () => { }).toThrow('No implementation available for apiRef{x}'); }).error, ).toEqual([ - expect.stringMatching( - /^Error: Uncaught \[Error: No implementation available for apiRef{x}\]/, - ), + expect.objectContaining({ + detail: new Error('No implementation available for apiRef{x}'), + type: 'unhandled exception', + }), expect.stringMatching( /^The above error occurred in the component/, ), @@ -173,9 +176,10 @@ describe('ApiProvider', () => { }).toThrow('No implementation available for apiRef{x}'); }).error, ).toEqual([ - expect.stringMatching( - /^Error: Uncaught \[Error: No implementation available for apiRef{x}\]/, - ), + expect.objectContaining({ + detail: new Error('No implementation available for apiRef{x}'), + type: 'unhandled exception', + }), expect.stringMatching( /^The above error occurred in the component/, ), diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 64c5ddc502..66284b6bc6 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/core-components +## 0.11.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- d3737da337: Export `HeaderActionMenu` and expose default `Table` icons via `Table.icons` +- 7d47def9c4: Removed dependency on `@types/jest`. +- b29c44d895: Adds code to generate ids for headers parsed through the MarkdownContent component. +- 37e8c5e128: The `RoutedTabs` component has been updated to be compatible with React Router v6 stable. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 13f23f6510: Allow custom images in the empty state component +- 023d14c52b: Fix overflow bug on MicDrop image for 404 page by moving the image and making it relative rather than absolute +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.11.1-next.3 ### Patch Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 3dc4e2848e..99d18b9b12 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -21,10 +21,12 @@ import { CSSProperties } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; +import { Icons } from '@material-table/core'; import { IdentityApi } from '@backstage/core-plugin-api'; import { LinearProgressProps } from '@material-ui/core/LinearProgress'; import { LinkProps as LinkProps_2 } from '@material-ui/core/Link'; import { LinkProps as LinkProps_3 } from 'react-router-dom'; +import { ListItemTextProps } from '@material-ui/core/ListItemText'; import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import { MaterialTableProps } from '@material-table/core'; import { NavLinkProps } from 'react-router-dom'; @@ -432,6 +434,23 @@ export function GroupIcon(props: IconComponentProps): JSX.Element; // @public export function Header(props: PropsWithChildren): JSX.Element; +// @public (undocumented) +export function HeaderActionMenu(props: HeaderActionMenuProps): JSX.Element; + +// @public (undocumented) +export type HeaderActionMenuItem = { + label?: ListItemTextProps['primary']; + secondaryLabel?: ListItemTextProps['secondary']; + icon?: ReactElement; + disabled?: boolean; + onClick?: (event: React_2.MouseEvent) => void; +}; + +// @public (undocumented) +export type HeaderActionMenuProps = { + actionItems: HeaderActionMenuItem[]; +}; + // @public (undocumented) export type HeaderClassKey = | 'header' @@ -1333,6 +1352,12 @@ export type TabIconClassKey = 'root'; // @public (undocumented) export function Table(props: TableProps): JSX.Element; +// @public (undocumented) +export namespace Table { + var // (undocumented) + icons: Readonly; +} + // Warning: (ae-missing-release-tag) "TableClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/package.json b/packages/core-components/package.json index a32961eece..2c861c145a 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.11.1-next.3", + "version": "0.11.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -32,11 +32,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/theme": "^0.2.16", - "@backstage/version-bridge": "^1.0.1", + "@backstage/config": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/version-bridge": "workspace:^", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -78,9 +78,9 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/packages/core-components/src/components/TabbedLayout/TabbedLayout.test.tsx b/packages/core-components/src/components/TabbedLayout/TabbedLayout.test.tsx index b700739a8b..e103fc2f46 100644 --- a/packages/core-components/src/components/TabbedLayout/TabbedLayout.test.tsx +++ b/packages/core-components/src/components/TabbedLayout/TabbedLayout.test.tsx @@ -47,9 +47,11 @@ describe('TabbedLayout', () => { }); expect(error).toEqual([ - expect.stringMatching( - /Child of TabbedLayout must be an TabbedLayout.Route/, - ), + expect.objectContaining({ + detail: new Error( + 'Child of TabbedLayout must be an TabbedLayout.Route', + ), + }), expect.stringMatching( /The above error occurred in the component/, ), diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 65edb5b69b..49538b680a 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -292,6 +292,9 @@ export function TableToolbar(toolbarProps: { ); } +/** + * @public + */ export function Table(props: TableProps) { const { data, @@ -519,3 +522,5 @@ export function Table(props: TableProps) { ); } + +Table.icons = Object.freeze(tableIcons); diff --git a/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx b/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx index 361ad123bf..6a16224a02 100644 --- a/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx +++ b/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx @@ -67,7 +67,9 @@ describe('', () => { }); expect(error).toEqual([ - expect.stringMatching(/^Error: Uncaught \[Error: Bomb\]/), + expect.objectContaining({ + detail: new Error('Bomb'), + }), expect.stringMatching( /^The above error occurred in the component:/, ), diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 928a3eb0d4..658c06de2a 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -70,7 +70,6 @@ export function ErrorPage(props: IErrorPageProps) { return ( - + ); } diff --git a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx index efa101b23b..aacb44079a 100644 --- a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx +++ b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx @@ -22,12 +22,10 @@ const useStyles = makeStyles( theme => ({ micDrop: { maxWidth: '60%', - position: 'absolute', bottom: theme.spacing(2), right: theme.spacing(2), [theme.breakpoints.down('xs')]: { maxWidth: '96%', - position: 'relative', bottom: 'unset', right: 'unset', margin: `${theme.spacing(10)}px auto ${theme.spacing(4)}px`, diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx index 3337571d4e..92ce241aa8 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.test.tsx @@ -60,7 +60,7 @@ describe('', () => { ); }); - it('Test wrapper, and secondary label', async () => { + it('Secondary label', async () => { const onClickFunction = jest.fn(); const rendered = await renderInTestApp( ', () => { { label: 'Some label', secondaryLabel: 'Secondary label', - WrapperComponent: ({ children }) => ( - - ), + onClick: onClickFunction, }, ]} />, diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx index d8dc398ce3..e164244ea7 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { Fragment, ReactElement, ComponentType } from 'react'; +import React, { Fragment, ReactElement } from 'react'; import IconButton from '@material-ui/core/IconButton'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; @@ -23,15 +23,17 @@ import ListItemText, { ListItemTextProps, } from '@material-ui/core/ListItemText'; import Popover from '@material-ui/core/Popover'; -import { VerticalMenuIcon } from './VerticalMenuIcon'; +import MoreVert from '@material-ui/icons/MoreVert'; -type ActionItemProps = { +/** + * @public + */ +export type HeaderActionMenuItem = { label?: ListItemTextProps['primary']; secondaryLabel?: ListItemTextProps['secondary']; icon?: ReactElement; disabled?: boolean; - onClick?: (event: React.MouseEvent) => void; - WrapperComponent?: ComponentType; + onClick?: (event: React.MouseEvent) => void; }; const ActionItem = ({ @@ -40,10 +42,9 @@ const ActionItem = ({ icon, disabled = false, onClick, - WrapperComponent = React.Fragment, -}: ActionItemProps) => { +}: HeaderActionMenuItem) => { return ( - + {icon}} - + ); }; +/** + * @public + */ export type HeaderActionMenuProps = { - actionItems: ActionItemProps[]; + actionItems: HeaderActionMenuItem[]; }; +/** + * @public + */ export function HeaderActionMenu(props: HeaderActionMenuProps) { const { actionItems } = props; const [open, setOpen] = React.useState(false); @@ -84,7 +91,7 @@ export function HeaderActionMenu(props: HeaderActionMenuProps) { padding: 0, }} > - + { describe('tokenToExpiry', () => { - beforeEach(() => jest.useFakeTimers('modern')); + beforeEach(() => jest.useFakeTimers()); afterEach(() => jest.useRealTimers()); it('handles undefined', async () => { @@ -56,7 +56,7 @@ describe('ProxiedSignInIdentity', () => { }); describe('ProxiedSignInIdentity', () => { - beforeEach(() => jest.useFakeTimers('modern')); + beforeEach(() => jest.useFakeTimers()); afterEach(() => jest.useRealTimers()); const worker = setupServer(); diff --git a/packages/core-components/src/layout/index.ts b/packages/core-components/src/layout/index.ts index 9bb7fbaa26..4b594f0e94 100644 --- a/packages/core-components/src/layout/index.ts +++ b/packages/core-components/src/layout/index.ts @@ -20,6 +20,7 @@ export * from './ContentHeader'; export * from './ErrorBoundary'; export * from './ErrorPage'; export * from './Header'; +export * from './HeaderActionMenu'; export * from './HeaderLabel'; export * from './HeaderTabs'; export * from './HomepageTimer'; diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 0889d0d6e9..9671062a50 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/core-plugin-api +## 1.0.6 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 744fea158b: Added `getSystemIcons()` function to the `AppContext` available through `useApp` that will pull a list of all the icons that have been registered in the App. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/config@1.0.2 + ## 1.0.6-next.3 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 5d013f6e87..71372e26f1 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.0.6-next.3", + "version": "1.0.6", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -33,9 +33,9 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/types": "^1.0.0", - "@backstage/version-bridge": "^1.0.1", + "@backstage/config": "workspace:^", + "@backstage/types": "workspace:^", + "@backstage/version-bridge": "workspace:^", "history": "^5.0.0", "prop-types": "^15.7.2", "zen-observable": "^0.8.15" @@ -46,9 +46,9 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/packages/core-plugin-api/src/extensions/extensions.test.tsx b/packages/core-plugin-api/src/extensions/extensions.test.tsx index 0601c01a17..3eca133608 100644 --- a/packages/core-plugin-api/src/extensions/extensions.test.tsx +++ b/packages/core-plugin-api/src/extensions/extensions.test.tsx @@ -117,7 +117,7 @@ describe('extensions', () => { render(); }); screen.getByText('Error in my-plugin'); - expect(errors[0]).toMatch('Test error'); + expect(errors[0]).toMatchObject({ detail: new Error('Test error') }); }); it('should wrap extended component with analytics context', async () => { diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index e131c59b6c..c3b9164858 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,197 @@ # @backstage/create-app +## 0.4.31 + +### Patch Changes + +- 6ff94d60d5: Removed usage of the deprecated `diff` command in the root `package.json`. + + To make this change in an existing app, make the following change in the root `package.json`: + + ```diff + - "diff": "lerna run diff --", + ``` + +- c1f1a4c760: The Backstage packages and plugins have all been updated to support React Router v6 stable. The `create-app` template has not been migrated yet, but if you want to migrate your own app or plugins, check out the [migration guide](https://backstage.io/docs/tutorials/react-router-stable-migration). +- e83de28e36: Fix typo in the documentation +- 7d47def9c4: Removed dependency on `@types/jest`. +- 208d6780c9: The `packages/backend/Dockerfile` received a couple of updates, it now looks as follows: + + ```Dockerfile + FROM node:16-bullseye-slim + + # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, + # in which case you should also move better-sqlite3 to "devDependencies" in package.json. + RUN apt-get update && \ + apt-get install -y --no-install-recommends libsqlite3-dev python3 build-essential && \ + rm -rf /var/lib/apt/lists/* && \ + yarn config set python /usr/bin/python3 + + # From here on we use the least-privileged `node` user to run the backend. + USER node + WORKDIR /app + + # This switches many Node.js dependencies to production mode. + ENV NODE_ENV production + + # Copy repo skeleton first, to avoid unnecessary docker cache invalidation. + # The skeleton contains the package.json of each package in the monorepo, + # and along with yarn.lock and the root package.json, that's enough to run yarn install. + COPY --chown=node:node yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ + RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz + + RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" + + # Then copy the rest of the backend bundle, along with any other files we might want. + COPY --chown=node:node packages/backend/dist/bundle.tar.gz app-config*.yaml ./ + RUN tar xzf bundle.tar.gz && rm bundle.tar.gz + + CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] + ``` + + The two notable changes are that a `USER node` instruction has been added and the ordering of instructions has been changed accordingly. This means that the app will now be running using the least-privileged `node` user. In order for this to work we now need to make sure that all app files are owned by the `node` user, which we do by adding the `--chown=node:node` option to the `COPY` instructions. + + The second change is the addition of `ENV NODE_ENV production`, which ensured that all Node.js modules run in production mode. If you apply this change to an existing app, note that one of the more significant changes is that this switches the log formatting to use the default production format, JSON. Rather than your log lines looking like this: + + ```log + 2022-08-10T11:36:05.478Z catalog info Performing database migration type=plugin + ``` + + They will now look like this: + + ```log + {"level":"info","message":"Performing database migration","plugin":"catalog","service":"backstage","type":"plugin"} + ``` + + If you wish to keep the existing format, you can override this change by applying the following change to `packages/backend/src/index.ts`: + + ```diff + getRootLogger, + + setRootLogger, + + createRootLogger, + + coloredFormat, + useHotMemoize, + ... + ServerTokenManager, + } from '@backstage/backend-common'; + + ... + + async function main() { + + setRootLogger(createRootLogger({ format: coloredFormat })); + + + const config = await loadBackendConfig({ + ``` + +- 49416194e8: Adds `IdentityApi` configuration to `create-app` scaffolding templates. + + To migrate to the new `IdentityApi`, edit the `packages/backend/src/index.ts` adding the following import: + + ```typescript + import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; + ``` + + Use the factory function to create an `IdentityApi` in the `makeCreateEnv` function and return it from the + function as follows: + + ```typescript + function makeCreateEnv(config: Config) { + ... + const identity = DefaultIdentityClient.create({ + discovery, + }); + ... + + return { + ..., + identity + } + } + ``` + + Backend plugins can be upgraded to work with this new `IdentityApi`. + + Add `identity` to the `RouterOptions` type. + + ```typescript + export interface RouterOptions { + ... + identity: IdentityApi; + } + ``` + + Then you can use the `IdentityApi` from the plugin. + + ```typescript + export async function createRouter( + options: RouterOptions, + ): Promise { + const { identity } = options; + + router.get('/user', async (req, res) => { + const user = await identity.getIdentity({ request: req }); + ... + ``` + +- 8d886dd33e: Added `yarn new` as one of the scripts installed by default, which calls `backstage-cli new`. This script replaces `create-plugin`, which you can now remove if you want to. It is kept in the `create-app` template for backwards compatibility. + + The `remove-plugin` command has been removed, as it has been removed from the Backstage CLI. + + To apply these changes to an existing app, make the following change to the root `package.json`: + + ```diff + - "remove-plugin": "backstage-cli remove-plugin" + + "new": "backstage-cli new --scope internal" + ``` + +- c3c90280be: The options part of `DatabaseManager.fromConfig` now accepts an optional logger + field. You may want to supply that logger in your backend initialization code to + ensure that you can get relevant logging data when things happen related to the + connection pool. + + In `packages/backend/src/index.ts`: + + ```diff + function makeCreateEnv(config: Config) { + const root = getRootLogger(); + ... + - const databaseManager = DatabaseManager.fromConfig(config); + + const databaseManager = DatabaseManager.fromConfig(config, { logger: root }); + ``` + +- a578558180: Updated the root `package.json` to use the new `backstage-cli repo clean` command. + + To apply this change to an existing project, make the following change to the root `package.json`: + + ```diff + - "clean": "backstage-cli clean && lerna run clean", + + "clean": "backstage-cli repo clean", + ``` + +- c0a08fd08c: Added `EntityLinksCard` to the system `EntityPage`. + + For an existing installation where you want to display the links card for entity pages of kind `system` you should make the following adjustment to `packages/app/src/components/catalog/EntityPage.tsx` + + ```diff + const systemPage = ( + ... + + + + + + + + + + - + + + + + ... + ); + ``` + +- Updated dependencies + - @backstage/cli-common@0.1.10 + ## 0.4.31-next.3 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index d059446638..bb65d3b4ca 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.4.31-next.3", + "version": "0.4.31", "publishConfig": { "access": "public" }, @@ -29,10 +29,10 @@ "clean": "backstage-cli package clean", "prepack": "node scripts/prepack.js", "postpack": "node scripts/postpack.js", - "start": "nodemon --" + "start": "yarn nodemon --" }, "dependencies": { - "@backstage/cli-common": "^0.1.10-next.0", + "@backstage/cli-common": "workspace:^", "chalk": "^4.0.0", "commander": "^9.1.0", "fs-extra": "10.1.0", @@ -42,12 +42,13 @@ "recursive-readdir": "^2.2.2" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/fs-extra": "^9.0.1", "@types/inquirer": "^8.1.3", "@types/node": "^16.11.26", "@types/recursive-readdir": "^2.2.0", "mock-fs": "^5.1.1", + "nodemon": "^2.0.2", "ts-node": "^10.0.0" }, "nodemonConfig": { diff --git a/packages/create-app/src/createApp.test.ts b/packages/create-app/src/createApp.test.ts index d339460243..8e7b3bae7d 100644 --- a/packages/create-app/src/createApp.test.ts +++ b/packages/create-app/src/createApp.test.ts @@ -28,17 +28,6 @@ jest.mock('./lib/versions', () => ({ packageVersions: { root: '1.0.0' }, })); -beforeAll(() => { - mockFs({ - [`${__dirname}/package.json`]: '', // required by `findPaths(__dirname)` - 'templates/': mockFs.load(path.resolve(__dirname, '../templates/')), - }); -}); - -afterAll(() => { - mockFs.restore(); -}); - const promptMock = jest.spyOn(inquirer, 'prompt'); const checkPathExistsMock = jest.spyOn(tasks, 'checkPathExistsTask'); const templatingMock = jest.spyOn(tasks, 'templatingTask'); @@ -51,6 +40,17 @@ const moveAppMock = jest.spyOn(tasks, 'moveAppTask'); const buildAppMock = jest.spyOn(tasks, 'buildAppTask'); describe('command entrypoint', () => { + beforeAll(() => { + mockFs({ + [`${__dirname}/package.json`]: '', // required by `findPaths(__dirname)` + 'templates/': mockFs.load(path.resolve(__dirname, '../templates/')), + }); + }); + + afterAll(() => { + mockFs.restore(); + }); + beforeEach(() => { promptMock.mockResolvedValueOnce({ name: 'MyApp', diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index d2efe81b90..decc89fbd4 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -86,185 +86,191 @@ jest.mock('./versions', () => ({ }, })); -beforeEach(() => { - mockFs({ - 'projects/my-module.ts': '', - 'projects/dir/my-file.txt': '', - 'tmp/mockApp/.gitignore': '', - 'tmp/mockApp/package.json': '', - 'tmp/mockApp/packages/app/package.json': '', - // load templates into mock filesystem - 'templates/': mockFs.load(path.resolve(__dirname, '../../templates/')), - }); -}); - -afterEach(() => { - mockFs.restore(); -}); - -describe('checkAppExistsTask', () => { - it('should do nothing if the directory does not exist', async () => { - const dir = 'projects/'; - const name = 'MyNewApp'; - await expect(checkAppExistsTask(dir, name)).resolves.not.toThrow(); +describe('tasks', () => { + beforeEach(() => { + mockFs({ + 'projects/my-module.ts': '', + 'projects/dir/my-file.txt': '', + 'tmp/mockApp/.gitignore': '', + 'tmp/mockApp/package.json': '', + 'tmp/mockApp/packages/app/package.json': '', + // load templates into mock filesystem + 'templates/': mockFs.load(path.resolve(__dirname, '../../templates/')), + }); }); - it('should throw an error when a file of the same name exists', async () => { - const dir = 'projects/'; - const name = 'my-module.ts'; - await expect(checkAppExistsTask(dir, name)).rejects.toThrow( - 'already exists', - ); + afterEach(() => { + mockFs.restore(); }); - it('should throw an error when a directory of the same name exists', async () => { - const dir = 'projects/'; - const name = 'dir'; - await expect(checkAppExistsTask(dir, name)).rejects.toThrow( - 'already exists', - ); - }); -}); - -describe('checkPathExistsTask', () => { - it('should create a directory at the given path', async () => { - const appDir = 'projects/newProject'; - await expect(checkPathExistsTask(appDir)).resolves.not.toThrow(); - expect(fs.existsSync(appDir)).toBe(true); - }); - - it('should do nothing if a directory of the same name exists', async () => { - const appDir = 'projects/dir'; - await expect(checkPathExistsTask(appDir)).resolves.not.toThrow(); - expect(fs.existsSync(appDir)).toBe(true); - }); - - it('should fail if a file of the same name exists', async () => { - await expect(checkPathExistsTask('projects/my-module.ts')).rejects.toThrow( - 'already exists', - ); - }); -}); - -describe('createTemporaryAppFolderTask', () => { - it('should create a directory at a given path', async () => { - const tempDir = 'projects/tmpFolder'; - await expect(createTemporaryAppFolderTask(tempDir)).resolves.not.toThrow(); - expect(fs.existsSync(tempDir)).toBe(true); - }); - - it('should fail if a directory of the same name exists', async () => { - const tempDir = 'projects/dir'; - await expect(createTemporaryAppFolderTask(tempDir)).rejects.toThrow( - 'file already exists', - ); - }); - - it('should fail if a file of the same name exists', async () => { - const tempDir = 'projects/dir/my-file.txt'; - await expect(createTemporaryAppFolderTask(tempDir)).rejects.toThrow( - 'file already exists', - ); - }); -}); - -describe('buildAppTask', () => { - it('should change to `appDir` and run `yarn install` and `yarn tsc`', async () => { - const mockChdir = jest.spyOn(process, 'chdir'); - const mockExec = child_process.exec as unknown as jest.MockedFunction< - ( - command: string, - callback: (error: null, stdout: string, stderr: string) => void, - ) => void - >; - - // requires callback implementation to support `promisify` wrapper - // https://stackoverflow.com/a/60579617/10044859 - mockExec.mockImplementation((_command, callback) => { - callback(null, 'standard out', 'standard error'); + describe('checkAppExistsTask', () => { + it('should do nothing if the directory does not exist', async () => { + const dir = 'projects/'; + const name = 'MyNewApp'; + await expect(checkAppExistsTask(dir, name)).resolves.not.toThrow(); }); - const appDir = 'projects/dir'; - await expect(buildAppTask(appDir)).resolves.not.toThrow(); - expect(mockChdir).toHaveBeenCalledTimes(2); - expect(mockChdir).toHaveBeenNthCalledWith(1, appDir); - expect(mockChdir).toHaveBeenNthCalledWith(2, appDir); - expect(mockExec).toHaveBeenCalledTimes(2); - expect(mockExec).toHaveBeenNthCalledWith( - 1, - 'yarn install', - expect.any(Function), - ); - expect(mockExec).toHaveBeenNthCalledWith( - 2, - 'yarn tsc', - expect.any(Function), - ); - }); - - it('should fail if project directory does not exist', async () => { - const appDir = 'projects/missingProject'; - await expect(buildAppTask(appDir)).rejects.toThrow( - 'no such file or directory', - ); - }); -}); - -describe('moveAppTask', () => { - const tempDir = 'tmp/mockApp/'; - const id = 'myApp'; - - it('should move all files in the temp dir to the target dir', async () => { - const destination = 'projects/mockApp'; - await moveAppTask(tempDir, destination, id); - expect(fs.existsSync('projects/mockApp/.gitignore')).toBe(true); - expect(fs.existsSync('projects/mockApp/package.json')).toBe(true); - expect(fs.existsSync('projects/mockApp/packages/app/package.json')).toBe( - true, - ); - }); - - it('should fail to move files if destination already exists', async () => { - const destination = 'projects'; - await expect(moveAppTask(tempDir, destination, id)).rejects.toThrow( - 'dest already exists', - ); - }); - - it('should remove temporary files if move succeeded', async () => { - const destination = 'projects/mockApp'; - await moveAppTask(tempDir, destination, id); - expect(fs.existsSync('tmp/mockApp')).toBe(false); - }); - - it('should remove temporary files if move failed', async () => { - const destination = 'projects'; - await expect(moveAppTask(tempDir, destination, id)).rejects.toThrow(); - expect(fs.existsSync('tmp/mockApp')).toBe(false); - }); -}); - -describe('templatingTask', () => { - it('should generate a project populating context parameters', async () => { - const templateDir = 'templates/default-app'; - const destinationDir = 'templatedApp'; - const context = { - name: 'SuperCoolBackstageInstance', - dbTypeSqlite: true, - }; - await templatingTask(templateDir, destinationDir, context); - expect(fs.existsSync('templatedApp/package.json')).toBe(true); - expect(fs.existsSync('templatedApp/.dockerignore')).toBe(true); - await expect(fs.readJson('templatedApp/backstage.json')).resolves.toEqual({ - version: '1.2.3', + it('should throw an error when a file of the same name exists', async () => { + const dir = 'projects/'; + const name = 'my-module.ts'; + await expect(checkAppExistsTask(dir, name)).rejects.toThrow( + 'already exists', + ); + }); + + it('should throw an error when a directory of the same name exists', async () => { + const dir = 'projects/'; + const name = 'dir'; + await expect(checkAppExistsTask(dir, name)).rejects.toThrow( + 'already exists', + ); + }); + }); + + describe('checkPathExistsTask', () => { + it('should create a directory at the given path', async () => { + const appDir = 'projects/newProject'; + await expect(checkPathExistsTask(appDir)).resolves.not.toThrow(); + expect(fs.existsSync(appDir)).toBe(true); + }); + + it('should do nothing if a directory of the same name exists', async () => { + const appDir = 'projects/dir'; + await expect(checkPathExistsTask(appDir)).resolves.not.toThrow(); + expect(fs.existsSync(appDir)).toBe(true); + }); + + it('should fail if a file of the same name exists', async () => { + await expect( + checkPathExistsTask('projects/my-module.ts'), + ).rejects.toThrow('already exists'); + }); + }); + + describe('createTemporaryAppFolderTask', () => { + it('should create a directory at a given path', async () => { + const tempDir = 'projects/tmpFolder'; + await expect( + createTemporaryAppFolderTask(tempDir), + ).resolves.not.toThrow(); + expect(fs.existsSync(tempDir)).toBe(true); + }); + + it('should fail if a directory of the same name exists', async () => { + const tempDir = 'projects/dir'; + await expect(createTemporaryAppFolderTask(tempDir)).rejects.toThrow( + 'file already exists', + ); + }); + + it('should fail if a file of the same name exists', async () => { + const tempDir = 'projects/dir/my-file.txt'; + await expect(createTemporaryAppFolderTask(tempDir)).rejects.toThrow( + 'file already exists', + ); + }); + }); + + describe('buildAppTask', () => { + it('should change to `appDir` and run `yarn install` and `yarn tsc`', async () => { + const mockChdir = jest.spyOn(process, 'chdir'); + const mockExec = child_process.exec as unknown as jest.MockedFunction< + ( + command: string, + callback: (error: null, stdout: string, stderr: string) => void, + ) => void + >; + + // requires callback implementation to support `promisify` wrapper + // https://stackoverflow.com/a/60579617/10044859 + mockExec.mockImplementation((_command, callback) => { + callback(null, 'standard out', 'standard error'); + }); + + const appDir = 'projects/dir'; + await expect(buildAppTask(appDir)).resolves.not.toThrow(); + expect(mockChdir).toHaveBeenCalledTimes(2); + expect(mockChdir).toHaveBeenNthCalledWith(1, appDir); + expect(mockChdir).toHaveBeenNthCalledWith(2, appDir); + expect(mockExec).toHaveBeenCalledTimes(2); + expect(mockExec).toHaveBeenNthCalledWith( + 1, + 'yarn install', + expect.any(Function), + ); + expect(mockExec).toHaveBeenNthCalledWith( + 2, + 'yarn tsc', + expect.any(Function), + ); + }); + + it('should fail if project directory does not exist', async () => { + const appDir = 'projects/missingProject'; + await expect(buildAppTask(appDir)).rejects.toThrow( + 'no such file or directory', + ); + }); + }); + + describe('moveAppTask', () => { + const tempDir = 'tmp/mockApp/'; + const id = 'myApp'; + + it('should move all files in the temp dir to the target dir', async () => { + const destination = 'projects/mockApp'; + await moveAppTask(tempDir, destination, id); + expect(fs.existsSync('projects/mockApp/.gitignore')).toBe(true); + expect(fs.existsSync('projects/mockApp/package.json')).toBe(true); + expect(fs.existsSync('projects/mockApp/packages/app/package.json')).toBe( + true, + ); + }); + + it('should fail to move files if destination already exists', async () => { + const destination = 'projects'; + await expect(moveAppTask(tempDir, destination, id)).rejects.toThrow( + 'dest already exists', + ); + }); + + it('should remove temporary files if move succeeded', async () => { + const destination = 'projects/mockApp'; + await moveAppTask(tempDir, destination, id); + expect(fs.existsSync('tmp/mockApp')).toBe(false); + }); + + it('should remove temporary files if move failed', async () => { + const destination = 'projects'; + await expect(moveAppTask(tempDir, destination, id)).rejects.toThrow(); + expect(fs.existsSync('tmp/mockApp')).toBe(false); + }); + }); + + describe('templatingTask', () => { + it('should generate a project populating context parameters', async () => { + const templateDir = 'templates/default-app'; + const destinationDir = 'templatedApp'; + const context = { + name: 'SuperCoolBackstageInstance', + dbTypeSqlite: true, + }; + await templatingTask(templateDir, destinationDir, context); + expect(fs.existsSync('templatedApp/package.json')).toBe(true); + expect(fs.existsSync('templatedApp/.dockerignore')).toBe(true); + await expect(fs.readJson('templatedApp/backstage.json')).resolves.toEqual( + { + version: '1.2.3', + }, + ); + // catalog was populated with `context.name` + expect( + fs.readFileSync('templatedApp/catalog-info.yaml', 'utf-8'), + ).toContain('name: SuperCoolBackstageInstance'); + // backend dependencies include `sqlite3` from `context.SQLite` + expect( + fs.readFileSync('templatedApp/packages/backend/package.json', 'utf-8'), + ).toContain('sqlite3"'); }); - // catalog was populated with `context.name` - expect( - fs.readFileSync('templatedApp/catalog-info.yaml', 'utf-8'), - ).toContain('name: SuperCoolBackstageInstance'); - // backend dependencies include `sqlite3` from `context.SQLite` - expect( - fs.readFileSync('templatedApp/packages/backend/package.json', 'utf-8'), - ).toContain('sqlite3"'); }); }); diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index a4f8815822..dd4d937db0 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -14,8 +14,8 @@ "tsc": "tsc", "tsc:full": "tsc --skipLibCheck false --incremental false", "clean": "backstage-cli repo clean", - "test": "backstage-cli test", - "test:all": "lerna run test -- --coverage", + "test": "backstage-cli repo test", + "test:all": "backstage-cli repo test --coverage", "lint": "backstage-cli repo lint --since origin/master", "lint:all": "backstage-cli repo lint", "prettier:check": "prettier --check .", diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index d6a5086215..c060cb330c 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -6,6 +6,17 @@ "backstage": { "role": "frontend" }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "test": "backstage-cli package test", + "lint": "backstage-cli package lint", + "test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev", + "test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run", + "cy:dev": "cypress open", + "cy:run": "cypress run --browser chrome" + }, "dependencies": { "@backstage/app-defaults": "^{{version '@backstage/app-defaults'}}", "@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}", @@ -37,33 +48,22 @@ "history": "^5.0.0", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-router": "6.0.0-beta.0", - "react-router-dom": "6.0.0-beta.0", - "react-use": "^15.3.3" + "react-router": "^6.3.0", + "react-router-dom": "^6.3.0", + "react-use": "^17.2.4" }, "devDependencies": { "@backstage/test-utils": "^{{version '@backstage/test-utils'}}", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", - "@testing-library/user-event": "^12.0.7", - "@types/node": "^14.14.32", + "@testing-library/user-event": "^14.0.0", + "@types/node": "^16.11.26", "@types/react-dom": "*", "cross-env": "^7.0.0", "cypress": "^9.7.0", "eslint-plugin-cypress": "^2.10.3", "start-server-and-test": "^1.10.11" }, - "scripts": { - "start": "backstage-cli package start", - "build": "backstage-cli package build", - "clean": "backstage-cli package clean", - "test": "backstage-cli package test", - "lint": "backstage-cli package lint", - "test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev", - "test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run", - "cy:dev": "cypress open", - "cy:run": "cypress run --browser chrome" - }, "browserslist": { "production": [ ">0.2%", diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index c4877263f0..4d5ace5aa4 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -31,7 +31,7 @@ import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; import { createApp } from '@backstage/app-defaults'; import { FlatRoutes } from '@backstage/core-app-api'; import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; -import { PermissionedRoute } from '@backstage/plugin-permission-react'; +import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; const app = createApp({ @@ -58,7 +58,7 @@ const AppRouter = app.getRouter(); const routes = ( - + } /> } /> } /> - } + element={ + + + + } /> }> {searchPage} diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoFull.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoFull.tsx index c7b1c846c4..47e3b73155 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoFull.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoFull.tsx @@ -1,19 +1,3 @@ -/* - * 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 { makeStyles } from '@material-ui/core'; diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoIcon.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoIcon.tsx index 073cf6edad..7eae8c7a0b 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoIcon.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/LogoIcon.tsx @@ -1,19 +1,3 @@ -/* - * 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 { makeStyles } from '@material-ui/core'; diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index b1164a32f0..5400421e25 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -1,19 +1,3 @@ -/* - * 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, { PropsWithChildren } from 'react'; import { Link, makeStyles } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/index.ts b/packages/create-app/templates/default-app/packages/app/src/components/Root/index.ts index dff706f08f..35288815f2 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/index.ts +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/index.ts @@ -1,17 +1 @@ -/* - * 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 { Root } from './Root'; 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 d98153f660..54a05eea03 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 @@ -1,18 +1,3 @@ -/* - * 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 { Button, Grid } from '@material-ui/core'; import { diff --git a/packages/create-app/templates/default-app/yarn.lock b/packages/create-app/templates/default-app/yarn.lock new file mode 100644 index 0000000000..5ad7fe233d --- /dev/null +++ b/packages/create-app/templates/default-app/yarn.lock @@ -0,0 +1 @@ +# intentionally left empty diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index e292fde14c..0f56fbb388 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/dev-utils +## 1.0.6 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 329ed2b9c7: Fixed routing when using React Router v6 stable. +- 4c5e85ddf3: Removed the dependency and setup of `react-hot-loader`, since the `@backstage/cli` now uses `swc` with `React Refresh` instead. +- Updated dependencies + - @backstage/app-defaults@1.0.6 + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/integration-react@1.1.4 + ## 1.0.6-next.2 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index dbbdce225a..4a43a0b8b3 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": "1.0.6-next.2", + "version": "1.0.6", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -32,15 +32,15 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/app-defaults": "^1.0.6-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/test-utils": "^1.2.0-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/app-defaults": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", @@ -57,7 +57,7 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/node": "^16.0.0" }, "files": [ diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 12f430cad2..374eaa6d18 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -18,7 +18,7 @@ "license": "Apache-2.0", "main": "src/index.ts", "scripts": { - "start": "nodemon --", + "start": "yarn nodemon --", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "test:e2e": "yarn start", @@ -27,9 +27,9 @@ }, "bin": "bin/e2e-test", "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/cli-common": "^0.1.10-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/cli": "workspace:^", + "@backstage/cli-common": "workspace:^", + "@backstage/errors": "workspace:^", "@types/fs-extra": "^9.0.1", "@types/node": "^16.11.26", "@types/puppeteer": "^5.4.4", @@ -38,6 +38,7 @@ "cross-fetch": "^3.1.5", "fs-extra": "10.1.0", "handlebars": "^4.7.3", + "nodemon": "^2.0.2", "pgtools": "^0.3.0", "puppeteer": "^17.0.0", "tree-kill": "^1.2.2", diff --git a/packages/errors/CHANGELOG.md b/packages/errors/CHANGELOG.md index cbb5292829..aad6d660c3 100644 --- a/packages/errors/CHANGELOG.md +++ b/packages/errors/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/errors +## 1.1.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + ## 1.1.1-next.0 ### Patch Changes diff --git a/packages/errors/package.json b/packages/errors/package.json index ab315338bb..eb3627f1ba 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/errors", "description": "Common utilities for error handling within Backstage", - "version": "1.1.1-next.0", + "version": "1.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/types": "^1.0.0", + "@backstage/types": "workspace:^", "cross-fetch": "^3.1.5", "serialize-error": "^8.0.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index f09758bffe..29376adbab 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/integration-react +## 1.1.4 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/integration@1.3.1 + - @backstage/config@1.0.2 + ## 1.1.4-next.2 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 80ea2f029b..3f23625aac 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.4-next.2", + "version": "1.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,11 +23,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -37,9 +37,9 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 0ac5e9e5bd..3aaeeb4d48 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/integration +## 1.3.1 + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 42918e085c: Fixed bug in the `bitbucketServer` integration where token did not take precedence over supplied username and password which is described in the documentation. +- f76f22c649: Improved caching around github app tokens. + Tokens are now cached for 50 minutes, not 10. + Calls to get app installations are also included in this cache. + If you have more than one github app configured, consider adding `allowedInstallationOwners` to your apps configuration to gain the most benefit from these performance changes. +- Updated dependencies + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 1.3.1-next.2 ### Patch Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index c68bd3176c..6c2e070467 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration", "description": "Helpers for managing integrations towards external systems", - "version": "1.3.1-next.2", + "version": "1.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@octokit/auth-app": "^4.0.0", "@octokit/rest": "^19.0.3", "cross-fetch": "^3.1.5", @@ -42,9 +42,9 @@ "luxon": "^3.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/config-loader": "^1.1.4-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/config-loader": "workspace:^", + "@backstage/test-utils": "workspace:^", "@types/luxon": "^3.0.0", "msw": "^0.47.0" }, diff --git a/packages/integration/src/helpers.test.ts b/packages/integration/src/helpers.test.ts index 06feb4104e..7468c83bc6 100644 --- a/packages/integration/src/helpers.test.ts +++ b/packages/integration/src/helpers.test.ts @@ -62,7 +62,7 @@ describe('isValidHost', () => { ['[::]', true], ['[::1]', true], ['[1:2:3:4:5:6:7:8]', true], - ['1.2.3.4.5.6.7.8', true], + ['1.2.3.4.5.6.7.8', false], ['https://example.com', false], ['foo:100000', false], ['FOO', false], diff --git a/packages/release-manifests/CHANGELOG.md b/packages/release-manifests/CHANGELOG.md index b0e104511f..177ea55a5a 100644 --- a/packages/release-manifests/CHANGELOG.md +++ b/packages/release-manifests/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/release-manifests +## 0.0.6 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + ## 0.0.6-next.2 ### Patch Changes diff --git a/packages/release-manifests/package.json b/packages/release-manifests/package.json index 44c081581c..949cd2bcd5 100644 --- a/packages/release-manifests/package.json +++ b/packages/release-manifests/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/release-manifests", "description": "Helper library for receiving release manifests", - "version": "0.0.6-next.2", + "version": "0.0.6", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -35,8 +35,8 @@ "cross-fetch": "^3.1.5" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", "@types/node": "^16.0.0", "msw": "^0.47.0" }, diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index ee1e3ea184..982eb6c0de 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,23 @@ # techdocs-cli-embedded-app +## 0.2.74 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/app-defaults@1.0.6 + - @backstage/core-app-api@1.1.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog@1.5.1 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/plugin-techdocs@1.3.2 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/integration-react@1.1.4 + ## 0.2.74-next.2 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index a8d0e0cf4c..060735636b 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,25 +1,25 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.74-next.2", + "version": "0.2.74", "private": true, "backstage": { "role": "frontend" }, "bundled": true, "dependencies": { - "@backstage/app-defaults": "^1.0.6-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-catalog": "^1.5.1-next.3", - "@backstage/plugin-techdocs": "^1.3.2-next.3", - "@backstage/plugin-techdocs-react": "^1.0.4-next.2", - "@backstage/test-utils": "^1.2.0-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/app-defaults": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-catalog": "workspace:^", + "@backstage/plugin-techdocs": "workspace:^", + "@backstage/plugin-techdocs-react": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "history": "^5.0.0", @@ -30,7 +30,7 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 56fdf6afd2..377e44625b 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,17 @@ # @techdocs/cli +## 1.2.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-techdocs-node@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + ## 1.2.1-next.2 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 2ec384eb34..2e74c28c62 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.2.1-next.2", + "version": "1.2.1", "publishConfig": { "access": "public" }, @@ -36,7 +36,7 @@ "techdocs-cli": "bin/techdocs-cli" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/commander": "^2.12.2", "@types/fs-extra": "^9.0.6", "@types/http-proxy": "^1.17.4", @@ -60,11 +60,11 @@ "ext": "ts" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/cli-common": "^0.1.10-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/plugin-techdocs-node": "^1.4.0-next.2", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/cli-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-techdocs-node": "workspace:^", "@types/dockerode": "^3.3.0", "commander": "^9.1.0", "dockerode": "^3.3.1", diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index b4101d9514..6258adff78 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/test-utils +## 1.2.0 + +### Minor Changes + +- 817f3196f6: Elements rendered in a test app are no longer wrapped in a `Routes` and `Route` element, as this is not compatible with React Router v6 stable. + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- d9e39544be: Add missing peer dependencies +- Updated dependencies + - @backstage/core-app-api@1.1.0 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + ## 1.2.0-next.3 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 4d45519fa1..90f966ad97 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": "1.2.0-next.3", + "version": "1.2.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -32,15 +32,15 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-permission-common": "^0.6.4-next.2", - "@backstage/plugin-permission-react": "^0.4.5-next.2", - "@backstage/theme": "^0.2.16", - "@backstage/types": "^1.0.0", + "@backstage/config": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", - "@material-ui/icons": "^4.11.2", + "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -55,7 +55,7 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/node": "^16.11.26", "msw": "^0.47.0" }, diff --git a/packages/test-utils/src/testUtils/appWrappers.test.tsx b/packages/test-utils/src/testUtils/appWrappers.test.tsx index 1b0601d030..a116c8c781 100644 --- a/packages/test-utils/src/testUtils/appWrappers.test.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.test.tsx @@ -92,9 +92,11 @@ describe('wrapInTestApp', () => { }); expect(error).toEqual([ - expect.stringMatching( - /^Error: Uncaught \[Error: MockErrorApi received unexpected error, Error: NOPE\]/, - ), + expect.objectContaining({ + detail: new Error( + 'MockErrorApi received unexpected error, Error: NOPE', + ), + }), expect.stringMatching(/^The above error occurred in the component:/), ]); }); diff --git a/packages/theme/package.json b/packages/theme/package.json index 4569c67eae..f311aa7c1f 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -35,7 +35,7 @@ "@material-ui/core": "^4.12.2" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/packages/types/package.json b/packages/types/package.json index 133b388ea9..b8ebeffce8 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -32,7 +32,7 @@ "clean": "backstage-cli package clean" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1", + "@backstage/cli": "workspace:^", "@types/zen-observable": "^0.8.0", "zen-observable": "^0.8.15" }, diff --git a/packages/version-bridge/package.json b/packages/version-bridge/package.json index 52595c0cc9..3abfcd40f6 100644 --- a/packages/version-bridge/package.json +++ b/packages/version-bridge/package.json @@ -36,7 +36,7 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1", + "@backstage/cli": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0" diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index b92a33a8f0..f32be42f18 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-adr-backend +## 0.2.1 + +### Patch Changes + +- b489de83b1: Continue processing subsequent entities when an error occurs in collator +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-adr-common@0.2.1 + - @backstage/plugin-search-common@1.0.1 + ## 0.2.1-next.3 ### Patch Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index 91ed8e002c..d8295c0e46 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.2.1-next.3", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,14 +28,14 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-adr-common": "^0.2.1-next.1", - "@backstage/plugin-search-common": "^1.0.1-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-adr-common": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", "luxon": "^3.0.0", "marked": "^4.0.14", "node-fetch": "^2.6.5", @@ -43,7 +43,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/marked": "^4.0.0", "@types/supertest": "^2.0.8", "msw": "^0.47.0", diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index 9269768185..9a68db3d74 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-adr-common +## 0.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/plugin-search-common@1.0.1 + ## 0.2.1-next.1 ### Patch Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index 02631c3e6c..02ae1d642d 100644 --- a/plugins/adr-common/package.json +++ b/plugins/adr-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-adr-common", "description": "Common functionalities for the adr plugin", - "version": "0.2.1-next.1", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-search-common": "^1.0.1-next.0" + "@backstage/catalog-model": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-search-common": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index 3fc5634bd1..2fe9aacb31 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-adr +## 0.2.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- b489de83b1: Fix parsing of ADR location which includes a trailing slash +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-adr-common@0.2.1 + - @backstage/plugin-search-common@1.0.1 + ## 0.2.1-next.3 ### Patch Changes diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 732cb4d67f..f64d901820 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.2.1-next.3", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,15 +22,15 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-adr-common": "^0.2.1-next.1", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-search-common": "^1.0.1-next.0", - "@backstage/plugin-search-react": "^1.1.0-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-adr-common": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "@backstage/plugin-search-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -45,10 +45,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index 1868062e0b..2e249ba14a 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-airbrake-backend +## 0.2.9 + +### Patch Changes + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + ## 0.2.9-next.3 ### Patch Changes diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index 1a04e5185d..b79cdd80b1 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.2.9-next.3", + "version": "0.2.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", "@types/express": "*", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -32,7 +32,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "msw": "^0.47.0", diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index 8262a89cb0..2d03fb6ef2 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-airbrake +## 0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/dev-utils@1.0.6 + - @backstage/test-utils@1.2.0 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.3.9-next.3 ### Patch Changes diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index c8ec322420..5dc54ea863 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.9-next.3", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,13 +22,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/test-utils": "^1.2.0-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -40,10 +40,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/app-defaults": "^1.0.6-next.2", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/app-defaults": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index 64e114dda7..60dcd97b14 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-allure +## 0.1.25 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.1.25-next.3 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 12400806a7..3bfe92c6ee 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.25-next.3", + "version": "0.1.25", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,11 +23,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -38,10 +38,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index fb5051da23..622257c75a 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-analytics-module-ga +## 0.1.20 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + ## 0.1.20-next.2 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index e9b5044d92..7e4ce38c20 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.20-next.2", + "version": "0.1.20", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,10 +22,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -36,10 +36,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index 2396ca2b6f..66b3d3120b 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-apache-airflow +## 0.2.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 2f52e74b49: Got rid of usages of the uppercase String type +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 0.2.2-next.3 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index f132c46a78..5555fd35fc 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.2-next.3", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -35,10 +35,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/api-docs-module-protoc-gen-doc/package.json b/plugins/api-docs-module-protoc-gen-doc/package.json index 04088e7566..072304d2cf 100644 --- a/plugins/api-docs-module-protoc-gen-doc/package.json +++ b/plugins/api-docs-module-protoc-gen-doc/package.json @@ -37,7 +37,7 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1", + "@backstage/cli": "workspace:^", "@testing-library/jest-dom": "^5.16.4", "@types/react": "^16.13.1 || ^17.0.0" }, diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index db0ffbeada..339ad58f59 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-api-docs +## 0.8.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- c8bb0ff8ce: Minor cleanup of the public API surface to reduce the number of warnings +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-catalog@1.5.1 + - @backstage/catalog-model@1.1.1 + ## 0.8.9-next.3 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 8281a1522d..42cd8d5d74 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.8.9-next.3", + "version": "0.8.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,13 +32,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@asyncapi/react-component": "1.0.0-next.40", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog": "^1.5.1-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@asyncapi/react-component": "1.0.0-next.42", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -56,10 +56,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/api-docs/src/setupTests.ts b/plugins/api-docs/src/setupTests.ts index 963c0f188b..a41fe2eeb4 100644 --- a/plugins/api-docs/src/setupTests.ts +++ b/plugins/api-docs/src/setupTests.ts @@ -15,3 +15,7 @@ */ import '@testing-library/jest-dom'; + +Object.defineProperty(global, 'TextDecoder', { + value: require('util').TextDecoder, +}); diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index 40c9fa0c57..f4dfdc2d35 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-apollo-explorer +## 0.1.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 0.1.2-next.2 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index 39143c3133..770af99853 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.2-next.2", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,9 +23,9 @@ }, "dependencies": { "@apollo/explorer": "^1.1.1", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.57", @@ -36,10 +36,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 02188afefc..a6cf9ef532 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-app-backend +## 0.3.36 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config-loader@1.1.4 + - @backstage/config@1.0.2 + ## 0.3.36-next.3 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index c06c41e6dd..ffd40b89c0 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.36-next.3", + "version": "0.3.36", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/config-loader": "^1.1.4-next.2", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/config-loader": "workspace:^", + "@backstage/types": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -49,9 +49,9 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/types": "workspace:^", "@types/supertest": "^2.0.8", "mock-fs": "^5.1.0", "msw": "^0.47.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 7c5fff0dd0..189a4a235f 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-auth-backend +## 0.16.0 + +### Minor Changes + +- 2fc41ebf07: Removed the previously deprecated class `AtlassianAuthProvider`. Please use `providers.atlassian.create(...)` instead. +- a291688bc5: Renamed the `RedirectInfo` type to `OAuthStartResponse` +- 8600855fbf: The auth0 integration is updated to use the `passport-auth0` library. The configuration under `auth.providers.auth0.\*` now supports an optional `audience` parameter; providing that allows you to connect to the correct API to get permissions, access tokens, and full profile information. + + [What is an Audience](https://community.auth0.com/t/what-is-the-audience/71414) + +### Patch Changes + +- 5b011fb2e6: Allow adding misc claims to JWT +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- e1ebaeb332: Cloudflare Access Provider: Add JWT to CloudflareAccessResult +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.16.0-next.3 ### Minor Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index d47abeee83..ea5b923baa 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -183,10 +183,12 @@ export type CookieConfigurer = (ctx: { providerId: string; baseUrl: string; callbackUrl: string; + appOrigin: string; }) => { domain: string; path: string; secure: boolean; + sameSite?: 'none' | 'lax' | 'strict'; }; // @public @@ -280,11 +282,10 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { // @public (undocumented) export type OAuthAdapterOptions = { providerId: string; - secure: boolean; persistScopes?: boolean; - cookieDomain: string; - cookiePath: string; appOrigin: string; + baseUrl: string; + cookieConfigurer: CookieConfigurer; isOriginAllowed: (origin: string) => boolean; callbackUrl: string; }; @@ -706,7 +707,7 @@ export type TokenParams = { claims: { sub: string; ent?: string[]; - }; + } & Record; }; // @public (undocumented) diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 4f820f4829..d3c4fc2724 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.16.0-next.3", + "version": "0.16.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,13 +32,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/types": "workspace:^", "@davidzemon/passport-okta-oauth": "^0.0.5", "@google-cloud/firestore": "^6.0.0", "@types/express": "^4.17.6", @@ -76,8 +76,8 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@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/identity/FirestoreKeyStore.test.ts b/plugins/auth-backend/src/identity/FirestoreKeyStore.test.ts index d28cbfa11a..b04cf8a6b3 100644 --- a/plugins/auth-backend/src/identity/FirestoreKeyStore.test.ts +++ b/plugins/auth-backend/src/identity/FirestoreKeyStore.test.ts @@ -44,7 +44,7 @@ jest.mock('@google-cloud/firestore', () => ({ Firestore: jest.fn().mockImplementation(() => firestoreMock), })); -jest.useFakeTimers('legacy'); +jest.useFakeTimers({ legacyFakeTimers: true }); describe('FirestoreKeyStore', () => { const key = { diff --git a/plugins/auth-backend/src/identity/TokenFactory.test.ts b/plugins/auth-backend/src/identity/TokenFactory.test.ts index 9a163b3ff5..4f6df89210 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.test.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.test.ts @@ -48,7 +48,12 @@ describe('TokenFactory', () => { await expect(factory.listPublicKeys()).resolves.toEqual({ keys: [] }); const token = await factory.issueToken({ - claims: { sub: entityRef, ent: [entityRef] }, + claims: { + sub: entityRef, + ent: [entityRef], + 'x-fancy-claim': 'my special claim', + aud: 'this value will be overridden', + }, }); const { keys } = await factory.listPublicKeys(); @@ -60,6 +65,7 @@ describe('TokenFactory', () => { aud: 'backstage', sub: entityRef, ent: [entityRef], + 'x-fancy-claim': 'my special claim', iat: expect.any(Number), exp: expect.any(Number), }); diff --git a/plugins/auth-backend/src/identity/TokenFactory.ts b/plugins/auth-backend/src/identity/TokenFactory.ts index 44cfebfb71..f80b79b6bc 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.ts @@ -77,8 +77,7 @@ export class TokenFactory implements TokenIssuer { const key = await this.getKey(); const iss = this.issuer; - const sub = params.claims.sub; - const ent = params.claims.ent; + const { sub, ent, ...additionalClaims } = params.claims; const aud = 'backstage'; const iat = Math.floor(Date.now() / MS_IN_S); const exp = iat + this.keyDurationSeconds; @@ -98,7 +97,7 @@ export class TokenFactory implements TokenIssuer { throw new AuthenticationError('No algorithm was provided in the key'); } - return new SignJWT({ iss, sub, ent, aud, iat, exp }) + return new SignJWT({ ...additionalClaims, iss, sub, ent, aud, iat, exp }) .setProtectedHeader({ alg: key.alg, kid: key.kid }) .setIssuer(iss) .setAudience(aud) diff --git a/plugins/auth-backend/src/identity/types.ts b/plugins/auth-backend/src/identity/types.ts index 4765b50d7d..e325f74c81 100644 --- a/plugins/auth-backend/src/identity/types.ts +++ b/plugins/auth-backend/src/identity/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { JsonValue } from '@backstage/types'; + /** Represents any form of serializable JWK */ export interface AnyJWK extends Record { use: 'sig'; @@ -28,13 +30,20 @@ export interface AnyJWK extends Record { * @public */ export type TokenParams = { - /** The claims that will be embedded within the token */ + /** + * The claims that will be embedded within the token. At a minimum, this should include + * the subject claim, `sub`. It is common to also list entity ownership relations in the + * `ent` list. Additional claims may also be added at the developer's discretion except + * for the following list, which will be overwritten by the TokenIssuer: `iss`, `aud`, + * `iat`, and `exp`. The Backstage team also maintains the right add new claims in the future + * without listing the change as a "breaking change". + */ claims: { /** The token subject, i.e. User ID */ sub: string; /** A list of entity references that the user claims ownership through */ ent?: string[]; - }; + } & Record; }; /** diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts index 9dcd76e92e..526b05d0ab 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.test.ts @@ -18,6 +18,7 @@ import express from 'express'; import { THOUSAND_DAYS_MS, TEN_MINUTES_MS, OAuthAdapter } from './OAuthAdapter'; import { encodeState } from './helpers'; import { OAuthHandlers, OAuthState } from './types'; +import { CookieConfigurer } from '../../providers/types'; const mockResponseData = { providerInfo: { @@ -36,6 +37,10 @@ const mockResponseData = { }; describe('OAuthAdapter', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + class MyAuthProvider implements OAuthHandlers { async start() { return { @@ -57,18 +62,24 @@ describe('OAuthAdapter', () => { } } const providerInstance = new MyAuthProvider(); + const mockCookieConfig: ReturnType = { + domain: 'domain.org', + path: '/auth/test-provider', + secure: false, + }; + const mockCookieConfigurer = jest.fn().mockReturnValue(mockCookieConfig); + const oAuthProviderOptions = { providerId: 'test-provider', - secure: false, appOrigin: 'http://localhost:3000', - cookieDomain: 'example.com', - cookiePath: '/auth/test-provider', + baseUrl: 'http://domain.org/auth', + cookieConfigurer: mockCookieConfigurer, tokenIssuer: { issueToken: async () => 'my-id-token', listPublicKeys: async () => ({ keys: [] }), }, isOriginAllowed: () => false, - callbackUrl: 'http://example.com:7007/auth/test-provider/frame/handler', + callbackUrl: 'http://domain.org/auth/test-provider/handler/frame', }; it('sets the correct headers in start', async () => { @@ -96,7 +107,14 @@ describe('OAuthAdapter', () => { expect(mockResponse.cookie).toHaveBeenCalledWith( `${oAuthProviderOptions.providerId}-nonce`, expect.any(String), - expect.objectContaining({ maxAge: TEN_MINUTES_MS }), + expect.objectContaining({ + httpOnly: true, + path: '/auth/test-provider/handler', + maxAge: TEN_MINUTES_MS, + domain: 'domain.org', + sameSite: 'lax', + secure: false, + }), ); // redirect checks expect(mockResponse.setHeader).toHaveBeenCalledTimes(2); @@ -129,13 +147,18 @@ describe('OAuthAdapter', () => { } as unknown as express.Response; await oauthProvider.frameHandler(mockRequest, mockResponse); + expect(mockCookieConfigurer).toHaveBeenCalledTimes(1); expect(mockResponse.cookie).toHaveBeenCalledTimes(1); expect(mockResponse.cookie).toHaveBeenCalledWith( expect.stringContaining('test-provider-refresh-token'), expect.stringContaining('token'), expect.objectContaining({ + httpOnly: true, path: '/auth/test-provider', maxAge: THOUSAND_DAYS_MS, + domain: 'domain.org', + secure: false, + sameSite: 'lax', }), ); }); @@ -207,8 +230,12 @@ describe('OAuthAdapter', () => { 'test-provider-granted-scope', 'user', expect.objectContaining({ + httpOnly: true, path: '/auth/test-provider', maxAge: THOUSAND_DAYS_MS, + domain: 'domain.org', + secure: false, + sameSite: 'lax', }), ); @@ -243,6 +270,7 @@ describe('OAuthAdapter', () => { const mockRequest = { header: () => 'XMLHttpRequest', + get: jest.fn(), } as unknown as express.Request; const mockResponse = { @@ -252,6 +280,7 @@ describe('OAuthAdapter', () => { } as unknown as express.Response; await oauthProvider.logout(mockRequest, mockResponse); + expect(mockRequest.get).toHaveBeenCalledTimes(1); expect(mockResponse.cookie).toHaveBeenCalledTimes(1); expect(mockResponse.cookie).toHaveBeenCalledWith( expect.stringContaining('test-provider-refresh-token'), @@ -295,7 +324,46 @@ describe('OAuthAdapter', () => { }); }); - it('sets the correct cookie configuration using a callbackUrl', async () => { + it('sets new access-token when old cookie exists', async () => { + const oauthProvider = new OAuthAdapter(providerInstance, { + ...oAuthProviderOptions, + isOriginAllowed: () => false, + }); + + const mockRequest = { + header: () => 'XMLHttpRequest', + cookies: { + 'test-provider-refresh-token': 'old-token', + }, + query: {}, + get: jest.fn(), + } as unknown as express.Request; + + const mockResponse = { + json: jest.fn().mockReturnThis(), + status: jest.fn().mockReturnThis(), + cookie: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + await oauthProvider.refresh(mockRequest, mockResponse); + expect(mockRequest.get).toHaveBeenCalledTimes(1); + expect(mockCookieConfigurer).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledWith( + 'test-provider-refresh-token', + 'token', + expect.objectContaining({ + httpOnly: true, + path: '/auth/test-provider', + maxAge: THOUSAND_DAYS_MS, + domain: 'domain.org', + secure: false, + sameSite: 'lax', + }), + ); + }); + + it('sets the correct nonce cookie configuration', async () => { const config = { baseUrl: 'http://domain.org/auth', appUrl: 'http://domain.org', @@ -304,13 +372,13 @@ describe('OAuthAdapter', () => { const oauthProvider = OAuthAdapter.fromConfig(config, providerInstance, { ...oAuthProviderOptions, - callbackUrl: 'https://authdomain.org/auth/test-provider/handler/frame', }); const mockRequest = { query: { scope: 'user', env: 'development', + origin: 'http://domain.org', }, } as unknown as express.Request; @@ -322,15 +390,252 @@ describe('OAuthAdapter', () => { } as unknown as express.Response; await oauthProvider.start(mockRequest, mockResponse); - + expect(mockCookieConfigurer).not.toHaveBeenCalled(); expect(mockResponse.cookie).toHaveBeenCalledTimes(1); expect(mockResponse.cookie).toHaveBeenCalledWith( `${oAuthProviderOptions.providerId}-nonce`, expect.any(String), expect.objectContaining({ - domain: 'authdomain.org', + httpOnly: true, + domain: 'domain.org', + maxAge: TEN_MINUTES_MS, + path: '/auth/test-provider/handler', + secure: false, + sameSite: 'lax', + }), + ); + }); + + it('sets the correct nonce cookie configuration using origin from request', async () => { + const config = { + baseUrl: 'http://domain.org/auth', + appUrl: 'http://domain.org', + isOriginAllowed: () => false, + }; + + const oauthProvider = OAuthAdapter.fromConfig(config, providerInstance, { + ...oAuthProviderOptions, + callbackUrl: 'https://domain.org/auth/test-provider/handler/frame', + }); + + const mockRequest = { + query: { + scope: 'user', + env: 'development', + origin: 'http://other.domain', + }, + } as unknown as express.Request; + + const mockResponse = { + cookie: jest.fn().mockReturnThis(), + end: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + statusCode: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + await oauthProvider.start(mockRequest, mockResponse); + expect(mockCookieConfigurer).not.toHaveBeenCalled(); + expect(mockResponse.cookie).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledWith( + `${oAuthProviderOptions.providerId}-nonce`, + expect.any(String), + expect.objectContaining({ + httpOnly: true, + domain: 'domain.org', + maxAge: TEN_MINUTES_MS, path: '/auth/test-provider/handler', secure: true, + sameSite: 'none', + }), + ); + }); + + it('sets the correct cookie configuration using an secure callbackUrl', async () => { + const config = { + baseUrl: 'https://domain.org/auth', + appUrl: 'http://domain.org', + isOriginAllowed: () => false, + }; + + const oauthProvider = OAuthAdapter.fromConfig(config, providerInstance, { + ...oAuthProviderOptions, + callbackUrl: 'https://domain.org/auth/test-provider/handler/frame', + }); + + const state = { + nonce: 'nonce', + env: 'development', + }; + + const mockRequest = { + cookies: { + 'test-provider-nonce': 'nonce', + }, + query: { + state: encodeState(state), + }, + } as unknown as express.Request; + + const mockResponse = { + cookie: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + end: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + await oauthProvider.frameHandler(mockRequest, mockResponse); + expect(mockCookieConfigurer).not.toHaveBeenCalled(); + expect(mockResponse.cookie).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledWith( + expect.stringContaining('test-provider-refresh-token'), + expect.stringContaining('token'), + expect.objectContaining({ + httpOnly: true, + domain: 'domain.org', + path: '/auth/test-provider', + secure: true, + sameSite: 'lax', + }), + ); + }); + + it('sets the correct cookie configuration when on different domains and secure', async () => { + const config = { + baseUrl: 'https://domain.org/auth', + appUrl: 'http://domain.org', + isOriginAllowed: () => false, + }; + + const oauthProvider = OAuthAdapter.fromConfig(config, providerInstance, { + ...oAuthProviderOptions, + callbackUrl: 'https://authdomain.org/auth/test-provider/handler/frame', + }); + + const state = { + nonce: 'nonce', + env: 'development', + }; + + const mockRequest = { + cookies: { + 'test-provider-nonce': 'nonce', + }, + query: { + state: encodeState(state), + }, + } as unknown as express.Request; + + const mockResponse = { + cookie: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + end: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + await oauthProvider.frameHandler(mockRequest, mockResponse); + expect(mockCookieConfigurer).not.toHaveBeenCalled(); + expect(mockResponse.cookie).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledWith( + expect.stringContaining('test-provider-refresh-token'), + expect.stringContaining('token'), + expect.objectContaining({ + httpOnly: true, + domain: 'authdomain.org', + path: '/auth/test-provider', + secure: true, + sameSite: 'none', + }), + ); + }); + + it('sets the correct cookie configuration using origin from state', async () => { + const config = { + baseUrl: 'https://domain.org/auth', + appUrl: 'http://domain.org', + isOriginAllowed: () => true, + }; + + const oauthProvider = OAuthAdapter.fromConfig(config, providerInstance, { + ...oAuthProviderOptions, + callbackUrl: 'https://domain.org/auth/test-provider/handler/frame', + }); + + const state = { + nonce: 'nonce', + env: 'development', + origin: 'http://other.domain', + }; + + const mockRequest = { + cookies: { + 'test-provider-nonce': 'nonce', + }, + query: { + state: encodeState(state), + }, + } as unknown as express.Request; + + const mockResponse = { + cookie: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + end: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + await oauthProvider.frameHandler(mockRequest, mockResponse); + expect(mockCookieConfigurer).not.toHaveBeenCalled(); + expect(mockResponse.cookie).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledWith( + expect.stringContaining('test-provider-refresh-token'), + expect.stringContaining('token'), + expect.objectContaining({ + httpOnly: true, + domain: 'domain.org', + path: '/auth/test-provider', + secure: true, + sameSite: 'none', + }), + ); + }); + + it('sets the correct cookie configuration using origin from header', async () => { + const config = { + baseUrl: 'https://domain.org/auth', + appUrl: 'http://domain.org', + isOriginAllowed: () => false, + }; + + const oauthProvider = OAuthAdapter.fromConfig(config, providerInstance, { + ...oAuthProviderOptions, + callbackUrl: 'https://domain.org/auth/test-provider/handler/frame', + }); + + const mockRequest = { + header: () => 'XMLHttpRequest', + cookies: { + 'test-provider-refresh-token': 'old-token', + }, + query: {}, + get: jest.fn().mockReturnValue('http://other.domain'), + } as unknown as express.Request; + + const mockResponse = { + json: jest.fn().mockReturnThis(), + status: jest.fn().mockReturnThis(), + cookie: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + await oauthProvider.refresh(mockRequest, mockResponse); + expect(mockRequest.get).toHaveBeenCalledTimes(1); + expect(mockCookieConfigurer).not.toHaveBeenCalled(); + expect(mockResponse.cookie).toHaveBeenCalledTimes(1); + expect(mockResponse.cookie).toHaveBeenCalledWith( + 'test-provider-refresh-token', + 'token', + expect.objectContaining({ + httpOnly: true, + path: '/auth/test-provider', + maxAge: THOUSAND_DAYS_MS, + domain: 'domain.org', + secure: true, + sameSite: 'none', }), ); }); diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts index 6caa9fed12..8f08cfbbd0 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts @@ -24,6 +24,7 @@ import { import { AuthProviderRouteHandlers, AuthProviderConfig, + CookieConfigurer, } from '../../providers/types'; import { AuthenticationError, @@ -47,11 +48,10 @@ export const TEN_MINUTES_MS = 600 * 1000; /** @public */ export type OAuthAdapterOptions = { providerId: string; - secure: boolean; persistScopes?: boolean; - cookieDomain: string; - cookiePath: string; appOrigin: string; + baseUrl: string; + cookieConfigurer: CookieConfigurer; isOriginAllowed: (origin: string) => boolean; callbackUrl: string; }; @@ -66,22 +66,17 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { 'providerId' | 'persistScopes' | 'callbackUrl' >, ): OAuthAdapter { - const { origin: appOrigin } = new URL(config.appUrl); + const { appUrl, baseUrl, isOriginAllowed } = config; + const { origin: appOrigin } = new URL(appUrl); const cookieConfigurer = config.cookieConfigurer ?? defaultCookieConfigurer; - const cookieConfig = cookieConfigurer({ - providerId: options.providerId, - baseUrl: config.baseUrl, - callbackUrl: options.callbackUrl, - }); return new OAuthAdapter(handlers, { ...options, appOrigin, - cookieDomain: cookieConfig.domain, - cookiePath: cookieConfig.path, - secure: cookieConfig.secure, - isOriginAllowed: config.isOriginAllowed, + baseUrl, + cookieConfigurer, + isOriginAllowed, }); } @@ -94,9 +89,6 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { this.baseCookieOptions = { httpOnly: true, sameSite: 'lax', - secure: this.options.secure, - path: this.options.cookiePath, - domain: this.options.cookieDomain, }; } @@ -110,9 +102,11 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { throw new InputError('No env provided in request query parameters'); } + const cookieConfig = this.getCookieConfig(origin); + const nonce = crypto.randomBytes(16).toString('base64'); // set a nonce cookie before redirecting to oauth provider - this.setNonceCookie(res, nonce); + this.setNonceCookie(res, nonce, cookieConfig); const state: OAuthState = { nonce, env, origin }; @@ -158,16 +152,18 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { const { response, refreshToken } = await this.handlers.handler(req); + const cookieConfig = this.getCookieConfig(appOrigin); + // Store the scope that we have been granted for this session. This is useful if // the provider does not return granted scopes on refresh or if they are normalized. if (this.options.persistScopes && state.scope) { - this.setGrantedScopeCookie(res, state.scope); + this.setGrantedScopeCookie(res, state.scope, cookieConfig); response.providerInfo.scope = state.scope; } if (refreshToken) { // set new refresh token - this.setRefreshTokenCookie(res, refreshToken); + this.setRefreshTokenCookie(res, refreshToken, cookieConfig); } const identity = await this.populateIdentity(response.backstageIdentity); @@ -195,7 +191,9 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { } // remove refresh token cookie if it is set - this.removeRefreshTokenCookie(res); + const origin = req.get('origin'); + const cookieConfig = this.getCookieConfig(origin); + this.removeRefreshTokenCookie(res, cookieConfig); res.status(200).end(); } @@ -235,7 +233,9 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { ); if (newRefreshToken && newRefreshToken !== refreshToken) { - this.setRefreshTokenCookie(res, newRefreshToken); + const origin = req.get('origin'); + const cookieConfig = this.getCookieConfig(origin); + this.setRefreshTokenCookie(res, newRefreshToken, cookieConfig); } res.status(200).json({ ...response, backstageIdentity }); @@ -261,18 +261,28 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { return prepareBackstageIdentityResponse(identity); } - private setNonceCookie = (res: express.Response, nonce: string) => { + private setNonceCookie = ( + res: express.Response, + nonce: string, + cookieConfig: ReturnType, + ) => { res.cookie(`${this.options.providerId}-nonce`, nonce, { maxAge: TEN_MINUTES_MS, ...this.baseCookieOptions, - path: `${this.options.cookiePath}/handler`, + ...cookieConfig, + path: `${cookieConfig.path}/handler`, }); }; - private setGrantedScopeCookie = (res: express.Response, scope: string) => { + private setGrantedScopeCookie = ( + res: express.Response, + scope: string, + cookieConfig: ReturnType, + ) => { res.cookie(`${this.options.providerId}-granted-scope`, scope, { maxAge: THOUSAND_DAYS_MS, ...this.baseCookieOptions, + ...cookieConfig, }); }; @@ -283,17 +293,32 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { private setRefreshTokenCookie = ( res: express.Response, refreshToken: string, + cookieConfig: ReturnType, ) => { res.cookie(`${this.options.providerId}-refresh-token`, refreshToken, { maxAge: THOUSAND_DAYS_MS, ...this.baseCookieOptions, + ...cookieConfig, }); }; - private removeRefreshTokenCookie = (res: express.Response) => { + private removeRefreshTokenCookie = ( + res: express.Response, + cookieConfig: ReturnType, + ) => { res.cookie(`${this.options.providerId}-refresh-token`, '', { maxAge: 0, ...this.baseCookieOptions, + ...cookieConfig, + }); + }; + + private getCookieConfig = (origin?: string) => { + return this.options.cookieConfigurer({ + providerId: this.options.providerId, + baseUrl: this.options.baseUrl, + callbackUrl: this.options.callbackUrl, + appOrigin: origin ?? this.options.appOrigin, }); }; } diff --git a/plugins/auth-backend/src/lib/oauth/helpers.test.ts b/plugins/auth-backend/src/lib/oauth/helpers.test.ts index b3052c93e1..58a3dcab05 100644 --- a/plugins/auth-backend/src/lib/oauth/helpers.test.ts +++ b/plugins/auth-backend/src/lib/oauth/helpers.test.ts @@ -117,6 +117,7 @@ describe('OAuthProvider Utils', () => { baseUrl: '', providerId: 'test-provider', callbackUrl: 'http://domain.org/auth', + appOrigin: 'http://domain.org', }), ).toMatchObject({ domain: 'domain.org', @@ -131,6 +132,7 @@ describe('OAuthProvider Utils', () => { baseUrl: '', providerId: 'test-provider', callbackUrl: 'http://domain.org/auth/test-provider/handler/frame', + appOrigin: 'http://domain.org', }), ).toMatchObject({ domain: 'domain.org', @@ -145,10 +147,67 @@ describe('OAuthProvider Utils', () => { baseUrl: '', providerId: 'test-provider', callbackUrl: 'https://domain.org/auth', + appOrigin: 'http://domain.org', }), ).toMatchObject({ secure: true, }); }); + + it('should set sameSite to lax for https on the same domain', () => { + expect( + defaultCookieConfigurer({ + baseUrl: '', + providerId: 'test-provider', + callbackUrl: 'https://domain.org/auth', + appOrigin: 'http://domain.org', + }), + ).toMatchObject({ + sameSite: 'lax', + secure: true, + }); + }); + + it('should set sameSite to lax for http on the same domain', () => { + expect( + defaultCookieConfigurer({ + baseUrl: '', + providerId: 'test-provider', + callbackUrl: 'http://domain.org/auth', + appOrigin: 'http://domain.org', + }), + ).toMatchObject({ + sameSite: 'lax', + secure: false, + }); + }); + + it('should set sameSite to lax if not secure and on different domains', () => { + expect( + defaultCookieConfigurer({ + baseUrl: '', + providerId: 'test-provider', + callbackUrl: 'http://authdomain.org/auth', + appOrigin: 'http://domain.org', + }), + ).toMatchObject({ + sameSite: 'lax', + secure: false, + }); + }); + + it('should set sameSite to none if secure and on different domains', () => { + expect( + defaultCookieConfigurer({ + baseUrl: '', + providerId: 'test-provider', + callbackUrl: 'https://authdomain.org/auth', + appOrigin: 'http://domain.org', + }), + ).toMatchObject({ + sameSite: 'none', + secure: true, + }); + }); }); }); diff --git a/plugins/auth-backend/src/lib/oauth/helpers.ts b/plugins/auth-backend/src/lib/oauth/helpers.ts index 63d5d62ef9..2b4e50b477 100644 --- a/plugins/auth-backend/src/lib/oauth/helpers.ts +++ b/plugins/auth-backend/src/lib/oauth/helpers.ts @@ -65,10 +65,20 @@ export const verifyNonce = (req: express.Request, providerId: string) => { export const defaultCookieConfigurer: CookieConfigurer = ({ callbackUrl, providerId, + appOrigin, }) => { const { hostname: domain, pathname, protocol } = new URL(callbackUrl); const secure = protocol === 'https:'; + // For situations where the auth-backend is running on a + // different domain than the app, we set the SameSite attribute + // to 'none' to allow third-party access to the cookie, but + // only if it's in a secure context (https). + let sameSite: ReturnType['sameSite'] = 'lax'; + if (new URL(appOrigin).hostname !== domain && secure) { + sameSite = 'none'; + } + // If the provider supports callbackUrls, the pathname will // contain the complete path to the frame handler so we need // to slice off the trailing part of the path. @@ -76,5 +86,5 @@ export const defaultCookieConfigurer: CookieConfigurer = ({ ? pathname.slice(0, -'/handler/frame'.length) : `${pathname}/${providerId}`; - return { domain, path, secure }; + return { domain, path, secure, sameSite }; }; diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 574afade79..1459cff484 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -100,7 +100,14 @@ export type CookieConfigurer = (ctx: { baseUrl: string; /** The configured callback URL of the auth provider */ callbackUrl: string; -}) => { domain: string; path: string; secure: boolean }; + /** The origin URL of the app */ + appOrigin: string; +}) => { + domain: string; + path: string; + secure: boolean; + sameSite?: 'none' | 'lax' | 'strict'; +}; /** @public */ export type AuthProviderConfig = { diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 13fb0bbcce..24ce679c1e 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-auth-node +## 0.2.5 + +### Patch Changes + +- 9212439d15: Minor update to tests +- 2cbd533426: `IdentityClient` is now deprecated. Please migrate to `IdentityApi` and `DefaultIdentityClient` instead. The authenticate function on `DefaultIdentityClient` is also deprecated. Please use `getIdentity` instead. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.2.5-next.3 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 52115d6611..eac9d4e4d1 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.2.5-next.3", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@types/express": "*", "express": "^4.17.1", "jose": "^4.6.0", @@ -32,8 +32,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "lodash": "^4.17.21", "msw": "^0.47.0", "uuid": "^8.0.0" diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index d6145f3308..4f40ad3c6e 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-azure-devops-backend +## 0.3.15 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- cb1cfc018b: `createRouter` now requires an additional reader: `UrlReader` argument + + ```diff + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return createRouter({ + logger: env.logger, + config: env.config, + + reader: env.reader, + }); + } + ``` + + Remember to check if you have already provided these settings previously. + + #### [Azure DevOps] + + ```yaml + # app-config.yaml + azureDevOps: + host: dev.azure.com + token: my-token + organization: my-company + ``` + + #### [Azure Integrations] + + ```yaml + # app-config.yaml + integrations: + azure: + - host: dev.azure.com + token: ${AZURE_TOKEN} + ``` + +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-azure-devops-common@0.3.0 + - @backstage/config@1.0.2 + ## 0.3.15-next.2 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 92bcf84c62..c77860abe5 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.3.15-next.2", + "version": "0.3.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/plugin-azure-devops-common": "^0.3.0-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-azure-devops-common": "workspace:^", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^0.47.0", "supertest": "^6.1.6" diff --git a/plugins/azure-devops-common/CHANGELOG.md b/plugins/azure-devops-common/CHANGELOG.md index 8f0c5b9f76..a96aae9f0a 100644 --- a/plugins/azure-devops-common/CHANGELOG.md +++ b/plugins/azure-devops-common/CHANGELOG.md @@ -1,5 +1,58 @@ # @backstage/plugin-azure-devops-common +## 0.3.0 + +### Minor Changes + +- 6c1c59b96e: Added README card `EntityAzureReadmeCard` for Azure Devops. + + To get the README component working you'll need to do the following two steps: + + 1. First we need to add the @backstage/plugin-azure-devops package to your frontend app: + + ```bash + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-azure-devops + ``` + + 2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app: + + ```tsx + // In packages/app/src/components/catalog/EntityPage.tsx + import { + EntityAzureReadmeCard, + isAzureDevOpsAvailable, + } from '@backstage/plugin-azure-devops'; + + // As it is a card, you can customize it the way you prefer + // For example in the Service section + + const overviewContent = ( + + + + + ... + + + + + + + + ); + ``` + + **Notes:** + + - You'll need to add the `EntitySwitch.Case` above from step 2 to all the entity sections you want to see Readme in. For example if you wanted to see Readme when looking at Website entities then you would need to add this to the `websiteEntityPage` section. + - The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation + - The `maxHeight` property on the `EntityAzureReadmeCard` will set the maximum screen size you would like to see, if not set it will default to 100% + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index 5633826d9e..cf0b33275f 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-common", - "version": "0.3.0-next.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ "clean": "backstage-cli package clean" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 13ea5bbaa5..6ca86dfefa 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,70 @@ # @backstage/plugin-azure-devops +## 0.2.0 + +### Minor Changes + +- 6c1c59b96e: Added README card `EntityAzureReadmeCard` for Azure Devops. + + To get the README component working you'll need to do the following two steps: + + 1. First we need to add the @backstage/plugin-azure-devops package to your frontend app: + + ```bash + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-azure-devops + ``` + + 2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app: + + ```tsx + // In packages/app/src/components/catalog/EntityPage.tsx + import { + EntityAzureReadmeCard, + isAzureDevOpsAvailable, + } from '@backstage/plugin-azure-devops'; + + // As it is a card, you can customize it the way you prefer + // For example in the Service section + + const overviewContent = ( + + + + + ... + + + + + + + + ); + ``` + + **Notes:** + + - You'll need to add the `EntitySwitch.Case` above from step 2 to all the entity sections you want to see Readme in. For example if you wanted to see Readme when looking at Website entities then you would need to add this to the `websiteEntityPage` section. + - The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation + - The `maxHeight` property on the `EntityAzureReadmeCard` will set the maximum screen size you would like to see, if not set it will default to 100% + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-azure-devops-common@0.3.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.2.0-next.3 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 0cdd048b1b..9785fdf2b3 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.2.0-next.3", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,13 +28,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-azure-devops-common": "^0.3.0-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-azure-devops-common": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -47,10 +47,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/azure-devops/src/utils/getDurationFromDates.test.ts b/plugins/azure-devops/src/utils/getDurationFromDates.test.ts index fac0e917d0..2a8ee6e04f 100644 --- a/plugins/azure-devops/src/utils/getDurationFromDates.test.ts +++ b/plugins/azure-devops/src/utils/getDurationFromDates.test.ts @@ -18,7 +18,7 @@ import { getDurationFromDates } from './getDurationFromDates'; describe('getDurationFromDates', () => { beforeAll(() => { - jest.useFakeTimers('modern'); + jest.useFakeTimers(); jest.setSystemTime(new Date('2021-10-15T09:45:25.0000000Z')); }); diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index c54c09a08b..8b842ca26a 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-badges-backend +## 0.1.30 + +### Patch Changes + +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.30-next.1 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 0002695cc8..0eaf8a9d0a 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.1.30-next.1", + "version": "0.1.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@types/express": "^4.17.6", "badge-maker": "^3.3.0", "cors": "^2.8.5", @@ -47,7 +47,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 50137c221b..fff53440bc 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-badges +## 0.2.33 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.2.33-next.3 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 8748a05077..2e8081f3f6 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.33-next.3", + "version": "0.2.33", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -45,10 +45,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index 4494697278..805b1d2068 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-bazaar-backend +## 0.1.20 + +### Patch Changes + +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/backend-test-utils@0.1.28 + ## 0.1.20-next.1 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index c1242acd7e..5f11c694f8 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.1.20-next.1", + "version": "0.1.20", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,9 +22,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/config": "^1.0.2-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/config": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 8d93b89e0f..f936900cd8 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-bazaar +## 0.1.24 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 1dd12349d1: Fixed broken routing by removing the wrapping `Router` from the `RoutedTabs` children. +- Updated dependencies + - @backstage/cli@0.19.0 + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-catalog@1.5.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + ## 0.1.24-next.2 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index 21e2f3ce25..919f8a7df7 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.1.24-next.2", + "version": "0.1.24", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,13 +22,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog": "^1.5.1-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,8 +44,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "cross-fetch": "^3.1.5" }, diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 759e3602f6..0990b34b42 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.1.3 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/integration@1.3.1 + ## 0.1.3-next.1 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index e9bc4f02a4..1e8db8185f 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", "description": "Common functionalities for bitbucket-cloud plugins", - "version": "0.1.3-next.1", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,11 +27,11 @@ "update-models": "yarn refresh-schema && yarn generate-models && yarn reduce-models" }, "dependencies": { - "@backstage/integration": "^1.3.1-next.1", + "@backstage/integration": "workspace:^", "cross-fetch": "^3.1.5" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.2", + "@backstage/cli": "workspace:^", "@openapitools/openapi-generator-cli": "^2.4.26", "msw": "^0.47.0", "ts-morph": "^15.0.0" diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index 114ab5549e..55eb78d63e 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-bitrise +## 0.1.36 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.1.36-next.3 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index b659cac03a..1a7e586e5c 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.36-next.3", + "version": "0.1.36", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,11 +23,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -42,10 +42,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 07b5446dcc..ea6f2810e7 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.9-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 33158ac182..c921aad767 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.1.9-next.2", + "version": "0.1.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,14 +32,14 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "aws-sdk": "^2.840.0", "lodash": "^4.17.21", "p-limit": "^3.0.2", @@ -47,7 +47,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", "aws-sdk-mock": "^5.2.1", "yaml": "^2.0.0" diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 57d5315cf4..51d9752af9 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.7 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.7-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 3dd90d1bb3..afcfdf9f0f 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.7-next.3", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,14 +32,14 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "lodash": "^4.17.21", "msw": "^0.47.0", "node-fetch": "^2.6.7", @@ -47,8 +47,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index cd50519279..453f6124ba 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.3 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/config@1.0.2 + - @backstage/plugin-bitbucket-cloud-common@0.1.3 + ## 0.1.3-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index e8ddff85b0..4fdebd65bd 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.3-next.3", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,18 +32,18 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/config": "^1.0.2-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-bitbucket-cloud-common": "^0.1.3-next.1", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", + "@backstage/backend-tasks": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-bitbucket-cloud-common": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", "uuid": "^8.0.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "msw": "^0.47.0" }, "files": [ diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index ae82dc8ad7..5ec5a515bb 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.1 + +### Patch Changes + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.1-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 4064732eca..d4e27a8ec6 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.1-next.3", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,21 +31,21 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", "@types/node-fetch": "^2.5.12", "node-fetch": "^2.6.7", "uuid": "^8.0.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "msw": "^0.47.0" }, "files": [ diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index 7533ebaa47..9af08254eb 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.3 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.3 + ## 0.2.3-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index 7d27598d1f..171493dfc2 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.3-next.3", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,22 +32,22 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-bitbucket-cloud-common": "^0.1.3-next.1", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-bitbucket-cloud-common": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "lodash": "^4.17.21", "msw": "^0.47.0", "node-fetch": "^2.6.7", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index d90cbfbe10..2dc4229172 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.4 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.4-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 53d54afc06..6a115ea481 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.4-next.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,13 +28,13 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", "fs-extra": "10.1.0", "msw": "^0.47.0", "node-fetch": "^2.6.7", @@ -42,8 +42,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/fs-extra": "^9.0.1" }, "files": [ diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index a1a11f1519..fa49109016 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog-backend-module-github +## 0.1.7 + +### Patch Changes + +- 3c4a388537: New experimental alpha exports for use with the upcoming backend system. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 3a62594a11: Add support for including (or excluding) Github repositories by topic +- 287a64bf97: Added the ability to configure the host for the `GitHubEntityProvider` to use against GitHub Enterprise +- 91e2abbd46: Remove the duplicated `scheduleFn` initialization in `GitHubEntityProvider`. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.7-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index c27d3d4c60..278f108a7b 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.1.7-next.3", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,16 +33,16 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-plugin-api": "^0.1.2-next.2", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/plugin-catalog-node": "^1.1.0-next.2", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/types": "workspace:^", "@octokit/graphql": "^5.0.0", "lodash": "^4.17.21", "msw": "^0.47.0", @@ -51,8 +51,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index ba2016b713..d6b3cfb3d7 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.1.7 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.7-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 56d6fd73e8..457be11480 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.1.7-next.3", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,14 +32,14 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "lodash": "^4.17.21", "msw": "^0.47.0", "node-fetch": "^2.6.7", @@ -47,8 +47,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", "@types/uuid": "^8.0.0" }, diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts index d0f2eb383d..385ce59bfb 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts @@ -153,7 +153,7 @@ describe('GitlabDiscoveryProcessor', () => { setupRequestMockHandlers(server); beforeAll(() => { - jest.useFakeTimers('modern'); + jest.useFakeTimers(); jest.setSystemTime(new Date(SERVER_TIME)); }); diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 6429576b05..7e21490644 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.3 + +### Patch Changes + +- 7fa54c3c77: Added ae-dir LDAP vendor to correctly extract DN. +- c54fcea9af: Now the `searchStream` method in LDAP client awaits the callbacks +- Updated dependencies + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.5.3-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 4d9de73a77..ea00832f98 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.3-next.2", + "version": "0.5.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "@types/ldapjs": "^2.2.0", "ldapjs": "^2.2.0", "lodash": "^4.17.21", @@ -45,7 +45,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 25ac8c2716..aa601f1c3f 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.4.2 + +### Patch Changes + +- c1d32d2b76: Fixed a bug in the `MicrosoftGraphEntityProvider` that ignored the `userExpand` and `groupExpand` configuration parameters +- d80aab31ae: Added $select attribute to user query +- a246d5a9b8: Read `queryMode` from the `microsoftGraphOrg` config +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 4c82b955fc: Fix typo +- Updated dependencies + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/backend-tasks@0.3.5 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + ## 0.4.2-next.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/config.d.ts b/plugins/catalog-backend-module-msgraph/config.d.ts index df41b581f9..9fe1cb81c9 100644 --- a/plugins/catalog-backend-module-msgraph/config.d.ts +++ b/plugins/catalog-backend-module-msgraph/config.d.ts @@ -144,6 +144,15 @@ export interface Config { */ clientSecret?: string; + /** + * By default, the Microsoft Graph API only provides the basic feature set + * for querying. Certain features are limited to advanced query capabilities + * (see https://docs.microsoft.com/en-us/graph/aad-advanced-queries) + * and need to be enabled. + * + * Some features like `$expand` are not available for advanced queries, though. + */ + queryMode?: string; user?: { /** * The "expand" argument to apply to users. @@ -230,6 +239,15 @@ export interface Config { */ clientSecret: string; + /** + * By default, the Microsoft Graph API only provides the basic feature set + * for querying. Certain features are limited to advanced query capabilities + * (see https://docs.microsoft.com/en-us/graph/aad-advanced-queries) + * and need to be enabled. + * + * Some features like `$expand` are not available for advanced queries, though. + */ + queryMode?: string; user?: { /** * The filter to apply to extract users. diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 93270570fb..5a5b90832e 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.4.2-next.3", + "version": "0.4.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ }, "dependencies": { "@azure/identity": "^2.1.0", - "@backstage/backend-tasks": "^0.3.5-next.1", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", "@microsoft/microsoft-graph-types": "^2.6.0", "@types/node-fetch": "^2.5.12", "lodash": "^4.17.21", @@ -47,9 +47,9 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", "msw": "^0.47.0" }, diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts index 8e2866df65..3331e3609d 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.test.ts @@ -162,6 +162,7 @@ describe('readProviderConfigs', () => { clientId: 'clientId', clientSecret: 'clientSecret', authority: 'https://login.example.com/', + queryMode: 'advanced', user: { expand: 'manager', filter: 'accountEnabled eq true', @@ -185,6 +186,7 @@ describe('readProviderConfigs', () => { clientId: 'clientId', clientSecret: 'clientSecret', authority: 'https://login.example.com/', + queryMode: 'advanced', userExpand: 'manager', userFilter: 'accountEnabled eq true', groupExpand: 'member', diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts index 2429b67c89..b8565e0d72 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/config.ts @@ -261,6 +261,15 @@ export function readProviderConfig( const groupSearch = config.getOptionalString('group.search'); const groupSelect = config.getOptionalStringArray('group.select'); + const queryMode = config.getOptionalString('queryMode'); + if ( + queryMode !== undefined && + queryMode !== 'basic' && + queryMode !== 'advanced' + ) { + throw new Error(`queryMode must be one of: basic, advanced`); + } + const userGroupMemberFilter = config.getOptionalString( 'userGroupMember.filter', ); @@ -300,6 +309,7 @@ export function readProviderConfig( groupFilter, groupSearch, groupSelect, + queryMode, userGroupMemberFilter, userGroupMemberSearch, }; diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index f9a1ad9ed9..81f356168e 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + ## 0.1.2-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 420a9d547e..f3081f575b 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.2-next.2", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,19 +33,19 @@ }, "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/plugin-catalog-node": "^1.1.0-next.2", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/types": "workspace:^", "winston": "^3.2.1", "yaml": "^2.1.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "openapi-types": "^12.0.0" }, "files": [ diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 07d434b7b2..d702df4af0 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-catalog-backend +## 1.4.0 + +### Minor Changes + +- dd395335bc: Allow unknown typed location from being registered via the location service by configuration settings +- 651c9d6800: The search index now does retain fields that have a very long value, but in the form of just a null. This makes it possible to at least filter for their existence. +- 6e63bc43f2: Added the `refresh` function to the Connection of the entity providers. + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 07dda0b746: Add optional value to `hasAnnotation` permission rule +- 243533ecdc: Added support to mysql on some raw queries +- ce77e78c93: Fixes a bug to be able to utilize refresh keys after the entity is loaded from cache +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 679f7c5e95: Include entity ref into error message when catalog policies fail +- 06e2b077a1: Limit the length of error messages that get written to the database and logs - to prevent performance issues +- 62788b2ee8: The experimental `CatalogProcessingExtensionPoint` now accepts multiple providers and processors at once. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-scaffolder-common@1.2.0 + - @backstage/plugin-catalog-common@1.0.6 + - @backstage/plugin-search-common@1.0.1 + ## 1.4.0-next.3 ### Minor Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index a92ebb9624..f18d396114 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -177,7 +177,7 @@ export const catalogConditions: Conditions<{ Entity, EntitiesSearchFilter, 'catalog-entity', - [annotation: string] + [annotation: string, value?: string | undefined] >; hasLabel: PermissionRule< Entity, @@ -448,7 +448,7 @@ export const permissionRules: { Entity, EntitiesSearchFilter, 'catalog-entity', - [annotation: string] + [annotation: string, value?: string | undefined] >; hasLabel: PermissionRule< Entity, diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 0b9d363b3a..f834b0e2e8 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.4.0-next.3", + "version": "1.4.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,20 +33,20 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-plugin-api": "^0.1.2-next.2", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-catalog-node": "^1.1.0-next.2", - "@backstage/plugin-permission-common": "^0.6.4-next.2", - "@backstage/plugin-permission-node": "^0.6.5-next.3", - "@backstage/plugin-scaffolder-common": "^1.2.0-next.1", - "@backstage/plugin-search-common": "^1.0.1-next.0", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", + "@backstage/plugin-scaffolder-common": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "@backstage/types": "workspace:^", "@types/express": "^4.17.6", "codeowners-utils": "^1.0.2", "core-js": "^3.6.5", @@ -69,10 +69,10 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/plugin-permission-common": "^0.6.4-next.2", - "@backstage/plugin-search-backend-node": "1.0.2-next.2", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-search-backend-node": "workspace:^", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/lodash": "^4.14.151", diff --git a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts index 8fd8114760..105ab03d90 100644 --- a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts +++ b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.test.ts @@ -387,6 +387,39 @@ describe('PlaceholderProcessor', () => { key: 'url:http://example.com/path-to-file.json', }); }); + + it('accepts arbitrary object as value', async () => { + const processor = new PlaceholderProcessor({ + resolvers: { + merge: async ({ value }) => { + if (value instanceof Object) { + return { merged: 'value', ...value }; + } + return value; + }, + }, + reader, + integrations, + }); + + const result = await processor.preProcessEntity( + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'n' }, + spec: { a: { $merge: { passed: 'value' } } }, + }, + { type: 'fake', target: 'http://example.com' }, + () => {}, + ); + + expect(result).toMatchObject({ + apiVersion: 'a', + kind: 'k', + metadata: { name: 'n' }, + spec: { a: { passed: 'value', merged: 'value' } }, + }); + }); }); describe('yamlPlaceholderResolver', () => { diff --git a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts index 39c9dc59ec..5e49c67915 100644 --- a/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/PlaceholderProcessor.ts @@ -111,12 +111,11 @@ export class PlaceholderProcessor implements CatalogProcessor { const resolverValue = data[keys[0]]; const resolver = this.options.resolvers[resolverKey]; - if (!resolver || typeof resolverValue !== 'string') { - // If there was no such placeholder resolver or if the value was not a - // string, we err on the side of safety and assume that this is - // something that's best left alone. For example, if the input contains - // JSONSchema, there may be "$ref": "#/definitions/node" nodes in the - // document. + if (!resolver) { + // If there was no such placeholder resolver, we err on the side of safety + // and assume that this is something that's best left alone. For example, if + // the input contains JSONSchema, there may be "$ref": "#/definitions/node" + // nodes in the document. return [data, false]; } diff --git a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts index 1596270907..609114be3d 100644 --- a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts +++ b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.test.ts @@ -49,6 +49,19 @@ describe('hasAnnotation permission rule', () => { 'backstage.io/test-annotation', ), ).toEqual(false); + expect( + hasAnnotation.apply( + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test-component', + }, + }, + 'backstage.io/test-annotation', + 'some value', + ), + ).toEqual(false); }); it('returns true when specified annotation is present', () => { @@ -69,6 +82,46 @@ describe('hasAnnotation permission rule', () => { ), ).toEqual(true); }); + + it('returns false when specified annotation has different than expected value', () => { + expect( + hasAnnotation.apply( + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test-component', + annotations: { + 'other-annotation': 'foo', + 'backstage.io/test-annotation': 'bar', + }, + }, + }, + 'backstage.io/test-annotation', + 'baz', + ), + ).toEqual(false); + }); + + it('returns true when specified annotation has expected value', () => { + expect( + hasAnnotation.apply( + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test-component', + annotations: { + 'other-annotation': 'foo', + 'backstage.io/test-annotation': 'bar', + }, + }, + }, + 'backstage.io/test-annotation', + 'bar', + ), + ).toEqual(true); + }); }); describe('toQuery', () => { @@ -78,4 +131,13 @@ describe('hasAnnotation permission rule', () => { }); }); }); + + it('returns an appropriate catalog-backend filter with values', () => { + expect( + hasAnnotation.toQuery('backstage.io/test-annotation', 'foo'), + ).toEqual({ + key: 'metadata.annotations.backstage.io/test-annotation', + values: ['foo'], + }); + }); }); diff --git a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts index 68ca65418d..22dbd307a6 100644 --- a/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts +++ b/plugins/catalog-backend/src/permissions/rules/hasAnnotation.ts @@ -22,6 +22,8 @@ import { createCatalogPermissionRule } from './util'; * A catalog {@link @backstage/plugin-permission-node#PermissionRule} which * filters for the presence of an annotation on a given entity. * + * If a value is given, it filters for the annotation value, too. + * * @alpha */ export const hasAnnotation = createCatalogPermissionRule({ @@ -29,9 +31,18 @@ export const hasAnnotation = createCatalogPermissionRule({ description: 'Allow entities which are annotated with the specified annotation', resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - apply: (resource: Entity, annotation: string) => - !!resource.metadata.annotations?.hasOwnProperty(annotation), - toQuery: (annotation: string) => ({ - key: `metadata.annotations.${annotation}`, - }), + apply: (resource: Entity, annotation: string, value?: string) => + !!resource.metadata.annotations?.hasOwnProperty(annotation) && + (value === undefined + ? true + : resource.metadata.annotations?.[annotation] === value), + toQuery: (annotation: string, value?: string) => + value === undefined + ? { + key: `metadata.annotations.${annotation}`, + } + : { + key: `metadata.annotations.${annotation}`, + values: [value], + }, }); diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index 25f9fde313..d47ace1454 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -409,7 +409,11 @@ export class CatalogBuilder { parser, policy, }); - const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog(dbClient); + const stitcher = new Stitcher(dbClient, logger); + const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog( + dbClient, + stitcher, + ); let permissionEvaluator: PermissionEvaluator; if ('authorizeConditional' in permissions) { @@ -453,7 +457,6 @@ export class CatalogBuilder { permissions: catalogPermissions, rules: this.permissionRules, }); - const stitcher = new Stitcher(dbClient, logger); const locationStore = new DefaultLocationStore(dbClient); const configLocationProvider = new ConfigLocationEntityProvider(config); diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 43e208197f..49d4fe9e19 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -25,12 +25,15 @@ import { DbRefreshStateRow, DbSearchRow, } from '../database/tables'; +import { Stitcher } from '../stitching/Stitcher'; import { DefaultEntitiesCatalog } from './DefaultEntitiesCatalog'; describe('DefaultEntitiesCatalog', () => { const databases = TestDatabases.create({ ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], }); + const stitch = jest.fn(); + const stitcher: Stitcher = { stitch } as any; async function createDatabase(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); @@ -122,6 +125,10 @@ describe('DefaultEntitiesCatalog', () => { ); } + afterEach(() => { + jest.resetAllMocks(); + }); + describe('entityAncestry', () => { it.each(databases.eachSupportedId())( 'should return the ancestry with one parent, %p', @@ -151,7 +158,7 @@ describe('DefaultEntitiesCatalog', () => { await addEntity(knex, parent, [{ entity: grandparent }]); await addEntity(knex, root, [{ entity: parent }]); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const result = await catalog.entityAncestry('k:default/root'); expect(result.rootEntityRef).toEqual('k:default/root'); @@ -181,7 +188,7 @@ describe('DefaultEntitiesCatalog', () => { 'should throw error if the entity does not exist, %p', async databaseId => { const { knex } = await createDatabase(databaseId); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); await expect(() => catalog.entityAncestry('k:default/root'), ).rejects.toThrow('No such entity k:default/root'); @@ -224,7 +231,7 @@ describe('DefaultEntitiesCatalog', () => { await addEntity(knex, parent2, [{ entity: grandparent }]); await addEntity(knex, root, [{ entity: parent1 }, { entity: parent2 }]); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const result = await catalog.entityAncestry('k:default/root'); expect(result.rootEntityRef).toEqual('k:default/root'); @@ -280,7 +287,7 @@ describe('DefaultEntitiesCatalog', () => { }; await addEntityToSearch(knex, entity1); await addEntityToSearch(knex, entity2); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const testFilter = { key: 'spec.test', @@ -313,7 +320,7 @@ describe('DefaultEntitiesCatalog', () => { }; await addEntityToSearch(knex, entity1); await addEntityToSearch(knex, entity2); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const testFilter = { not: { @@ -360,7 +367,7 @@ describe('DefaultEntitiesCatalog', () => { await addEntityToSearch(knex, entity2); await addEntityToSearch(knex, entity3); await addEntityToSearch(knex, entity4); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const testFilter1 = { key: 'metadata.org', @@ -415,7 +422,7 @@ describe('DefaultEntitiesCatalog', () => { }; await addEntityToSearch(knex, entity1); await addEntityToSearch(knex, entity2); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const testFilter1 = { key: 'metadata.org', @@ -457,7 +464,7 @@ describe('DefaultEntitiesCatalog', () => { }; await addEntityToSearch(knex, entity1); await addEntityToSearch(knex, entity2); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const testFilter = { key: 'kind', @@ -501,7 +508,7 @@ describe('DefaultEntitiesCatalog', () => { }, [], ); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); const { entities } = await catalog.entities(); @@ -557,10 +564,16 @@ describe('DefaultEntitiesCatalog', () => { metadata: { name: 'root' }, spec: {}, }; - const unrelated: Entity = { + const unrelated1: Entity = { apiVersion: 'a', kind: 'k', - metadata: { name: 'unrelated' }, + metadata: { name: 'unrelated1' }, + spec: {}, + }; + const unrelated2: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'unrelated2' }, spec: {}, }; @@ -571,10 +584,23 @@ describe('DefaultEntitiesCatalog', () => { { entity: parent1 }, { entity: parent2 }, ]); - await addEntity(knex, unrelated, []); + await addEntity(knex, unrelated1, []); + await addEntity(knex, unrelated2, []); await knex('refresh_state').update({ result_hash: 'not-changed' }); + await knex('relations').insert({ + originating_entity_id: uid, + type: 't', + source_entity_ref: 'k:default/root', + target_entity_ref: 'k:default/unrelated1', + }); + await knex('relations').insert({ + originating_entity_id: uid, + type: 't', + source_entity_ref: 'k:default/unrelated2', + target_entity_ref: 'k:default/root', + }); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); await catalog.removeEntityByUid(uid); await expect( @@ -586,8 +612,12 @@ describe('DefaultEntitiesCatalog', () => { { entity_ref: 'k:default/grandparent', result_hash: 'not-changed' }, { entity_ref: 'k:default/parent1', result_hash: 'child-was-deleted' }, { entity_ref: 'k:default/parent2', result_hash: 'child-was-deleted' }, - { entity_ref: 'k:default/unrelated', result_hash: 'not-changed' }, + { entity_ref: 'k:default/unrelated1', result_hash: 'not-changed' }, + { entity_ref: 'k:default/unrelated2', result_hash: 'not-changed' }, ]); + expect(stitch).toHaveBeenCalledWith( + new Set(['k:default/unrelated1', 'k:default/unrelated2']), + ); }, ); }); @@ -616,7 +646,7 @@ describe('DefaultEntitiesCatalog', () => { metadata: { name: 'two' }, spec: {}, }); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); await expect(catalog.facets({ facets: ['kind'] })).resolves.toEqual({ facets: { @@ -654,7 +684,7 @@ describe('DefaultEntitiesCatalog', () => { }, spec: {}, }); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); await expect( catalog.facets({ @@ -698,7 +728,7 @@ describe('DefaultEntitiesCatalog', () => { }, spec: {}, }); - const catalog = new DefaultEntitiesCatalog(knex); + const catalog = new DefaultEntitiesCatalog(knex, stitcher); await expect( catalog.facets({ diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index fa11903447..dc84b12484 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -38,8 +38,10 @@ import { DbPageInfo, DbRefreshStateReferencesRow, DbRefreshStateRow, + DbRelationsRow, DbSearchRow, } from '../database/tables'; +import { Stitcher } from '../stitching/Stitcher'; function parsePagination(input?: EntityPagination): { limit?: number; @@ -158,7 +160,10 @@ function parseFilter( } export class DefaultEntitiesCatalog implements EntitiesCatalog { - constructor(private readonly database: Knex) {} + constructor( + private readonly database: Knex, + private readonly stitcher: Stitcher, + ) {} async entities(request?: EntitiesRequest): Promise { const db = this.database; @@ -244,6 +249,7 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { await this.database('refresh_state') .update({ result_hash: 'child-was-deleted', + next_update_at: this.database.fn.now(), }) .whereIn('entity_ref', function parents(builder) { return builder @@ -256,9 +262,35 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { .select('refresh_state_references.source_entity_ref'); }); + // Stitch the entities that the deleted one had relations to. If we do not + // do this, the entities in the other end of the relations will still look + // like they have a relation to the entity that was deleted, despite not + // having any corresponding rows in the relations table. + const relationPeers = await this.database + .from('relations') + .innerJoin('refresh_state', { + 'refresh_state.entity_ref': 'relations.target_entity_ref', + }) + .where('relations.originating_entity_id', '=', uid) + .andWhere('refresh_state.entity_id', '!=', uid) + .select({ ref: 'relations.target_entity_ref' }) + .union(other => + other + .from('relations') + .innerJoin('refresh_state', { + 'refresh_state.entity_ref': 'relations.source_entity_ref', + }) + .where('relations.originating_entity_id', '=', uid) + .andWhere('refresh_state.entity_id', '!=', uid) + .select({ ref: 'relations.source_entity_ref' }), + ); + + // Perform the actual deletion await this.database('refresh_state') .where('entity_id', uid) .delete(); + + await this.stitcher.stitch(new Set(relationPeers.map(p => p.ref))); } async entityAncestry(rootRef: string): Promise { diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index 45d1e9bbec..57600eb6a5 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-common +## 1.0.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-search-common@1.0.1 + ## 1.0.6-next.0 ### Patch Changes diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 32a34ec1c0..357636341a 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-common", "description": "Common functionalities for the catalog plugin", - "version": "1.0.6-next.0", + "version": "1.0.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-permission-common": "^0.6.4-next.0", - "@backstage/plugin-search-common": "^1.0.1-next.0" + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-search-common": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1" + "@backstage/cli": "workspace:^" }, "files": [ "dist", diff --git a/plugins/catalog-customized/CHANGELOG.md b/plugins/catalog-customized/CHANGELOG.md index d8c95242db..409c007eea 100644 --- a/plugins/catalog-customized/CHANGELOG.md +++ b/plugins/catalog-customized/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-catalog-customized +## 0.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-catalog@1.5.1 + ## 0.0.2-next.0 ### Patch Changes diff --git a/plugins/catalog-customized/package.json b/plugins/catalog-customized/package.json index deed4a5885..a8ffbd7494 100644 --- a/plugins/catalog-customized/package.json +++ b/plugins/catalog-customized/package.json @@ -1,7 +1,7 @@ { "name": "@internal/plugin-catalog-customized", "description": "The internal Backstage Customizable plugin for browsing the Backstage catalog", - "version": "0.0.2-next.0", + "version": "0.0.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,8 +33,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-catalog": "^1.5.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.0" + "@backstage/plugin-catalog": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^" }, "devDependencies": { "@types/react": "^16.13.1 || ^17.0.0" diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 5fbaa9ecca..09a41b65c5 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-graph +## 0.2.21 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + ## 0.2.21-next.2 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 995ed3eebe..88a45c4bde 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.21-next.2", + "version": "0.2.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -43,12 +43,12 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/plugin-catalog": "^1.5.1-next.3", - "@backstage/test-utils": "^1.2.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/plugin-catalog": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@backstage/types": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index 28eafef43b..f2280d2f0a 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-graphql +## 0.3.13 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + ## 0.3.13-next.3 ### Patch Changes diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index b8c65b2784..7ad5fb68be 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-graphql", "description": "An experimental Backstage catalog GraphQL module", - "version": "0.3.13-next.3", + "version": "0.3.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,9 +34,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/types": "^1.0.0", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/types": "workspace:^", "apollo-server": "^3.0.0", "graphql": "^16.0.0", "graphql-modules": "^2.0.0", @@ -46,8 +46,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", "@graphql-codegen/cli": "^2.3.1", "@graphql-codegen/graphql-modules-preset": "^2.3.2", "@graphql-codegen/typescript": "^2.4.2", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 35633dca6a..a1c4d9145f 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-import +## 0.8.12 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + ## 0.8.12-next.3 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 895973f1ca..7ec45fc6ed 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.8.12-next.3", + "version": "0.8.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,15 +32,15 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -58,10 +58,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx index b439366728..3acb7390a9 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreparePullRequestForm.test.tsx @@ -87,7 +87,6 @@ describe('', () => { {formState.errors.main && ( diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 0c88527b97..f4c1782da2 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-node +## 1.1.0 + +### Minor Changes + +- 9743bc788c: Added refresh function to the `EntityProviderConnection` to be able to schedule refreshes from entity providers. + +### Patch Changes + +- 7d7d947352: Adds experimental `catalogServiceRef` for obtaining a `CatalogClient` in the new backend system. +- 409ed984e8: Updated usage of experimental backend service APIs. +- 62788b2ee8: The experimental `CatalogProcessingExtensionPoint` now accepts multiple providers and processors at once. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 1.1.0-next.2 ### Minor Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 2ef9a738fc..4bd1f5259b 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-node", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", - "version": "1.1.0-next.2", + "version": "1.1.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,16 +24,16 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-plugin-api": "^0.1.2-next.2", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/errors": "1.1.1-next.0", - "@backstage/types": "^1.0.0" + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/types": "workspace:^" }, "devDependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3" + "@backstage/backend-common": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^" }, "files": [ "dist", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index a0e7bfcae5..00710c2f45 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-react +## 1.1.4 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f6033d1121: humanizeEntityRef function can now be forced to include default namespace +- c86741a052: Support showing counts in option labels of the `EntityTagPicker`. You can enable this by adding the `showCounts` property +- 7d47def9c4: Removed dependency on `@types/jest`. +- a6d551fad9: Properly handle free-text entity filtering in the case of empty tag arrays +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-catalog-common@1.0.6 + ## 1.1.4-next.2 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 962c859c62..759aa6e4a9 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.1.4-next.2", + "version": "1.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,18 +33,18 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-permission-common": "^0.6.4-next.2", - "@backstage/plugin-permission-react": "^0.4.5-next.2", - "@backstage/theme": "^0.2.16", - "@backstage/types": "^1.0.0", - "@backstage/version-bridge": "^1.0.1", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", + "@backstage/version-bridge": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -62,11 +62,11 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-scaffolder-common": "^1.2.0-next.1", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-scaffolder-common": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 51507389cf..57a1c60174 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog +## 1.5.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-catalog-common@1.0.6 + - @backstage/plugin-search-common@1.0.1 + ## 1.5.1-next.3 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 2610c9db93..99361b6a3d 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog", "description": "The Backstage plugin for browsing the Backstage catalog", - "version": "1.5.1-next.3", + "version": "1.5.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,18 +32,18 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-search-common": "^1.0.1-next.0", - "@backstage/plugin-search-react": "^1.1.0-next.2", - "@backstage/theme": "^0.2.16", - "@backstage/types": "^1.0.0", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "@backstage/plugin-search-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -59,11 +59,11 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/plugin-permission-react": "^0.4.5-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md index 32ababf2d9..01dd968ad6 100644 --- a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md +++ b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-cicd-statistics-module-gitlab +## 0.1.5 + +### Patch Changes + +- 71b7464a7c: Fixing typo in the Readme file: `'@backstage plugin-cicd-statistics-module-gitlab';` -> `'@backstage/plugin-cicd-statistics-module-gitlab';` +- a98f5d4c21: add the ability to add CICD default options to createGitlabApi constructor +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + - @backstage/plugin-cicd-statistics@0.1.11 + ## 0.1.5-next.1 ### Patch Changes diff --git a/plugins/cicd-statistics-module-gitlab/README.md b/plugins/cicd-statistics-module-gitlab/README.md index 0b11f6950a..67da1c2291 100644 --- a/plugins/cicd-statistics-module-gitlab/README.md +++ b/plugins/cicd-statistics-module-gitlab/README.md @@ -7,13 +7,14 @@ This is an extension module to the `cicd-statistics` plugin, providing a `CicdSt 1. Install the `cicd-statistics` and `cicd-statistics-module-gitlab` plugins in the `app` package. 2. Configure your ApiFactory: + - You can optionally pass in a second argument to `CicdStatisticsApiGitlab` of type [CicdDefaults](https://github.com/backstage/backstage/blob/2881c53cb383bf127c150f837f37fe535d8cf97b/plugins/cicd-statistics/src/apis/types.ts#L179) to alter the default CICD UI configuration ```tsx // packages/app/src/apis.ts import { gitlabAuthApiRef } from '@backstage/core-plugin-api'; import { cicdStatisticsApiRef } from '@backstage/plugin-cicd-statistics'; -import { CicdStatisticsApiGitlab } from '@backstage plugin-cicd-statistics-module-gitlab'; +import { CicdStatisticsApiGitlab } from '@backstage/plugin-cicd-statistics-module-gitlab'; export const apis: AnyApiFactory[] = [ createApiFactory({ diff --git a/plugins/cicd-statistics-module-gitlab/api-report.md b/plugins/cicd-statistics-module-gitlab/api-report.md index 5e07255f54..a0364afa31 100644 --- a/plugins/cicd-statistics-module-gitlab/api-report.md +++ b/plugins/cicd-statistics-module-gitlab/api-report.md @@ -4,6 +4,7 @@ ```ts import { CicdConfiguration } from '@backstage/plugin-cicd-statistics'; +import { CicdDefaults } from '@backstage/plugin-cicd-statistics'; import { CicdState } from '@backstage/plugin-cicd-statistics'; import { CicdStatisticsApi } from '@backstage/plugin-cicd-statistics'; import { Entity } from '@backstage/catalog-model'; @@ -13,7 +14,7 @@ import { OAuthApi } from '@backstage/core-plugin-api'; // @public export class CicdStatisticsApiGitlab implements CicdStatisticsApi { - constructor(gitLabAuthApi: OAuthApi); + constructor(gitLabAuthApi: OAuthApi, cicdDefaults?: Partial); // (undocumented) createGitlabApi(entity: Entity, scopes: string[]): Promise; // (undocumented) diff --git a/plugins/cicd-statistics-module-gitlab/package.json b/plugins/cicd-statistics-module-gitlab/package.json index ee8eaed465..a8c4ef7dec 100644 --- a/plugins/cicd-statistics-module-gitlab/package.json +++ b/plugins/cicd-statistics-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics-module-gitlab", "description": "CI/CD Statistics plugin module; Gitlab CICD", - "version": "0.1.5-next.1", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,16 +28,16 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-cicd-statistics": "^0.1.11-next.2", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-cicd-statistics": "workspace:^", "@gitbeaker/browser": "^35.6.0", "@gitbeaker/core": "^35.6.0", "luxon": "^3.0.0", "p-limit": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/plugins/cicd-statistics-module-gitlab/src/api/gitlab.ts b/plugins/cicd-statistics-module-gitlab/src/api/gitlab.ts index 7439d1800c..5e4384fe88 100644 --- a/plugins/cicd-statistics-module-gitlab/src/api/gitlab.ts +++ b/plugins/cicd-statistics-module-gitlab/src/api/gitlab.ts @@ -18,6 +18,7 @@ import { CicdStatisticsApi, CicdState, CicdConfiguration, + CicdDefaults, Build, FetchBuildsOptions, Stage, @@ -47,9 +48,14 @@ export type GitlabClient = { */ export class CicdStatisticsApiGitlab implements CicdStatisticsApi { readonly #gitLabAuthApi: OAuthApi; + readonly #cicdDefaults: Partial; - constructor(gitLabAuthApi: OAuthApi) { + constructor( + gitLabAuthApi: OAuthApi, + cicdDefaults: Partial = {}, + ) { this.#gitLabAuthApi = gitLabAuthApi; + this.#cicdDefaults = cicdDefaults; } public async createGitlabApi( @@ -154,6 +160,7 @@ export class CicdStatisticsApiGitlab implements CicdStatisticsApi { 'expired', 'unknown', ] as const, + defaults: this.#cicdDefaults, }; } } diff --git a/plugins/cicd-statistics/CHANGELOG.md b/plugins/cicd-statistics/CHANGELOG.md index ac1cd23660..6bda1cf68d 100644 --- a/plugins/cicd-statistics/CHANGELOG.md +++ b/plugins/cicd-statistics/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-cicd-statistics +## 0.1.11 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.1.11-next.2 ### Patch Changes diff --git a/plugins/cicd-statistics/package.json b/plugins/cicd-statistics/package.json index 6394baaf94..1e1a7f95ad 100644 --- a/plugins/cicd-statistics/package.json +++ b/plugins/cicd-statistics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics", "description": "A frontend plugin visualizing CI/CD pipeline statistics (build time)", - "version": "0.1.11-next.2", + "version": "0.1.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,17 +32,17 @@ "start": "backstage-cli package start" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/luxon": "^3.0.0", "@types/react": "^16.13.1 || ^17.0.0" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", "@date-io/luxon": "^1.3.13", "@material-ui/core": "^4.9.13", - "@material-ui/icons": "^4.11.2", + "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/pickers": "^3.3.10", "already": "^3.2.0", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index 44ccf89c42..f020e67b0f 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-circleci +## 0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.3.9-next.3 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 229ff9fb11..7363bd98f0 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-circleci", "description": "A Backstage plugin that integrates towards Circle CI", - "version": "0.3.9-next.3", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -53,10 +53,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index 35a8a675e0..5c01b5be4b 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-cloudbuild +## 0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.3.9-next.3 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 6215547041..3b20b28c3d 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cloudbuild", "description": "A Backstage plugin that integrates towards Google Cloud Build", - "version": "0.3.9-next.3", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -51,10 +51,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/code-climate/CHANGELOG.md b/plugins/code-climate/CHANGELOG.md index 3d45e5c3a2..418421aafe 100644 --- a/plugins/code-climate/CHANGELOG.md +++ b/plugins/code-climate/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-code-climate +## 0.1.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.1.9-next.3 ### Patch Changes diff --git a/plugins/code-climate/package.json b/plugins/code-climate/package.json index 464142f680..1863e3e080 100644 --- a/plugins/code-climate/package.json +++ b/plugins/code-climate/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-code-climate", - "version": "0.1.9-next.3", + "version": "0.1.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -39,9 +39,9 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index 2d440fd31b..9001fbeab0 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-code-coverage-backend +## 0.2.2 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 8872cc735d: Fixed a bug where the database option to skip migrations was ignored. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.2.2-next.2 ### Patch Changes diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index bbc5714fb8..3cf9b2f087 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage-backend", "description": "A Backstage backend plugin that helps you keep track of your code coverage", - "version": "0.2.2-next.2", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,12 +23,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -39,7 +39,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/express-xml-bodyparser": "^0.3.2", "@types/supertest": "^2.0.8", "msw": "^0.47.0", diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index 9e120afd38..b8e384fbd7 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-code-coverage +## 0.2.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.2.2-next.3 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index c3f992a428..ab4e071ccc 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage", "description": "A Backstage plugin that helps you keep track of your code coverage", - "version": "0.2.2-next.3", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,13 +23,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -45,10 +45,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/codescene/CHANGELOG.md b/plugins/codescene/CHANGELOG.md index aa43d5edd3..b5d70d9d17 100644 --- a/plugins/codescene/CHANGELOG.md +++ b/plugins/codescene/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-codescene +## 0.1.4 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.4-next.3 ### Patch Changes diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index bc709a215c..884b751347 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-codescene", - "version": "0.1.4-next.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/theme": "^0.2.16", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.10", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.57", @@ -38,10 +38,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 3e7483e879..88d12a9da1 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-config-schema +## 0.1.32 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.1.32-next.3 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 7f650fb4bc..cf2cb6cf90 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-config-schema", "description": "A Backstage plugin that lets you browse the configuration schema of your app", - "version": "0.1.32-next.3", + "version": "0.1.32", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,12 +23,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/theme": "^0.2.16", - "@backstage/types": "^1.0.0", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -40,10 +40,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/cost-insights-common/package.json b/plugins/cost-insights-common/package.json index ff458b0a5c..d675e0a548 100644 --- a/plugins/cost-insights-common/package.json +++ b/plugins/cost-insights-common/package.json @@ -32,7 +32,7 @@ "clean": "backstage-cli package clean" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index fec45acdd1..1bb7d5aa75 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-cost-insights +## 0.11.31 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 9318bf15d4: The `CostInsightsHeader`component now uses group names if available +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + ## 0.11.31-next.3 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 8034195d62..fc128f405f 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.11.31-next.3", + "version": "0.11.31", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-cost-insights-common": "^0.1.1", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-cost-insights-common": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -59,10 +59,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewBreakdownChart.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewBreakdownChart.tsx index d0731326f2..5b912c09a6 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewBreakdownChart.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewBreakdownChart.tsx @@ -113,8 +113,8 @@ export const CostOverviewBreakdownChart = ({ {} as Record>, ); - const chartData: Record[] = Object.keys(breakdownsByDate).map( - date => { + const chartData: Record[] = Object.keys(breakdownsByDate) + .map(date => { const costsForDate = Object.keys(breakdownsByDate[date]).reduce( (dateCosts, breakdown) => { // Group costs for items that belong to 'Other' in the chart. @@ -131,8 +131,8 @@ export const CostOverviewBreakdownChart = ({ ...costsForDate, date: Date.parse(date), }; - }, - ); + }) + .sort((a, b) => a.date - b.date); const sortedBreakdowns = costBreakdown.sort( (a, b) => aggregationSum(a.aggregation) - aggregationSum(b.aggregation), diff --git a/plugins/dynatrace/CHANGELOG.md b/plugins/dynatrace/CHANGELOG.md index 33e547a2bf..ae367016e3 100644 --- a/plugins/dynatrace/CHANGELOG.md +++ b/plugins/dynatrace/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-dynatrace +## 0.2.0 + +### Minor Changes + +- e44c0b3811: New features: + + - Some visual improvements to the table that displays Problems + - Added support for viewing recent Synthetics results using + - Added some additional linking to the configured Dynatrace instance + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/catalog-model@1.1.1 + ## 0.2.0-next.3 ### Patch Changes diff --git a/plugins/dynatrace/package.json b/plugins/dynatrace/package.json index 3df69ca25f..d8b3cb06a8 100644 --- a/plugins/dynatrace/package.json +++ b/plugins/dynatrace/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-dynatrace", - "version": "0.2.0-next.3", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,24 +22,24 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "react-use": "^17.2.4" }, "peerDependencies": { - "@backstage/plugin-catalog-react": "^1.1.4-next.0", + "@backstage/plugin-catalog-react": "workspace:^", "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index f3bdba1397..5bc46323d8 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @internal/plugin-todo-list-backend +## 1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 1.0.5-next.1 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 5623c22f58..295aad4021 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.5-next.1", + "version": "1.0.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,10 +23,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-auth-node": "^0.2.5-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -35,7 +35,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", "msw": "^0.47.0", diff --git a/plugins/example-todo-list-common/CHANGELOG.md b/plugins/example-todo-list-common/CHANGELOG.md index ed40a99c3e..3754f768ab 100644 --- a/plugins/example-todo-list-common/CHANGELOG.md +++ b/plugins/example-todo-list-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @internal/plugin-todo-list-common +## 1.0.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + ## 1.0.4-next.0 ### Patch Changes diff --git a/plugins/example-todo-list-common/api-report.md b/plugins/example-todo-list-common/api-report.md index 15ffb7b139..1e51e560c8 100644 --- a/plugins/example-todo-list-common/api-report.md +++ b/plugins/example-todo-list-common/api-report.md @@ -8,5 +8,8 @@ import { BasicPermission } from '@backstage/plugin-permission-common'; // @public export const tempExamplePermission: BasicPermission; +// @public +export const todoListPermissions: BasicPermission[]; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/example-todo-list-common/package.json b/plugins/example-todo-list-common/package.json index 564d56e784..32dfe96ef4 100644 --- a/plugins/example-todo-list-common/package.json +++ b/plugins/example-todo-list-common/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-common", - "version": "1.0.4-next.0", + "version": "1.0.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,13 +23,13 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/plugin-permission-common": "^0.6.4-next.0" + "@backstage/plugin-permission-common": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1", - "@backstage/core-app-api": "^1.1.0-next.1", - "@backstage/dev-utils": "^1.0.6-next.1", - "@backstage/test-utils": "^1.2.0-next.1", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", "msw": "^0.47.0" diff --git a/plugins/example-todo-list-common/src/permissions.ts b/plugins/example-todo-list-common/src/permissions.ts index 439bf5e8b9..ea0b097376 100644 --- a/plugins/example-todo-list-common/src/permissions.ts +++ b/plugins/example-todo-list-common/src/permissions.ts @@ -25,3 +25,10 @@ export const tempExamplePermission = createPermission({ name: 'temp.example.noop', attributes: {}, }); + +/** + * List of all todo list permissions. + * + * @public + */ +export const todoListPermissions = [tempExamplePermission]; diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index b096ccfa4e..be969303aa 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list +## 1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 1.0.5-next.0 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index 07cf546d1d..9b66c0c0a9 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.5-next.0", + "version": "1.0.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,9 +23,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.1", - "@backstage/core-plugin-api": "^1.0.6-next.1", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -35,10 +35,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1", - "@backstage/core-app-api": "^1.1.0-next.1", - "@backstage/dev-utils": "^1.0.6-next.1", - "@backstage/test-utils": "^1.2.0-next.1", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/example-todo-list/src/components/TodoListPage/TodoListPage.tsx b/plugins/example-todo-list/src/components/TodoListPage/TodoListPage.tsx index 7ebb517532..6071502fdb 100644 --- a/plugins/example-todo-list/src/components/TodoListPage/TodoListPage.tsx +++ b/plugins/example-todo-list/src/components/TodoListPage/TodoListPage.tsx @@ -45,17 +45,16 @@ export const TodoListPage = () => { const discoveryApi = useApi(discoveryApiRef); const { fetch } = useApi(fetchApiRef); const alertApi = useApi(alertApiRef); - const title = useRef(''); const [key, refetchTodos] = useReducer(i => i + 1, 0); const [editElement, setEdit] = useState(); - const handleAdd = async () => { + const handleAdd = async (title: string) => { try { const response = await fetch( `${await discoveryApi.getBaseUrl('todolist')}/todos`, { method: 'POST', - body: JSON.stringify({ title: title.current }), + body: JSON.stringify({ title }), headers: { 'Content-Type': 'application/json', }, @@ -117,21 +116,7 @@ export const TodoListPage = () => { - Add todo - - (title.current = e.target.value)} - /> - - + @@ -149,6 +134,30 @@ export const TodoListPage = () => { ); }; +function AddTodo({ onAdd }: { onAdd: (title: string) => any }) { + const title = useRef(''); + + return ( + <> + Add todo + + (title.current = e.target.value)} + /> + + + + ); +} + function EditModal({ todo, onCancel, diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index 0a1d2c795d..e35dae9e32 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-explore-react +## 0.0.21 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + ## 0.0.21-next.3 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index 1e715965a8..66ac17f785 100644 --- a/plugins/explore-react/package.json +++ b/plugins/explore-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore-react", "description": "A frontend library for Backstage plugins that want to interact with the explore plugin", - "version": "0.0.21-next.3", + "version": "0.0.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/core-plugin-api": "^1.0.6-next.3" + "@backstage/core-plugin-api": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index c558be8b63..3e803d2d6b 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-explore +## 0.3.40 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/plugin-explore-react@0.0.21 + ## 0.3.40-next.3 ### Patch Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index c0a5453c02..b9b9257a3a 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore", "description": "A Backstage plugin for building an exploration page of your software ecosystem", - "version": "0.3.40-next.3", + "version": "0.3.40", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,12 +32,12 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-explore-react": "^0.0.21-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-explore-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -51,10 +51,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/firehydrant/CHANGELOG.md b/plugins/firehydrant/CHANGELOG.md index 1e55a1fdfa..eae65d3c36 100644 --- a/plugins/firehydrant/CHANGELOG.md +++ b/plugins/firehydrant/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-firehydrant +## 0.1.26 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + ## 0.1.26-next.3 ### Patch Changes diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index bc10c957c6..37aa7b259b 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-firehydrant", "description": "A Backstage plugin that integrates towards FireHydrant", - "version": "0.1.26-next.3", + "version": "0.1.26", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,10 +23,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -37,10 +37,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index 05ec48b992..3126dbc0e1 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-fossa +## 0.2.41 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.2.41-next.2 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index c96353f3d6..bf249c1b9d 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-fossa", "description": "A Backstage plugin that integrates towards FOSSA", - "version": "0.2.41-next.2", + "version": "0.2.41", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,12 +33,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -51,10 +51,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/gcalendar/CHANGELOG.md b/plugins/gcalendar/CHANGELOG.md index f87f087880..2be25841e7 100644 --- a/plugins/gcalendar/CHANGELOG.md +++ b/plugins/gcalendar/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-gcalendar +## 0.3.5 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- a69ac5b63a: Upgrade `react-query:3` to `@tanstack/react-query:4` +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/errors@1.1.1 + ## 0.3.5-next.3 ### Patch Changes diff --git a/plugins/gcalendar/package.json b/plugins/gcalendar/package.json index fbcc9b0c33..9ef566e95c 100644 --- a/plugins/gcalendar/package.json +++ b/plugins/gcalendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcalendar", - "version": "0.3.5-next.3", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,10 +22,10 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -43,10 +43,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index cbd55e8414..d4e54838c4 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-gcp-projects +## 0.3.28 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 0.3.28-next.3 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 601d491184..d2926de41b 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gcp-projects", "description": "A Backstage plugin that helps you manage projects in GCP", - "version": "0.3.28-next.3", + "version": "0.3.28", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,9 +33,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -46,10 +46,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index 1dc0bd72bd..0c7f930e60 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-git-release-manager +## 0.3.22 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/integration@1.3.1 + ## 0.3.22-next.3 ### Patch Changes diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index fb56a6d0e1..89061cc510 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-git-release-manager", "description": "A Backstage plugin that helps you manage releases in git", - "version": "0.3.22-next.3", + "version": "0.3.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,10 +23,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -42,10 +42,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/git-release-manager/src/api/GitReleaseClient.test.ts b/plugins/git-release-manager/src/api/GitReleaseClient.test.ts index 418da485a2..2b1877d487 100644 --- a/plugins/git-release-manager/src/api/GitReleaseClient.test.ts +++ b/plugins/git-release-manager/src/api/GitReleaseClient.test.ts @@ -52,7 +52,7 @@ describe('GitReleaseClient', () => { "getRepository": [Function], "getTag": [Function], "getUser": [Function], - "githubAuthApi": Object { + "githubAuthApi": { "getAccessToken": [MockFunction], }, "host": "github.com", diff --git a/plugins/git-release-manager/src/api/serviceApiRef.test.ts b/plugins/git-release-manager/src/api/serviceApiRef.test.ts index b27738671d..0ac1c89265 100644 --- a/plugins/git-release-manager/src/api/serviceApiRef.test.ts +++ b/plugins/git-release-manager/src/api/serviceApiRef.test.ts @@ -22,7 +22,7 @@ describe('gitReleaseManagerApiRef', () => { expect(result).toMatchInlineSnapshot(` ApiRefImpl { - "config": Object { + "config": { "id": "plugin.git-release-manager.service", }, } diff --git a/plugins/git-release-manager/src/constants/constants.test.ts b/plugins/git-release-manager/src/constants/constants.test.ts index fb475bd577..49b5f90c7f 100644 --- a/plugins/git-release-manager/src/constants/constants.test.ts +++ b/plugins/git-release-manager/src/constants/constants.test.ts @@ -19,19 +19,19 @@ import * as constants from './constants'; describe('constants', () => { it('should match snapshot', () => { expect(constants).toMatchInlineSnapshot(` - Object { - "DISABLE_CACHE": Object { - "headers": Object { + { + "DISABLE_CACHE": { + "headers": { "If-None-Match": "", }, }, - "SEMVER_PARTS": Object { + "SEMVER_PARTS": { "major": "major", "minor": "minor", "patch": "patch", }, "TAG_OBJECT_MESSAGE": "Tag generated by your friendly neighborhood Backstage Release Manager", - "VERSIONING_STRATEGIES": Object { + "VERSIONING_STRATEGIES": { "calver": "calver", "semver": "semver", }, diff --git a/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.test.tsx b/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.test.tsx index 3128fde9c8..25a987df2a 100644 --- a/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.test.tsx +++ b/plugins/git-release-manager/src/features/CreateReleaseCandidate/hooks/useCreateReleaseCandidate.test.tsx @@ -72,37 +72,37 @@ describe('useCreateReleaseCandidate', () => { expect(result.current.responseSteps).toHaveLength(7); expect(result.current).toMatchInlineSnapshot(` - Object { + { "progress": 100, - "responseSteps": Array [ - Object { + "responseSteps": [ + { "link": "https://latestCommit.html_url", - "message": "Fetched latest commit from \\"mock_defaultBranch\\"", - "secondaryMessage": "with message \\"latestCommit.commit.message\\"", + "message": "Fetched latest commit from "mock_defaultBranch"", + "secondaryMessage": "with message "latestCommit.commit.message"", }, - Object { + { "message": "Created Release Branch", - "secondaryMessage": "with ref \\"mock_createRef_ref\\"", + "secondaryMessage": "with ref "mock_createRef_ref"", }, - Object { + { "message": "Created Tag Object", - "secondaryMessage": "with sha \\"mock_tag_object_sha\\"", + "secondaryMessage": "with sha "mock_tag_object_sha"", }, - Object { + { "message": "Cut Tag Reference", - "secondaryMessage": "with ref \\"mock_createRef_ref\\"", + "secondaryMessage": "with ref "mock_createRef_ref"", }, - Object { + { "link": "https://mock_compareCommits_html_url", "message": "Fetched commit comparison", "secondaryMessage": "rc/2020.01.01_1...rc/2020.01.01_1", }, - Object { + { "link": "https://mock_createRelease_html_url", - "message": "Created Release Candidate \\"mock_createRelease_name\\"", - "secondaryMessage": "with tag \\"rc-2020.01.01_1\\"", + "message": "Created Release Candidate "mock_createRelease_name"", + "secondaryMessage": "with tag "rc-2020.01.01_1"", }, - Object { + { "icon": "success", "message": "Success callback successfully called 🚀", }, diff --git a/plugins/git-release-manager/src/features/Patch/hooks/usePatch.test.ts b/plugins/git-release-manager/src/features/Patch/hooks/usePatch.test.ts index f3b94c2476..0bd7f27cdb 100644 --- a/plugins/git-release-manager/src/features/Patch/hooks/usePatch.test.ts +++ b/plugins/git-release-manager/src/features/Patch/hooks/usePatch.test.ts @@ -75,91 +75,91 @@ describe('patch', () => { expect(result.error).toEqual(undefined); expect(result.current.responseSteps).toHaveLength(19); expect(result.current).toMatchInlineSnapshot(` - Object { + { "progress": 100, - "responseSteps": Array [ - Object { + "responseSteps": [ + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "message": , }, - Object { + { "link": "https://mock_branch_links_html", - "message": "Fetched release branch \\"rc/1.2.3\\"", + "message": "Fetched release branch "rc/1.2.3"", }, - Object { + { "message": "Created temporary commit", - "secondaryMessage": "with message \\"mock_commit_message\\"", + "secondaryMessage": "with message "mock_commit_message"", }, - Object { - "message": "Forced branch \\"rc/2020.01.01_1\\" to temporary commit \\"mock_commit_sha\\"", + { + "message": "Forced branch "rc/2020.01.01_1" to temporary commit "mock_commit_sha"", }, - Object { + { "link": "https://mock_merge_html_url", - "message": "Merged temporary commit into \\"rc/2020.01.01_1\\"", - "secondaryMessage": "with message \\"mock_merge_commit_message\\"", + "message": "Merged temporary commit into "rc/2020.01.01_1"", + "secondaryMessage": "with message "mock_merge_commit_message"", }, - Object { - "message": "Cherry-picked patch commit to \\"mock_branch_commit_sha\\"", - "secondaryMessage": "with message \\"mock_commit_message\\"", + { + "message": "Cherry-picked patch commit to "mock_branch_commit_sha"", + "secondaryMessage": "with message "mock_commit_message"", }, - Object { - "message": "Updated reference \\"mock_update_ref_ref\\"", + { + "message": "Updated reference "mock_update_ref_ref"", }, - Object { + { "message": "Created new tag object", - "secondaryMessage": "with name \\"mock_tag_object_tag\\"", + "secondaryMessage": "with name "mock_tag_object_tag"", }, - Object { - "message": "Created new reference \\"mock_createRef_ref\\"", - "secondaryMessage": "for tag object \\"mock_tag_object_tag\\"", + { + "message": "Created new reference "mock_createRef_ref"", + "secondaryMessage": "for tag object "mock_tag_object_tag"", }, - Object { + { "link": "https://mock_update_release_html_url", - "message": "Updated release \\"mock_update_release_name\\"", + "message": "Updated release "mock_update_release_name"", "secondaryMessage": "with tag mock_update_release_tag_name", }, - Object { + { "icon": "success", "message": "Success callback successfully called 🚀", }, diff --git a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts index ff1c58b5be..f4e0fcb081 100644 --- a/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts +++ b/plugins/git-release-manager/src/features/PromoteRc/hooks/usePromoteRc.test.ts @@ -72,27 +72,27 @@ describe('usePromoteRc', () => { expect(result.current.responseSteps).toHaveLength(5); expect(result.current).toMatchInlineSnapshot(` - Object { + { "progress": 100, - "responseSteps": Array [ - Object { + "responseSteps": [ + { "message": "Fetched most recent commit from release branch", - "secondaryMessage": "with sha \\"latestCommit.sha\\"", + "secondaryMessage": "with sha "latestCommit.sha"", }, - Object { + { "message": "Created Tag Object", - "secondaryMessage": "with sha \\"mock_tag_object_sha\\"", + "secondaryMessage": "with sha "mock_tag_object_sha"", }, - Object { + { "message": "Create Tag Reference", - "secondaryMessage": "with ref \\"mock_createRef_ref\\"", + "secondaryMessage": "with ref "mock_createRef_ref"", }, - Object { + { "link": "https://mock_update_release_html_url", - "message": "Promoted \\"mock_update_release_name\\"", - "secondaryMessage": "from \\"rc-2020.01.01_1\\" to \\"mock_update_release_tag_name\\"", + "message": "Promoted "mock_update_release_name"", + "secondaryMessage": "from "rc-2020.01.01_1" to "mock_update_release_tag_name"", }, - Object { + { "icon": "success", "message": "Success callback successfully called 🚀", }, diff --git a/plugins/git-release-manager/src/features/RepoDetailsForm/Owner.test.tsx b/plugins/git-release-manager/src/features/RepoDetailsForm/Owner.test.tsx index e8798f3ce0..d73f6bf2aa 100644 --- a/plugins/git-release-manager/src/features/RepoDetailsForm/Owner.test.tsx +++ b/plugins/git-release-manager/src/features/RepoDetailsForm/Owner.test.tsx @@ -71,7 +71,7 @@ describe('Owner', () => { await waitFor(() => screen.getAllByTestId(TEST_IDS.form.owner.empty)); expect(getAllByTestId(TEST_IDS.form.owner.empty)).toMatchInlineSnapshot(` - Array [ + [

{ await waitFor(() => screen.getAllByTestId(TEST_IDS.form.repo.empty)); expect(getAllByTestId(TEST_IDS.form.repo.empty)).toMatchInlineSnapshot(` - Array [ + [

{ fireEvent.click(radio2); expect(mockNavigate.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ + [ + [ "?versioningStrategy=calver&owner=mock_owner&repo=mock_repo", - Object { + { "replace": true, }, ], diff --git a/plugins/git-release-manager/src/features/Stats/Info/helpers/getReleaseCommitPairs.test.tsx b/plugins/git-release-manager/src/features/Stats/Info/helpers/getReleaseCommitPairs.test.tsx index 5b800a4df5..4e946d965c 100644 --- a/plugins/git-release-manager/src/features/Stats/Info/helpers/getReleaseCommitPairs.test.tsx +++ b/plugins/git-release-manager/src/features/Stats/Info/helpers/getReleaseCommitPairs.test.tsx @@ -96,29 +96,29 @@ describe('getReleaseCommitPairs', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "releaseCommitPairs": Array [ - Object { + { + "releaseCommitPairs": [ + { "baseVersion": "2.0", - "endCommit": Object { + "endCommit": { "tagName": "version-2.0.0", "tagSha": "sha-2.0.0", "tagType": "tag", }, - "startCommit": Object { + "startCommit": { "tagName": "rc-2.0.0", "tagSha": "sha-2.0.0", "tagType": "tag", }, }, - Object { + { "baseVersion": "3.0", - "endCommit": Object { + "endCommit": { "tagName": "version-3.0.1", "tagSha": "sha-3.0.1", "tagType": "tag", }, - "startCommit": Object { + "startCommit": { "tagName": "rc-3.0.0", "tagSha": "sha-3.0.0", "tagType": "tag", diff --git a/plugins/git-release-manager/src/features/Stats/helpers/getMappedReleases.test.tsx b/plugins/git-release-manager/src/features/Stats/helpers/getMappedReleases.test.tsx index b0467b1260..1989cd9c09 100644 --- a/plugins/git-release-manager/src/features/Stats/helpers/getMappedReleases.test.tsx +++ b/plugins/git-release-manager/src/features/Stats/helpers/getMappedReleases.test.tsx @@ -33,27 +33,27 @@ describe('getMappedReleases', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "mappedReleases": Object { - "releases": Object { - "1.0": Object { + { + "mappedReleases": { + "releases": { + "1.0": { "baseVersion": "1.0", - "candidates": Array [], + "candidates": [], "createdAt": "2021-01-01T10:11:12Z", "htmlUrl": "html_url", - "versions": Array [], + "versions": [], }, - "1.1": Object { + "1.1": { "baseVersion": "1.1", - "candidates": Array [], + "candidates": [], "createdAt": "2021-01-01T10:11:12Z", "htmlUrl": "html_url", - "versions": Array [], + "versions": [], }, }, - "unmappableTags": Array [], - "unmatchedReleases": Array [], - "unmatchedTags": Array [], + "unmappableTags": [], + "unmatchedReleases": [], + "unmatchedTags": [], }, } `); diff --git a/plugins/git-release-manager/src/features/Stats/helpers/getReleaseStats.test.tsx b/plugins/git-release-manager/src/features/Stats/helpers/getReleaseStats.test.tsx index f46d0978b8..f446bca1c3 100644 --- a/plugins/git-release-manager/src/features/Stats/helpers/getReleaseStats.test.tsx +++ b/plugins/git-release-manager/src/features/Stats/helpers/getReleaseStats.test.tsx @@ -60,23 +60,23 @@ describe('getReleaseStats', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "releaseStats": Object { - "releases": Object { - "1.0": Object { + { + "releaseStats": { + "releases": { + "1.0": { "baseVersion": "1.0", - "candidates": Array [ - Object { + "candidates": [ + { "tagName": "rc-1.0.0", "tagSha": "sha", "tagType": "tag", }, - Object { + { "tagName": "rc-1.0.1", "tagSha": "sha", "tagType": "tag", }, - Object { + { "tagName": "rc-1.0.2", "tagSha": "sha", "tagType": "tag", @@ -84,18 +84,18 @@ describe('getReleaseStats', () => { ], "createdAt": "2021-01-01T10:11:12Z", "htmlUrl": "html_url", - "versions": Array [ - Object { + "versions": [ + { "tagName": "version-1.0.2", "tagSha": "sha", "tagType": "tag", }, ], }, - "1.1": Object { + "1.1": { "baseVersion": "1.1", - "candidates": Array [ - Object { + "candidates": [ + { "tagName": "rc-1.1.1", "tagSha": "sha", "tagType": "tag", @@ -103,14 +103,14 @@ describe('getReleaseStats', () => { ], "createdAt": "2021-01-01T10:11:12Z", "htmlUrl": "html_url", - "versions": Array [], + "versions": [], }, }, - "unmappableTags": Array [ + "unmappableTags": [ "rc-123.123.123", ], - "unmatchedReleases": Array [], - "unmatchedTags": Array [ + "unmatchedReleases": [], + "unmatchedTags": [ "rc-1/2/3", ], }, diff --git a/plugins/git-release-manager/src/features/Stats/helpers/getSummary.test.tsx b/plugins/git-release-manager/src/features/Stats/helpers/getSummary.test.tsx index d36eb85128..ee873800af 100644 --- a/plugins/git-release-manager/src/features/Stats/helpers/getSummary.test.tsx +++ b/plugins/git-release-manager/src/features/Stats/helpers/getSummary.test.tsx @@ -22,8 +22,8 @@ describe('getSummary', () => { const result = getSummary({ releaseStats: mockReleaseStats }); expect(result).toMatchInlineSnapshot(` - Object { - "summary": Object { + { + "summary": { "totalCandidatePatches": 3, "totalReleases": 2, "totalVersionPatches": 1, diff --git a/plugins/git-release-manager/src/features/Stats/helpers/getTagDates.test.ts b/plugins/git-release-manager/src/features/Stats/helpers/getTagDates.test.ts index ba17bc0be6..7900942c82 100644 --- a/plugins/git-release-manager/src/features/Stats/helpers/getTagDates.test.ts +++ b/plugins/git-release-manager/src/features/Stats/helpers/getTagDates.test.ts @@ -43,7 +43,7 @@ describe('getTagDates', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "endDate": undefined, "startDate": "TAG-START", } @@ -65,7 +65,7 @@ describe('getTagDates', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "endDate": undefined, "startDate": "COMMIT_START", } @@ -91,7 +91,7 @@ describe('getTagDates', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "endDate": "TAG-END", "startDate": "TAG-START", } @@ -117,7 +117,7 @@ describe('getTagDates', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "endDate": "COMMIT_END", "startDate": "COMMIT_START", } @@ -160,7 +160,7 @@ describe('getTagDates', () => { [{ owner, ref: 'OBJECT_SHA_START', repo }], ]); expect(result).toMatchInlineSnapshot(` - Object { + { "endDate": "COMMIT_START", "startDate": "COMMIT_END", } @@ -203,7 +203,7 @@ describe('getTagDates', () => { [{ owner, ref: 'OBJECT_SHA_END', repo }], ]); expect(result).toMatchInlineSnapshot(` - Object { + { "endDate": "COMMIT_END", "startDate": "COMMIT_START", } diff --git a/plugins/git-release-manager/src/helpers/createResponseStepError.test.ts b/plugins/git-release-manager/src/helpers/createResponseStepError.test.ts index b010a0e21f..fd21b7ea52 100644 --- a/plugins/git-release-manager/src/helpers/createResponseStepError.test.ts +++ b/plugins/git-release-manager/src/helpers/createResponseStepError.test.ts @@ -21,7 +21,7 @@ describe('createResponseStepError', () => { const result = createResponseStepError(new Error('banana')); expect(result).toMatchInlineSnapshot(` - Object { + { "icon": "failure", "message": "Something went wrong ❌", "secondaryMessage": "Error message: banana", diff --git a/plugins/git-release-manager/src/helpers/getBumpedTag.test.ts b/plugins/git-release-manager/src/helpers/getBumpedTag.test.ts index 32a296bbd2..84c2b5de04 100644 --- a/plugins/git-release-manager/src/helpers/getBumpedTag.test.ts +++ b/plugins/git-release-manager/src/helpers/getBumpedTag.test.ts @@ -30,10 +30,10 @@ describe('getBumpedTag', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "bumpedTag": "rc-2020.01.01_2", "error": undefined, - "tagParts": Object { + "tagParts": { "calver": "2020.01.01", "patch": 2, "prefix": "rc", @@ -50,10 +50,10 @@ describe('getBumpedTag', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "bumpedTag": "rc-2020.01.01_2", "error": undefined, - "tagParts": Object { + "tagParts": { "calver": "2020.01.01", "patch": 2, "prefix": "rc", @@ -72,10 +72,10 @@ describe('getBumpedTag', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "bumpedTag": "rc-1.2.4", "error": undefined, - "tagParts": Object { + "tagParts": { "major": 1, "minor": 2, "patch": 4, @@ -93,10 +93,10 @@ describe('getBumpedTag', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "bumpedTag": "rc-1.3.0", "error": undefined, - "tagParts": Object { + "tagParts": { "major": 1, "minor": 3, "patch": 0, @@ -114,10 +114,10 @@ describe('getBumpedTag', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "bumpedTag": "rc-2.0.0", "error": undefined, - "tagParts": Object { + "tagParts": { "major": 2, "minor": 0, "patch": 0, @@ -137,9 +137,9 @@ describe('getBumpedTag', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"😬\\"", + { + "error": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "😬"", "title": "Invalid tag", }, } diff --git a/plugins/git-release-manager/src/helpers/getReleaseCandidateGitInfo.test.ts b/plugins/git-release-manager/src/helpers/getReleaseCandidateGitInfo.test.ts index 20130c450c..693e9971db 100644 --- a/plugins/git-release-manager/src/helpers/getReleaseCandidateGitInfo.test.ts +++ b/plugins/git-release-manager/src/helpers/getReleaseCandidateGitInfo.test.ts @@ -43,7 +43,7 @@ describe('getReleaseCandidateGitInfo', () => { injectedDate: '2021.01.28', }), ).toMatchInlineSnapshot(` - Object { + { "rcBranch": "rc/2021.01.28", "rcReleaseTag": "rc-2021.01.28_0", "releaseName": "Version 2021.01.28", @@ -61,7 +61,7 @@ describe('getReleaseCandidateGitInfo', () => { semverBumpLevel: 'minor', }), ).toMatchInlineSnapshot(` - Object { + { "rcBranch": "rc/1.3.0", "rcReleaseTag": "rc-1.3.0", "releaseName": "Version 1.3.0", @@ -77,7 +77,7 @@ describe('getReleaseCandidateGitInfo', () => { semverBumpLevel: 'minor', }), ).toMatchInlineSnapshot(` - Object { + { "rcBranch": "rc/0.0.1", "rcReleaseTag": "rc-0.0.1", "releaseName": "Version 0.0.1", diff --git a/plugins/git-release-manager/src/helpers/tagParts/getCalverTagParts.test.ts b/plugins/git-release-manager/src/helpers/tagParts/getCalverTagParts.test.ts index 1d46c0abdd..7a12fab45b 100644 --- a/plugins/git-release-manager/src/helpers/tagParts/getCalverTagParts.test.ts +++ b/plugins/git-release-manager/src/helpers/tagParts/getCalverTagParts.test.ts @@ -26,28 +26,28 @@ describe('getCalverTagParts', () => { const result = getCalverTagParts(mockReleaseCandidateCalver.tagName); expect(result).toMatchInlineSnapshot(` - Object { - "tagParts": Object { - "calver": "2020.01.01", - "patch": 1, - "prefix": "rc", - }, - } - `); + { + "tagParts": { + "calver": "2020.01.01", + "patch": 1, + "prefix": "rc", + }, + } + `); }); it('should return tagParts for Version tag', () => { const result = getCalverTagParts(mockReleaseVersionCalver.tagName); expect(result).toMatchInlineSnapshot(` - Object { - "tagParts": Object { - "calver": "2020.01.01", - "patch": 1, - "prefix": "version", - }, - } - `); + { + "tagParts": { + "calver": "2020.01.01", + "patch": 1, + "prefix": "version", + }, + } + `); }); }); @@ -56,9 +56,9 @@ describe('getCalverTagParts', () => { const result = getCalverTagParts('invalid-2020.01.01_1'); expect(result).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"invalid-2020.01.01_1\\"", + { + "error": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "invalid-2020.01.01_1"", "title": "Invalid tag", }, } @@ -69,9 +69,9 @@ describe('getCalverTagParts', () => { const result = getCalverTagParts('rc-2020.1.01_1'); expect(result).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"rc-2020.1.01_1\\"", + { + "error": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "rc-2020.1.01_1"", "title": "Invalid tag", }, } @@ -82,9 +82,9 @@ describe('getCalverTagParts', () => { const result = getCalverTagParts('rc-2020.01_1'); expect(result).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"rc-2020.01_1\\"", + { + "error": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "rc-2020.01_1"", "title": "Invalid tag", }, } @@ -95,9 +95,9 @@ describe('getCalverTagParts', () => { const result = getCalverTagParts('rc-2020.01.01_a'); expect(result).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"rc-2020.01.01_a\\"", + { + "error": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "rc-2020.01.01_a"", "title": "Invalid tag", }, } diff --git a/plugins/git-release-manager/src/helpers/tagParts/getSemverTagParts.test.ts b/plugins/git-release-manager/src/helpers/tagParts/getSemverTagParts.test.ts index c637325843..7edb013dfd 100644 --- a/plugins/git-release-manager/src/helpers/tagParts/getSemverTagParts.test.ts +++ b/plugins/git-release-manager/src/helpers/tagParts/getSemverTagParts.test.ts @@ -28,15 +28,15 @@ describe('getSemverTagParts', () => { ); expect(semverTagParts).toMatchInlineSnapshot(` - Object { - "tagParts": Object { - "major": 1, - "minor": 2, - "patch": 3, - "prefix": "rc", - }, - } - `); + { + "tagParts": { + "major": 1, + "minor": 2, + "patch": 3, + "prefix": "rc", + }, + } + `); }); it('should return tagParts for Version tag', () => { @@ -45,15 +45,15 @@ describe('getSemverTagParts', () => { ); expect(semverTagParts).toMatchInlineSnapshot(` - Object { - "tagParts": Object { - "major": 1, - "minor": 2, - "patch": 3, - "prefix": "version", - }, - } - `); + { + "tagParts": { + "major": 1, + "minor": 2, + "patch": 3, + "prefix": "version", + }, + } + `); }); }); @@ -62,9 +62,9 @@ describe('getSemverTagParts', () => { const semverTagParts = getSemverTagParts('invalid-1.2.3'); expect(semverTagParts).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected semver matching \\"/(rc|version)-([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)/\\", found \\"invalid-1.2.3\\"", + { + "error": { + "subtitle": "Expected semver matching "/(rc|version)-([0-9]+)\\.([0-9]+)\\.([0-9]+)/", found "invalid-1.2.3"", "title": "Invalid tag", }, } @@ -75,9 +75,9 @@ describe('getSemverTagParts', () => { const semverTagParts = getSemverTagParts('rc-1.2'); expect(semverTagParts).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected semver matching \\"/(rc|version)-([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)/\\", found \\"rc-1.2\\"", + { + "error": { + "subtitle": "Expected semver matching "/(rc|version)-([0-9]+)\\.([0-9]+)\\.([0-9]+)/", found "rc-1.2"", "title": "Invalid tag", }, } @@ -88,9 +88,9 @@ describe('getSemverTagParts', () => { const semverTagParts = getSemverTagParts('rc-1337.01.01_1'); expect(semverTagParts).toMatchInlineSnapshot(` - Object { - "error": Object { - "subtitle": "Expected semver matching \\"/(rc|version)-([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)/\\", found calver \\"rc-1337.01.01_1\\"", + { + "error": { + "subtitle": "Expected semver matching "/(rc|version)-([0-9]+)\\.([0-9]+)\\.([0-9]+)/", found calver "rc-1337.01.01_1"", "title": "Invalid tag", }, } diff --git a/plugins/git-release-manager/src/helpers/tagParts/validateTagParts.test.ts b/plugins/git-release-manager/src/helpers/tagParts/validateTagParts.test.ts index 50cbd6cf9e..64f60ec4d6 100644 --- a/plugins/git-release-manager/src/helpers/tagParts/validateTagParts.test.ts +++ b/plugins/git-release-manager/src/helpers/tagParts/validateTagParts.test.ts @@ -31,7 +31,7 @@ describe('validateTagName', () => { }); expect(result).toMatchInlineSnapshot(` - Object { + { "tagNameError": undefined, } `); @@ -43,10 +43,10 @@ describe('validateTagName', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "tagNameError": null, - } - `); + { + "tagNameError": null, + } + `); }); }); @@ -58,9 +58,9 @@ describe('validateTagName', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "tagNameError": Object { - "subtitle": "Expected semver matching \\"/(rc|version)-([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)/\\", found calver \\"rc-2020.01.01_1\\"", + { + "tagNameError": { + "subtitle": "Expected semver matching "/(rc|version)-([0-9]+)\\.([0-9]+)\\.([0-9]+)/", found calver "rc-2020.01.01_1"", "title": "Invalid tag", }, } @@ -74,9 +74,9 @@ describe('validateTagName', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "tagNameError": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"rc-1.2.3\\"", + { + "tagNameError": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "rc-1.2.3"", "title": "Invalid tag", }, } @@ -92,9 +92,9 @@ describe('validateTagName', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "tagNameError": Object { - "subtitle": "Expected semver matching \\"/(rc|version)-([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+)/\\", found \\"this-is-so-invalid\\"", + { + "tagNameError": { + "subtitle": "Expected semver matching "/(rc|version)-([0-9]+)\\.([0-9]+)\\.([0-9]+)/", found "this-is-so-invalid"", "title": "Invalid tag", }, } @@ -108,9 +108,9 @@ describe('validateTagName', () => { }); expect(result).toMatchInlineSnapshot(` - Object { - "tagNameError": Object { - "subtitle": "Expected calver matching \\"/(rc|version)-([0-9]{4}\\\\.[0-9]{2}\\\\.[0-9]{2})_([0-9]+)/\\", found \\"this-is-so-invalid\\"", + { + "tagNameError": { + "subtitle": "Expected calver matching "/(rc|version)-([0-9]{4}\\.[0-9]{2}\\.[0-9]{2})_([0-9]+)/", found "this-is-so-invalid"", "title": "Invalid tag", }, } diff --git a/plugins/git-release-manager/src/hooks/useGetGitBatchInfo.test.ts b/plugins/git-release-manager/src/hooks/useGetGitBatchInfo.test.ts index fb1a8b6919..8e89add810 100644 --- a/plugins/git-release-manager/src/hooks/useGetGitBatchInfo.test.ts +++ b/plugins/git-release-manager/src/hooks/useGetGitBatchInfo.test.ts @@ -35,31 +35,31 @@ describe('useGetHubBatchInfo', () => { }); expect(result.current.gitBatchInfo).toMatchInlineSnapshot(` - Object { + { "loading": false, - "value": Object { - "latestRelease": Object { + "value": { + "latestRelease": { "htmlUrl": "https://mock_release_html_url", "id": 1, "prerelease": false, "tagName": "rc-2020.01.01_1", "targetCommitish": "rc/2020.01.01_1", }, - "releaseBranch": Object { - "commit": Object { - "commit": Object { - "tree": Object { + "releaseBranch": { + "commit": { + "commit": { + "tree": { "sha": "mock_branch_commit_commit_tree_sha", }, }, "sha": "mock_branch_commit_sha", }, - "links": Object { + "links": { "html": "https://mock_branch_links_html", }, "name": "rc/1.2.3", }, - "repository": Object { + "repository": { "defaultBranch": "mock_defaultBranch", "name": "mock_repo", "pushPermissions": true, @@ -86,12 +86,12 @@ describe('useGetHubBatchInfo', () => { }); expect(result.current.gitBatchInfo).toMatchInlineSnapshot(` - Object { + { "loading": false, - "value": Object { + "value": { "latestRelease": null, "releaseBranch": null, - "repository": Object { + "repository": { "defaultBranch": "mock_defaultBranch", "name": "mock_repo", "pushPermissions": true, diff --git a/plugins/git-release-manager/src/hooks/useQueryHandler.test.tsx b/plugins/git-release-manager/src/hooks/useQueryHandler.test.tsx index 7d06c5d829..020b068188 100644 --- a/plugins/git-release-manager/src/hooks/useQueryHandler.test.tsx +++ b/plugins/git-release-manager/src/hooks/useQueryHandler.test.tsx @@ -50,14 +50,14 @@ describe('useQueryHandler', () => { const result = getByTestId(TEST_ID).innerHTML; expect(result).toMatchInlineSnapshot(` - "{ - \\"parsedQuery\\": { - \\"versioningStrategy\\": \\"semver\\", - \\"owner\\": \\"mock_owner\\", - \\"repo\\": \\"mock_repo\\" - }, - \\"queryParams\\": \\"versioningStrategy=semver&owner=mock_owner&repo=updated_mock_repo\\" - }" - `); + "{ + "parsedQuery": { + "versioningStrategy": "semver", + "owner": "mock_owner", + "repo": "mock_repo" + }, + "queryParams": "versioningStrategy=semver&owner=mock_owner&repo=updated_mock_repo" + }" + `); }); }); diff --git a/plugins/git-release-manager/src/hooks/useResponseSteps.test.ts b/plugins/git-release-manager/src/hooks/useResponseSteps.test.ts index 5daa8b47ad..450fc87b97 100644 --- a/plugins/git-release-manager/src/hooks/useResponseSteps.test.ts +++ b/plugins/git-release-manager/src/hooks/useResponseSteps.test.ts @@ -23,11 +23,11 @@ describe('useResponseSteps', () => { const { result } = renderHook(() => useResponseSteps()); expect(result.current).toMatchInlineSnapshot(` - Object { + { "abortIfError": [Function], "addStepToResponseSteps": [Function], "asyncCatcher": [Function], - "responseSteps": Array [], + "responseSteps": [], } `); }); @@ -36,7 +36,7 @@ describe('useResponseSteps', () => { it('should add responseSteps to state', async () => { const { result } = renderHook(() => useResponseSteps()); - expect(result.current.responseSteps).toMatchInlineSnapshot(`Array []`); + expect(result.current.responseSteps).toMatchInlineSnapshot(`[]`); act(() => { result.current.addStepToResponseSteps({ @@ -45,12 +45,12 @@ describe('useResponseSteps', () => { }); expect(result.current.responseSteps).toMatchInlineSnapshot(` - Array [ - Object { - "message": "totally added a messaage ✌🏼", - }, - ] - `); + [ + { + "message": "totally added a messaage ✌🏼", + }, + ] + `); }); }); @@ -58,7 +58,7 @@ describe('useResponseSteps', () => { it('should catch Errors and add as failure step, then throw', async () => { const { result } = renderHook(() => useResponseSteps()); - expect(result.current.responseSteps).toMatchInlineSnapshot(`Array []`); + expect(result.current.responseSteps).toMatchInlineSnapshot(`[]`); await act(async () => { await new Promise((_, reject) => reject(new Error(':('))) @@ -69,8 +69,8 @@ describe('useResponseSteps', () => { }); expect(result.current.responseSteps).toMatchInlineSnapshot(` - Array [ - Object { + [ + { "icon": "failure", "message": Something went wrong @@ -91,7 +91,7 @@ describe('useResponseSteps', () => { it('should catch unknown Errors and add as failure step, then throw', async () => { const { result } = renderHook(() => useResponseSteps()); - expect(result.current.responseSteps).toMatchInlineSnapshot(`Array []`); + expect(result.current.responseSteps).toMatchInlineSnapshot(`[]`); await act(async () => { await new Promise((_, reject) => reject()) @@ -102,8 +102,8 @@ describe('useResponseSteps', () => { }); expect(result.current.responseSteps).toMatchInlineSnapshot(` - Array [ - Object { + [ + { "icon": "failure", "message": Something went wrong @@ -126,13 +126,13 @@ describe('useResponseSteps', () => { it('should do nothing if not Error', async () => { const { result } = renderHook(() => useResponseSteps()); - expect(result.current.responseSteps).toMatchInlineSnapshot(`Array []`); + expect(result.current.responseSteps).toMatchInlineSnapshot(`[]`); act(() => { result.current.abortIfError(undefined); }); - expect(result.current.responseSteps).toMatchInlineSnapshot(`Array []`); + expect(result.current.responseSteps).toMatchInlineSnapshot(`[]`); }); }); }); diff --git a/plugins/git-release-manager/src/plugin.test.ts b/plugins/git-release-manager/src/plugin.test.ts index db60617c24..b293ea05eb 100644 --- a/plugins/git-release-manager/src/plugin.test.ts +++ b/plugins/git-release-manager/src/plugin.test.ts @@ -19,7 +19,7 @@ import * as plugin from './plugin'; describe('git-release-manager', () => { it('should export plugin & friends', () => { expect(Object.keys(plugin)).toMatchInlineSnapshot(` - Array [ + [ "gitReleaseManagerApiRef", "constants", "helpers", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index b261cef2ba..4a0ca4dce0 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-github-actions +## 0.5.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 1bdb0bf24b: Modify RecentWorkflowRunsCard use constructed route instead of hardcoded route. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + ## 0.5.9-next.3 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 7866ded70e..596e844c11 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.5.9-next.3", + "version": "0.5.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,12 +34,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -53,10 +53,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index 655d640357..244a610d0c 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-github-deployments +## 0.1.40 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + ## 0.1.40-next.3 ### Patch Changes diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 8e32c3c0ff..a0f94015dd 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-deployments", "description": "A Backstage plugin that integrates towards GitHub Deployments", - "version": "0.1.40-next.3", + "version": "0.1.40", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,14 +23,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -42,10 +42,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/github-issues/CHANGELOG.md b/plugins/github-issues/CHANGELOG.md index 758146689a..8519b5cc08 100644 --- a/plugins/github-issues/CHANGELOG.md +++ b/plugins/github-issues/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-github-issues +## 0.1.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.1.1-next.2 ### Patch Changes diff --git a/plugins/github-issues/package.json b/plugins/github-issues/package.json index b98197505c..c393e4db8a 100644 --- a/plugins/github-issues/package.json +++ b/plugins/github-issues/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-issues", - "version": "0.1.1-next.2", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,13 +22,13 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.15", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.61", @@ -40,10 +40,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@spotify/prettier-config": "^14.0.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", diff --git a/plugins/github-pull-requests-board/CHANGELOG.md b/plugins/github-pull-requests-board/CHANGELOG.md index 37d3fdc257..061fca1187 100644 --- a/plugins/github-pull-requests-board/CHANGELOG.md +++ b/plugins/github-pull-requests-board/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-github-pull-requests-board +## 0.1.3 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 2665ee4ed4: Clarified GitHub app permissions required by plugin +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 723113296b: Add optional `pullRequestLimit` prop to `EntityTeamPullRequestsCard` and `EntityTeamPullRequestsContent` to limit the number of PRs shown per repository. Excluding this prop will default the number of pull requests shown to 10 per repository (the existing functionality). +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + ## 0.1.3-next.3 ### Patch Changes diff --git a/plugins/github-pull-requests-board/package.json b/plugins/github-pull-requests-board/package.json index d667acb5a3..62826918a9 100644 --- a/plugins/github-pull-requests-board/package.json +++ b/plugins/github-pull-requests-board/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-pull-requests-board", "description": "A Backstage plugin that allows you to see all open Pull Requests for all the repositories owned by your team", - "version": "0.1.3-next.3", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,12 +34,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -48,9 +48,9 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index 93975d7184..0845dcf919 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-gitops-profiles +## 0.3.27 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 0.3.27-next.3 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 6e54ed98e9..9c2114a91f 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gitops-profiles", "description": "A Backstage plugin that helps you manage GitOps profiles", - "version": "0.3.27-next.3", + "version": "0.3.27", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,9 +33,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -46,10 +46,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/gocd/CHANGELOG.md b/plugins/gocd/CHANGELOG.md index 3673cc4163..aa676a8c77 100644 --- a/plugins/gocd/CHANGELOG.md +++ b/plugins/gocd/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-gocd +## 0.1.15 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.1.15-next.2 ### Patch Changes diff --git a/plugins/gocd/package.json b/plugins/gocd/package.json index 0ab50a90e5..6ed5489b61 100644 --- a/plugins/gocd/package.json +++ b/plugins/gocd/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gocd", "description": "A Backstage plugin that integrates towards GoCD", - "version": "0.1.15-next.2", + "version": "0.1.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -47,10 +47,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index a9f2f743ec..fbd18a82db 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-graphiql +## 0.2.41 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 0.2.41-next.3 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index d6a16708b9..2ad33b0f9a 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.41-next.3", + "version": "0.2.41", "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -32,9 +32,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -48,10 +48,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/graphql-backend/CHANGELOG.md b/plugins/graphql-backend/CHANGELOG.md index c94a7619b5..d25668d794 100644 --- a/plugins/graphql-backend/CHANGELOG.md +++ b/plugins/graphql-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-graphql-backend +## 0.1.26 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + - @backstage/plugin-catalog-graphql@0.3.13 + ## 0.1.26-next.3 ### Patch Changes diff --git a/plugins/graphql-backend/package.json b/plugins/graphql-backend/package.json index 96fe0c1fe8..8575323fe6 100644 --- a/plugins/graphql-backend/package.json +++ b/plugins/graphql-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-backend", "description": "An experimental Backstage backend plugin for GraphQL", - "version": "0.1.26-next.3", + "version": "0.1.26", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,9 +33,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/plugin-catalog-graphql": "^0.3.13-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-catalog-graphql": "workspace:^", "@graphql-tools/schema": "^9.0.0", "@types/express": "^4.17.6", "apollo-server": "^3.0.0", @@ -50,7 +50,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^0.47.0", "supertest": "^6.1.3" diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 55da92f23f..bbeec680e2 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-home +## 0.4.25 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/plugin-stack-overflow@0.1.5 + ## 0.4.25-next.3 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index df285bad43..bfa3d388c3 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.4.25-next.3", + "version": "0.4.25", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,13 +33,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-stack-overflow": "^0.1.5-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-stack-overflow": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -52,10 +52,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index bfb3ada100..9c228fdf99 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-ilert +## 0.1.35 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.1.35-next.3 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index 7aa79fb143..391a9e495d 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.1.35-next.3", + "version": "0.1.35", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,12 +23,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,10 +42,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index 9aa3e20d79..693c0d2988 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-jenkins-backend +## 0.1.26 + +### Patch Changes + +- 618c06f838: Add example on how to pass through permissions +- a966ed8385: Unwrap error message when getting projects +- 47952621dc: Extend configuration so that extra headers can be sent to jenkins instance +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- 69ec1cf39f: Updated dependency `jenkins` to `^1.0.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- e55d855a6b: Jenkins plugin works again +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-jenkins-common@0.1.8 + ## 0.1.26-next.3 ### Patch Changes diff --git a/plugins/jenkins-backend/README.md b/plugins/jenkins-backend/README.md index 6d767c502e..133b9f42e2 100644 --- a/plugins/jenkins-backend/README.md +++ b/plugins/jenkins-backend/README.md @@ -43,6 +43,7 @@ export default async function createPlugin( config: env.config, catalog, }), + permissions: env.permissions, }); } ``` diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index 55f856003e..7c2203a866 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.1.26-next.3", + "version": "0.1.26", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,14 +24,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/plugin-jenkins-common": "^0.1.8-next.0", - "@backstage/plugin-permission-common": "^0.6.4-next.2", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-jenkins-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -41,7 +41,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/jenkins": "^0.23.1", "@types/supertest": "^2.0.8", "msw": "^0.47.0", diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts index 8e14997fd7..626e5b0a5a 100644 --- a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.test.ts @@ -55,7 +55,7 @@ describe('JenkinsConfig', () => { baseUrl: 'https://jenkins.example.com', username: 'backstage - bot', apiKey: '123456789abcdef0123456789abcedf012', - headers: { + extraRequestHeaders: { myHeader: 'my-value', }, }, @@ -70,7 +70,7 @@ describe('JenkinsConfig', () => { baseUrl: 'https://jenkins.example.com', username: 'backstage - bot', apiKey: '123456789abcdef0123456789abcedf012', - headers: { + extraRequestHeaders: { myHeader: 'my-value', }, }, diff --git a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts index 2ad5b60f6d..c4b4ef58dd 100644 --- a/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts +++ b/plugins/jenkins-backend/src/service/jenkinsInfoProvider.ts @@ -84,7 +84,7 @@ export class JenkinsConfig { baseUrl: c.getString('baseUrl'), username: c.getString('username'), apiKey: c.getString('apiKey'), - headers: c.getOptional('headers'), + extraRequestHeaders: c.getOptional('extraRequestHeaders'), crumbIssuer: c.getOptionalBoolean('crumbIssuer'), })) || []; diff --git a/plugins/jenkins-common/CHANGELOG.md b/plugins/jenkins-common/CHANGELOG.md index 1eedf680d5..a1d29726fd 100644 --- a/plugins/jenkins-common/CHANGELOG.md +++ b/plugins/jenkins-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-jenkins-common +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-catalog-common@1.0.6 + ## 0.1.8-next.0 ### Patch Changes diff --git a/plugins/jenkins-common/package.json b/plugins/jenkins-common/package.json index 2e2d670ab9..fd752b9d84 100644 --- a/plugins/jenkins-common/package.json +++ b/plugins/jenkins-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins-common", - "version": "0.1.8-next.0", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-permission-common": "^0.6.4-next.0" + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.1" + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index c6cc5ed432..ff4612ba8f 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-jenkins +## 0.7.8 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/plugin-jenkins-common@0.1.8 + ## 0.7.8-next.3 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index f415770809..18b4507b48 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.7.8-next.3", + "version": "0.7.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,13 +33,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-jenkins-common": "^0.1.8-next.0", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-jenkins-common": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -52,10 +52,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 6bd87b05b7..8294bcd1aa 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kafka-backend +## 0.2.29 + +### Patch Changes + +- f6be17460d: Minor API signatures cleanup +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.2.29-next.1 ### Patch Changes diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 0d65283a53..5c56582454 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.2.29-next.1", + "version": "0.2.29", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,10 +34,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -46,7 +46,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/jest-when": "^3.5.0", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index 3a9e86b62e..ca3383b39e 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-kafka +## 0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f6be17460d: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + ## 0.3.9-next.3 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 291f1dcea6..228b2d6bc9 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka", "description": "A Backstage plugin that integrates towards Kafka", - "version": "0.3.9-next.3", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,12 +24,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -40,10 +40,10 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 626003f241..135bb3cd6e 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-kubernetes-backend +## 0.7.2 + +### Patch Changes + +- 8902c2e39d: chore: Exporting KubernetesClientProvider and everything in kubernetes-auth-translator as requested in issue #10457 +- a57d29d572: Adds skipMetricsLookup to the kubernetes-backend schema +- 0768d6dece: add new kubernetes backend endpoints to kubernetes backend client +- 60b85d8ade: Updated dependency `helmet` to `^6.0.0`. + + Please note that these policies are no longer applied by default: + + helmet.contentSecurityPolicy no longer sets block-all-mixed-content directive by default + helmet.expectCt is no longer set by default. It can, however, be explicitly enabled. It will be removed in Helmet 7. + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-kubernetes-common@0.4.2 + ## 0.7.2-next.3 ### Patch Changes diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index f97a0fd032..4219387819 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -8,6 +8,7 @@ import { Config } from '@backstage/config'; import { CoreV1Api } from '@kubernetes/client-node'; import { Credentials } from 'aws-sdk'; import { CustomObjectsApi } from '@kubernetes/client-node'; +import type { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common'; import { Duration } from 'luxon'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; @@ -101,9 +102,6 @@ export interface CustomResource extends ObjectToFetch { objectType: 'customresources'; } -// @alpha (undocumented) -export type CustomResourceMatcher = Omit; - // @alpha (undocumented) export interface CustomResourcesByEntity extends KubernetesObjectsByEntity { // (undocumented) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index f40492e1b7..9adb3d4c22 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-backend", "description": "A Backstage backend plugin that integrates towards Kubernetes", - "version": "0.7.2-next.3", + "version": "0.7.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,13 +35,13 @@ }, "dependencies": { "@azure/identity": "^2.0.4", - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/plugin-kubernetes-common": "^0.4.2-next.1", + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-kubernetes-common": "workspace:^", "@google-cloud/container": "^4.0.0", "@kubernetes/client-node": "^0.17.0", "@types/express": "^4.17.6", @@ -62,7 +62,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/aws4": "^1.5.1", "aws-sdk-mock": "^5.2.1", "supertest": "^6.1.3" diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts index a28f825434..704551eb9f 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts @@ -46,6 +46,10 @@ describe('AzureIdentityKubernetesAuthTranslator tests', () => { url: 'mycluster.privatelink.westeurope.azmk8s.io', }; + afterEach(() => { + jest.useRealTimers(); + }); + it('should decorate cluster with Azure token', async () => { const authTranslator = new AzureIdentityKubernetesAuthTranslator( logger, @@ -70,6 +74,8 @@ describe('AzureIdentityKubernetesAuthTranslator tests', () => { }); it('should issue new token 15 minutes befory expiry', async () => { + jest.useFakeTimers(); + const authTranslator = new AzureIdentityKubernetesAuthTranslator( logger, new StaticTokenCredential(16 * 60 * 1000), // token expires in 16min @@ -78,13 +84,15 @@ describe('AzureIdentityKubernetesAuthTranslator tests', () => { const response = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); - jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2mins + jest.setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2mins const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response2.serviceAccountToken).toEqual('MY_TOKEN_2'); }); it('should re-use existing token if there is afailure', async () => { + jest.useFakeTimers(); + const authTranslator = new AzureIdentityKubernetesAuthTranslator( logger, new StaticTokenCredential(16 * 60 * 1000), // new tokens expires in 16min @@ -93,12 +101,12 @@ describe('AzureIdentityKubernetesAuthTranslator tests', () => { const response = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); - jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min + jest.setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response2.serviceAccountToken).toEqual('MY_TOKEN_2'); - jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min + jest.setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min const response3 = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response3.serviceAccountToken).toEqual('MY_TOKEN_2'); @@ -108,6 +116,8 @@ describe('AzureIdentityKubernetesAuthTranslator tests', () => { }); it('should throw if existing token expired and failed to fetch a new one', async () => { + jest.useFakeTimers(); + const authTranslator = new AzureIdentityKubernetesAuthTranslator( logger, new StaticTokenCredential(16 * 60 * 1000), // new tokens expires in 16min @@ -116,12 +126,12 @@ describe('AzureIdentityKubernetesAuthTranslator tests', () => { const response = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); - jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min + jest.setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); expect(response2.serviceAccountToken).toEqual('MY_TOKEN_2'); - jest.useFakeTimers().setSystemTime(Date.now() + 17 * 60 * 1000); // advance time by 17min + jest.setSystemTime(Date.now() + 17 * 60 * 1000); // advance time by 17min await expect( authTranslator.decorateClusterDetailsWithAuth(cd), diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index 7fcc9836a3..23163d1004 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -25,7 +25,6 @@ import { FetchResponseWrapper, ObjectToFetch, CustomResource, - CustomResourceMatcher, CustomResourcesByEntity, KubernetesObjectsByEntity, } from '../types/types'; @@ -40,6 +39,7 @@ import { ObjectsByEntityResponse, PodFetchResponse, KubernetesRequestAuth, + CustomResourceMatcher, } from '@backstage/plugin-kubernetes-common'; import { ContainerStatus, diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 33fde70b24..5ddfe7367d 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -18,6 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { Logger } from 'winston'; import type { JsonObject } from '@backstage/types'; import type { + CustomResourceMatcher, FetchResponse, KubernetesFetchError, KubernetesRequestAuth, @@ -86,12 +87,6 @@ export interface CustomResource extends ObjectToFetch { objectType: 'customresources'; } -/** - * - * @alpha - */ -export type CustomResourceMatcher = Omit; - /** * * @alpha diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 08ba0764dc..dd5e74b384 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes-common +## 0.4.2 + +### Patch Changes + +- 0768d6dece: add new kubernetes backend endpoints to kubernetes backend client +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/catalog-model@1.1.1 + ## 0.4.2-next.1 ### Patch Changes diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index ef36d5ce28..85015662c6 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -89,6 +89,16 @@ export interface CronJobsFetchResponse { type: 'cronjobs'; } +// @public (undocumented) +export interface CustomObjectsByEntityRequest { + // (undocumented) + auth: KubernetesRequestAuth; + // (undocumented) + customResources: CustomResourceMatcher[]; + // (undocumented) + entity: Entity; +} + // @public (undocumented) export interface CustomResourceFetchResponse { // (undocumented) @@ -97,6 +107,16 @@ export interface CustomResourceFetchResponse { type: 'customresources'; } +// @public (undocumented) +export interface CustomResourceMatcher { + // (undocumented) + apiVersion: string; + // (undocumented) + group: string; + // (undocumented) + plural: string; +} + // @public (undocumented) export interface DaemonSetsFetchResponse { // (undocumented) @@ -233,4 +253,12 @@ export interface StatefulSetsFetchResponse { // (undocumented) type: 'statefulsets'; } + +// @public (undocumented) +export interface WorkloadsByEntityRequest { + // (undocumented) + auth: KubernetesRequestAuth; + // (undocumented) + entity: Entity; +} ``` diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 3095ac68d4..5baa12800d 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-common", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", - "version": "0.4.2-next.1", + "version": "0.4.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -38,11 +38,11 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", + "@backstage/catalog-model": "workspace:^", "@kubernetes/client-node": "^0.17.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" }, "jest": { "roots": [ diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index 830c32c32c..149caadb15 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -39,6 +39,26 @@ export interface KubernetesRequestAuth { }; } +/** @public */ +export interface CustomResourceMatcher { + group: string; + apiVersion: string; + plural: string; +} + +/** @public */ +export interface WorkloadsByEntityRequest { + auth: KubernetesRequestAuth; + entity: Entity; +} + +/** @public */ +export interface CustomObjectsByEntityRequest { + auth: KubernetesRequestAuth; + customResources: CustomResourceMatcher[]; + entity: Entity; +} + /** @public */ export interface KubernetesRequestBody { auth?: KubernetesRequestAuth; diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 264e550c5c..b5909b8c01 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-kubernetes +## 0.7.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 0768d6dece: add new kubernetes backend endpoints to kubernetes backend client +- 19a27929fb: Reset error state on success +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/plugin-kubernetes-common@0.4.2 + ## 0.7.2-next.3 ### Patch Changes diff --git a/plugins/kubernetes/api-report.md b/plugins/kubernetes/api-report.md index 81ce386200..3e1b2f0198 100644 --- a/plugins/kubernetes/api-report.md +++ b/plugins/kubernetes/api-report.md @@ -10,6 +10,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ClientPodStatus } from '@backstage/plugin-kubernetes-common'; import { ClusterAttributes } from '@backstage/plugin-kubernetes-common'; import { ClusterObjects } from '@backstage/plugin-kubernetes-common'; +import { CustomObjectsByEntityRequest } from '@backstage/plugin-kubernetes-common'; +import { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { IdentityApi } from '@backstage/core-plugin-api'; @@ -31,6 +33,7 @@ import { V1Pod } from '@kubernetes/client-node'; import { V1ReplicaSet } from '@kubernetes/client-node'; import { V1Service } from '@kubernetes/client-node'; import { V1StatefulSet } from '@kubernetes/client-node'; +import { WorkloadsByEntityRequest } from '@backstage/plugin-kubernetes-common'; // Warning: (ae-forgotten-export) The symbol "ClusterProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "Cluster" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -245,9 +248,17 @@ export interface KubernetesApi { }[] >; // (undocumented) + getCustomObjectsByEntity( + request: CustomObjectsByEntityRequest, + ): Promise; + // (undocumented) getObjectsByEntity( requestBody: KubernetesRequestBody, ): Promise; + // (undocumented) + getWorkloadsByEntity( + request: WorkloadsByEntityRequest, + ): Promise; } // Warning: (ae-missing-release-tag) "kubernetesApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -304,9 +315,17 @@ export class KubernetesBackendClient implements KubernetesApi { }[] >; // (undocumented) + getCustomObjectsByEntity( + request: CustomObjectsByEntityRequest, + ): Promise; + // (undocumented) getObjectsByEntity( requestBody: KubernetesRequestBody, ): Promise; + // (undocumented) + getWorkloadsByEntity( + request: WorkloadsByEntityRequest, + ): Promise; } // Warning: (ae-forgotten-export) The symbol "KubernetesContentProps" needs to be exported by the entry point index.d.ts @@ -340,6 +359,8 @@ export interface KubernetesObjects { error?: string; // (undocumented) kubernetesObjects?: ObjectsByEntityResponse; + // (undocumented) + loading: boolean; } // Warning: (ae-missing-release-tag) "kubernetesPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -403,6 +424,13 @@ export class ServerSideKubernetesAuthProvider // @public (undocumented) export const ServicesAccordions: ({}: ServicesAccordionsProps) => JSX.Element; +// @public +export const useCustomResources: ( + entity: Entity, + customResourceMatchers: CustomResourceMatcher[], + intervalMs?: number, +) => KubernetesObjects; + // Warning: (ae-missing-release-tag) "useKubernetesObjects" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/kubernetes/dev/index.tsx b/plugins/kubernetes/dev/index.tsx index 068f26b7f5..77708f1287 100644 --- a/plugins/kubernetes/dev/index.tsx +++ b/plugins/kubernetes/dev/index.tsx @@ -24,8 +24,10 @@ import { KubernetesApi, } from '../src'; import { + CustomObjectsByEntityRequest, FetchResponse, ObjectsByEntityResponse, + WorkloadsByEntityRequest, } from '@backstage/plugin-kubernetes-common'; import fixture1 from '../src/__fixtures__/1-deployments.json'; import fixture2 from '../src/__fixtures__/2-deployments.json'; @@ -59,6 +61,34 @@ class MockKubernetesClient implements KubernetesApi { ({ type: type.toLocaleLowerCase('en-US'), resources } as FetchResponse), ); } + async getWorkloadsByEntity( + _request: WorkloadsByEntityRequest, + ): Promise { + return { + items: [ + { + cluster: { name: 'mock-cluster' }, + resources: this.resources, + podMetrics: [], + errors: [], + }, + ], + }; + } + async getCustomObjectsByEntity( + _request: CustomObjectsByEntityRequest, + ): Promise { + return { + items: [ + { + cluster: { name: 'mock-cluster' }, + resources: this.resources, + podMetrics: [], + errors: [], + }, + ], + }; + } async getObjectsByEntity(): Promise { return { diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 27b91b7c79..a6c3171529 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.7.2-next.3", + "version": "0.7.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,13 +33,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-kubernetes-common": "^0.4.2-next.1", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-kubernetes-common": "workspace:^", + "@backstage/theme": "workspace:^", "@kubernetes/client-node": "^0.17.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -56,10 +56,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts index 209f5f950d..bffc966333 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -18,8 +18,11 @@ import { KubernetesApi } from './types'; import { KubernetesRequestBody, ObjectsByEntityResponse, + WorkloadsByEntityRequest, + CustomObjectsByEntityRequest, } from '@backstage/plugin-kubernetes-common'; import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; +import { stringifyEntityRef } from '@backstage/catalog-model'; export class KubernetesBackendClient implements KubernetesApi { private readonly discoveryApi: DiscoveryApi; @@ -51,10 +54,7 @@ export class KubernetesBackendClient implements KubernetesApi { return await response.json(); } - private async postRequired( - path: string, - requestBody: KubernetesRequestBody, - ): Promise { + private async postRequired(path: string, requestBody: any): Promise { const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}${path}`; const { token: idToken } = await this.identityApi.getCredentials(); const response = await fetch(url, { @@ -78,6 +78,25 @@ export class KubernetesBackendClient implements KubernetesApi { ); } + async getWorkloadsByEntity( + request: WorkloadsByEntityRequest, + ): Promise { + return await this.postRequired('/resources/workloads/query', { + auth: request.auth, + entityRef: stringifyEntityRef(request.entity), + }); + } + + async getCustomObjectsByEntity( + request: CustomObjectsByEntityRequest, + ): Promise { + return await this.postRequired(`/resources/custom/query`, { + entityRef: stringifyEntityRef(request.entity), + auth: request.auth, + customResources: request.customResources, + }); + } + async getClusters(): Promise<{ name: string; authProvider: string }[]> { const { token: idToken } = await this.identityApi.getCredentials(); const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/clusters`; diff --git a/plugins/kubernetes/src/api/types.ts b/plugins/kubernetes/src/api/types.ts index 7c5943a6eb..0005a8d069 100644 --- a/plugins/kubernetes/src/api/types.ts +++ b/plugins/kubernetes/src/api/types.ts @@ -17,6 +17,8 @@ import { KubernetesRequestBody, ObjectsByEntityResponse, + WorkloadsByEntityRequest, + CustomObjectsByEntityRequest, } from '@backstage/plugin-kubernetes-common'; import { createApiRef } from '@backstage/core-plugin-api'; @@ -35,4 +37,10 @@ export interface KubernetesApi { oidcTokenProvider?: string | undefined; }[] >; + getWorkloadsByEntity( + request: WorkloadsByEntityRequest, + ): Promise; + getCustomObjectsByEntity( + request: CustomObjectsByEntityRequest, + ): Promise; } diff --git a/plugins/kubernetes/src/hooks/auth.test.ts b/plugins/kubernetes/src/hooks/auth.test.ts new file mode 100644 index 0000000000..aa2b9afb0d --- /dev/null +++ b/plugins/kubernetes/src/hooks/auth.test.ts @@ -0,0 +1,126 @@ +/* + * Copyright 2022 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 { generateAuth } from './auth'; + +jest.mock('@backstage/core-plugin-api'); + +const entity = { + metadata: { + name: 'some-entity', + }, +} as Entity; + +const entityWithAuthToken = { + auth: { + google: 'some-token', + }, + entity, +}; + +const getClustersResponse = [ + { + name: 'cluster-a', + authProvider: 'google', + }, + { + name: 'cluster-b', + authProvider: 'authprovider2', + }, +]; + +describe('generateAuth', () => { + const mockGetClusters = jest.fn(); + const mockDecorateRequestBodyForAuth = jest.fn(); + + const expectMocksCalledCorrectly = (numOfCalls: number = 1) => { + expect(mockGetClusters).toHaveBeenCalledTimes(numOfCalls); + expect(mockGetClusters).toHaveBeenLastCalledWith(); + expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledTimes( + numOfCalls * 2, + ); + expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith('google', { + entity, + }); + expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith( + 'authprovider2', + entityWithAuthToken, + ); + }; + + afterEach(() => { + jest.resetAllMocks(); + }); + it('should return auth', async () => { + const result = await generateAuth( + entity, + { + getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), + } as any, + { + decorateRequestBodyForAuth: + mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), + } as any, + ); + + expect(result).toStrictEqual({ + google: 'some-token', + }); + expectMocksCalledCorrectly(); + }); + + it('should return error when getClusters throws', async () => { + await expect( + generateAuth( + entity, + { + getClusters: mockGetClusters.mockRejectedValue('some-cluster-error'), + } as any, + { + decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth, + } as any, + ), + ).rejects.toBe('some-cluster-error'); + + expect(mockGetClusters).toHaveBeenCalledTimes(1); + expect(mockGetClusters).toHaveBeenLastCalledWith(); + expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledTimes(0); + }); + it('should return error when decorateRequestBodyForAuth throws', async () => { + await expect( + generateAuth( + entity, + { + getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), + } as any, + { + decorateRequestBodyForAuth: + mockDecorateRequestBodyForAuth.mockRejectedValue( + 'some-decorate-error', + ), + } as any, + ), + ).rejects.toBe('some-decorate-error'); + + expect(mockGetClusters).toHaveBeenCalledTimes(1); + expect(mockGetClusters).toHaveBeenLastCalledWith(); + expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledTimes(1); + expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith('google', { + entity, + }); + }); +}); diff --git a/plugins/kubernetes/src/hooks/auth.ts b/plugins/kubernetes/src/hooks/auth.ts new file mode 100644 index 0000000000..cce0bc27d6 --- /dev/null +++ b/plugins/kubernetes/src/hooks/auth.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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 { KubernetesApi } from '../api/types'; +import { KubernetesAuthProvidersApi } from '../kubernetes-auth-provider/types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; + +export const generateAuth = async ( + entity: Entity, + kubernetesApi: KubernetesApi, + kubernetesAuthProvidersApi: KubernetesAuthProvidersApi, +) => { + const clusters = await kubernetesApi.getClusters(); + + const authProviders: string[] = [ + ...new Set( + clusters.map( + c => + `${c.authProvider}${ + c.oidcTokenProvider ? `.${c.oidcTokenProvider}` : '' + }`, + ), + ), + ]; + + let requestBody: KubernetesRequestBody = { + entity, + }; + for (const authProviderStr of authProviders) { + requestBody = await kubernetesAuthProvidersApi.decorateRequestBodyForAuth( + authProviderStr, + requestBody, + ); + } + return requestBody.auth ?? {}; +}; diff --git a/plugins/kubernetes/src/hooks/index.ts b/plugins/kubernetes/src/hooks/index.ts index 0a5835eee4..c3968a19b2 100644 --- a/plugins/kubernetes/src/hooks/index.ts +++ b/plugins/kubernetes/src/hooks/index.ts @@ -15,6 +15,7 @@ */ export * from './useKubernetesObjects'; +export * from './useCustomResources'; export * from './PodNamesWithErrors'; export * from './PodNamesWithMetrics'; export * from './GroupedResponses'; diff --git a/plugins/kubernetes/src/hooks/types.ts b/plugins/kubernetes/src/hooks/types.ts new file mode 100644 index 0000000000..0796089baf --- /dev/null +++ b/plugins/kubernetes/src/hooks/types.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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 { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; + +export interface KubernetesObjects { + kubernetesObjects: ObjectsByEntityResponse; + loading: boolean; + error: string; +} diff --git a/plugins/kubernetes/src/hooks/useCustomResources.test.ts b/plugins/kubernetes/src/hooks/useCustomResources.test.ts new file mode 100644 index 0000000000..abd8fc392c --- /dev/null +++ b/plugins/kubernetes/src/hooks/useCustomResources.test.ts @@ -0,0 +1,256 @@ +/* + * 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 { useCustomResources } from './useCustomResources'; +import { Entity } from '@backstage/catalog-model'; +import { renderHook } from '@testing-library/react-hooks'; +import { useApi } from '@backstage/core-plugin-api'; +import { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common'; +import { generateAuth } from './auth'; + +jest.mock('@backstage/core-plugin-api'); + +const entity = { + metadata: { + name: 'some-entity', + }, +} as Entity; + +const customResourceMatchers: CustomResourceMatcher[] = [ + { + group: 'myGroup', + apiVersion: 'v1', + plural: 'thing', + }, +]; + +const entityWithAuthToken = { + auth: { + google: 'some-token', + }, + customResources: customResourceMatchers, + entity, +}; + +const mockResponse = { + items: [ + { + cluster: { name: 'some-cluster' }, + resources: [ + { + type: 'pods', + resources: [ + { + metadata: { + name: 'some-pod', + }, + }, + ], + }, + ], + errors: [], + }, + ], +}; + +jest.mock('./auth', () => { + return { + ...jest.requireActual('./auth'), + generateAuth: jest.fn(), + }; +}); + +describe('useCustomResources', () => { + const mockGetCustomObjectsByEntity = jest.fn(); + const mockGenerateAuth = generateAuth as jest.Mock; + + const expectMocksCalledCorrectly = (numOfCalls: number = 1) => { + expect(mockGenerateAuth).toHaveBeenCalledTimes(numOfCalls); + expect(mockGenerateAuth.mock.calls[numOfCalls - 1][0]).toStrictEqual( + entity, + ); + expect(mockGetCustomObjectsByEntity).toHaveBeenCalledTimes(numOfCalls); + expect(mockGetCustomObjectsByEntity).toHaveBeenLastCalledWith( + entityWithAuthToken, + ); + }; + + afterEach(() => { + jest.resetAllMocks(); + }); + it('should return objects', async () => { + mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth); + (useApi as any).mockReturnValue({ + getCustomObjectsByEntity: + mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse), + }); + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers), + ); + + expect(result.current.loading).toEqual(true); + + await waitForNextUpdate(); + + expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toStrictEqual(mockResponse); + + expectMocksCalledCorrectly(); + }); + it('should update on an interval', async () => { + mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth); + (useApi as any).mockReturnValue({ + getCustomObjectsByEntity: + mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse), + }); + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers, 100), + ); + + await waitForNextUpdate(); + expect(result.current.error).toBeUndefined(); + + await waitForNextUpdate(); + + expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toStrictEqual(mockResponse); + + expectMocksCalledCorrectly(2); + }); + it('should return error when getObjectsByEntity throws', async () => { + mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth); + (useApi as any).mockReturnValue({ + getCustomObjectsByEntity: mockGetCustomObjectsByEntity.mockRejectedValue({ + message: 'some error', + }), + }); + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers), + ); + + await waitForNextUpdate(); + + expect(result.current.error).toBe('some error'); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toBeUndefined(); + + expectMocksCalledCorrectly(); + }); + + describe('when retrying', () => { + it('should reset error after generateAuth has failed and then succeeded', async () => { + (useApi as any).mockReturnValue({ + generateAuth: mockGenerateAuth + .mockRejectedValueOnce({ message: 'generateAuth failed' }) + .mockResolvedValue(entityWithAuthToken.auth), + getCustomObjectsByEntity: + mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse), + }); + + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers, 100), + ); + + await waitForNextUpdate(); + + expect(result.current.error).toBe('generateAuth failed'); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toBeUndefined(); + + await waitForNextUpdate(); + + expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).not.toBeUndefined(); + }); + + it('should reset error after getCustomObjectsByEntity has failed and then succeeded', async () => { + (useApi as any).mockReturnValue({ + getCustomObjectsByEntity: mockGetCustomObjectsByEntity + .mockRejectedValueOnce({ message: 'failed to fetch' }) + .mockResolvedValue(mockResponse), + }); + + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers, 100), + ); + + await waitForNextUpdate(); + + expect(result.current.error).toBe('failed to fetch'); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toBeUndefined(); + + await waitForNextUpdate(); + + expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).not.toBeUndefined(); + }); + + it('should reset data after generateAuth succeeded then failed', async () => { + (useApi as any).mockReturnValue({ + generateAuth: mockGenerateAuth + .mockResolvedValueOnce(entityWithAuthToken.auth) + .mockRejectedValue({ message: 'generateAuth failed' }), + getCustomObjectsByEntity: + mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse), + }); + + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers, 100), + ); + + await waitForNextUpdate(); + + expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).not.toBeUndefined(); + + await waitForNextUpdate(); + + expect(result.current.error).toBe('generateAuth failed'); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toBeUndefined(); + }); + + it('should reset data after getCustomObjectsByEntity succeeded then failed', async () => { + (useApi as any).mockReturnValue({ + getCustomObjectsByEntity: mockGetCustomObjectsByEntity + .mockResolvedValueOnce(mockResponse) + .mockRejectedValue({ message: 'failed to fetch' }), + }); + + const { result, waitForNextUpdate } = renderHook(() => + useCustomResources(entity, customResourceMatchers, 100), + ); + + await waitForNextUpdate(); + + expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).not.toBeUndefined(); + + await waitForNextUpdate(); + + expect(result.current.error).toBe('failed to fetch'); + expect(result.current.loading).toEqual(false); + expect(result.current.kubernetesObjects).toBeUndefined(); + }); + }); +}); diff --git a/plugins/kubernetes/src/hooks/useCustomResources.ts b/plugins/kubernetes/src/hooks/useCustomResources.ts new file mode 100644 index 0000000000..c0ec7d91ea --- /dev/null +++ b/plugins/kubernetes/src/hooks/useCustomResources.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2022 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 { kubernetesApiRef } from '../api/types'; +import { kubernetesAuthProvidersApiRef } from '../kubernetes-auth-provider/types'; +import { useCallback } from 'react'; +import useInterval from 'react-use/lib/useInterval'; +import { + CustomResourceMatcher, + ObjectsByEntityResponse, +} from '@backstage/plugin-kubernetes-common'; +import { useApi } from '@backstage/core-plugin-api'; +import { KubernetesObjects } from './useKubernetesObjects'; +import { generateAuth } from './auth'; +import useAsyncRetry from 'react-use/lib/useAsyncRetry'; + +/** + * Retrieves the provided custom resources related to the provided entity, refreshes at an interval. + * + * @public + */ +export const useCustomResources = ( + entity: Entity, + customResourceMatchers: CustomResourceMatcher[], + intervalMs: number = 10000, +): KubernetesObjects => { + const kubernetesApi = useApi(kubernetesApiRef); + const kubernetesAuthProvidersApi = useApi(kubernetesAuthProvidersApiRef); + + const getCustomObjects = + useCallback(async (): Promise => { + const auth = await generateAuth( + entity, + kubernetesApi, + kubernetesAuthProvidersApi, + ); + return await kubernetesApi.getCustomObjectsByEntity({ + auth, + customResources: customResourceMatchers, + entity, + }); + }, [ + kubernetesApi, + entity, + kubernetesAuthProvidersApi, + customResourceMatchers, + ]); + + const { value, loading, error, retry } = useAsyncRetry( + () => getCustomObjects(), + [getCustomObjects], + ); + + useInterval(() => retry(), intervalMs); + + return { + kubernetesObjects: value, + loading, + error: error?.message, + }; +}; diff --git a/plugins/kubernetes/src/hooks/useKubernetesObjects.test.ts b/plugins/kubernetes/src/hooks/useKubernetesObjects.test.ts index 4b66467403..5c4db4db14 100644 --- a/plugins/kubernetes/src/hooks/useKubernetesObjects.test.ts +++ b/plugins/kubernetes/src/hooks/useKubernetesObjects.test.ts @@ -18,6 +18,7 @@ import { useKubernetesObjects } from './useKubernetesObjects'; import { Entity } from '@backstage/catalog-model'; import { renderHook } from '@testing-library/react-hooks'; import { useApi } from '@backstage/core-plugin-api'; +import { generateAuth } from './auth'; jest.mock('@backstage/core-plugin-api'); @@ -55,34 +56,21 @@ const mockResponse = { ], }; -const getClustersResponse = [ - { - name: 'cluster-a', - authProvider: 'google', - }, - { - name: 'cluster-b', - authProvider: 'authprovider2', - }, -]; +jest.mock('./auth', () => { + return { + ...jest.requireActual('./auth'), + generateAuth: jest.fn(), + }; +}); describe('useKubernetesObjects', () => { - const mockGetClusters = jest.fn(); const mockGetObjectsByEntity = jest.fn(); - const mockDecorateRequestBodyForAuth = jest.fn(); + const mockGenerateAuth = generateAuth as jest.Mock; const expectMocksCalledCorrectly = (numOfCalls: number = 1) => { - expect(mockGetClusters).toHaveBeenCalledTimes(numOfCalls); - expect(mockGetClusters).toHaveBeenLastCalledWith(); - expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledTimes( - numOfCalls * 2, - ); - expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith('google', { + expect(mockGenerateAuth).toHaveBeenCalledTimes(numOfCalls); + expect(mockGenerateAuth.mock.calls[numOfCalls - 1][0]).toStrictEqual( entity, - }); - expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith( - 'authprovider2', - entityWithAuthToken, ); expect(mockGetObjectsByEntity).toHaveBeenCalledTimes(numOfCalls); expect(mockGetObjectsByEntity).toHaveBeenLastCalledWith( @@ -94,31 +82,30 @@ describe('useKubernetesObjects', () => { jest.resetAllMocks(); }); it('should return objects', async () => { + mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth); (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), getObjectsByEntity: mockGetObjectsByEntity.mockResolvedValue(mockResponse), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), }); const { result, waitForNextUpdate } = renderHook(() => useKubernetesObjects(entity), ); + expect(result.current.loading).toEqual(true); + await waitForNextUpdate(); expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toStrictEqual(mockResponse); expectMocksCalledCorrectly(); }); it('should update on an interval', async () => { + mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth); (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), getObjectsByEntity: mockGetObjectsByEntity.mockResolvedValue(mockResponse), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), }); const { result, waitForNextUpdate } = renderHook(() => useKubernetesObjects(entity, 100), @@ -130,18 +117,17 @@ describe('useKubernetesObjects', () => { await waitForNextUpdate(); expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toStrictEqual(mockResponse); expectMocksCalledCorrectly(2); }); it('should return error when getObjectsByEntity throws', async () => { + mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth); (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), getObjectsByEntity: mockGetObjectsByEntity.mockRejectedValue({ message: 'some error', }), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), }); const { result, waitForNextUpdate } = renderHook(() => useKubernetesObjects(entity), @@ -150,66 +136,18 @@ describe('useKubernetesObjects', () => { await waitForNextUpdate(); expect(result.current.error).toBe('some error'); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toBeUndefined(); expectMocksCalledCorrectly(); }); - it('should return error when getClusters throws', async () => { - (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockRejectedValue({ message: 'some-error' }), - getObjectsByEntity: mockGetObjectsByEntity, - decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth, - }); - const { result, waitForNextUpdate } = renderHook(() => - useKubernetesObjects(entity), - ); - - await waitForNextUpdate(); - - expect(result.current.error).toBe('some-error'); - expect(result.current.kubernetesObjects).toBeUndefined(); - - expect(mockGetClusters).toHaveBeenCalledTimes(1); - expect(mockGetClusters).toHaveBeenLastCalledWith(); - expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledTimes(0); - expect(mockGetObjectsByEntity).toHaveBeenCalledTimes(0); - }); - it('should return error when decorateRequestBodyForAuth throws', async () => { - (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockRejectedValue({ - message: 'some-error', - }), - getObjectsByEntity: mockGetObjectsByEntity, - }); - const { result, waitForNextUpdate } = renderHook(() => - useKubernetesObjects(entity), - ); - - await waitForNextUpdate(); - - expect(result.current.error).toBe('some-error'); - expect(result.current.kubernetesObjects).toBeUndefined(); - - expect(mockGetClusters).toHaveBeenCalledTimes(1); - expect(mockGetClusters).toHaveBeenLastCalledWith(); - expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledTimes(1); - expect(mockDecorateRequestBodyForAuth).toHaveBeenCalledWith('google', { - entity, - }); - expect(mockGetObjectsByEntity).toHaveBeenCalledTimes(0); - }); - describe('when retrying', () => { - it('should reset error after getClusters has failed and then succeeded', async () => { + it('should reset error after generateAuth has failed and then succeeded', async () => { (useApi as any).mockReturnValue({ - getClusters: mockGetClusters - .mockRejectedValueOnce({ message: 'some-error' }) - .mockResolvedValue(getClustersResponse), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), + generateAuth: mockGenerateAuth + .mockRejectedValueOnce({ message: 'generateAuth failed' }) + .mockResolvedValue(entityWithAuthToken.auth), getObjectsByEntity: mockGetObjectsByEntity.mockResolvedValue(mockResponse), }); @@ -220,45 +158,19 @@ describe('useKubernetesObjects', () => { await waitForNextUpdate(); - expect(result.current.error).toBe('some-error'); - expect(result.current.kubernetesObjects).toBeUndefined(); - - await waitForNextUpdate(); - - expect(result.current.error).toBeUndefined(); - expect(result.current.kubernetesObjects).not.toBeUndefined(); - }); - - it('should reset error after decorateRequestBodyForAuth has failed and then succeeded', async () => { - (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), - decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth - .mockRejectedValueOnce({ message: 'decoration failed' }) - .mockResolvedValue(entityWithAuthToken), - getObjectsByEntity: - mockGetObjectsByEntity.mockResolvedValue(mockResponse), - }); - - const { result, waitForNextUpdate } = renderHook(() => - useKubernetesObjects(entity, 100), - ); - - await waitForNextUpdate(); - - expect(result.current.error).toBe('decoration failed'); + expect(result.current.error).toBe('generateAuth failed'); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toBeUndefined(); await waitForNextUpdate(); expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).not.toBeUndefined(); }); it('should reset error after getObjectsByEntity has failed and then succeeded', async () => { (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), getObjectsByEntity: mockGetObjectsByEntity .mockRejectedValueOnce({ message: 'failed to fetch' }) .mockResolvedValue(mockResponse), @@ -271,47 +183,21 @@ describe('useKubernetesObjects', () => { await waitForNextUpdate(); expect(result.current.error).toBe('failed to fetch'); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toBeUndefined(); await waitForNextUpdate(); expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).not.toBeUndefined(); }); - it('should reset data after getClusters succeeded then failed', async () => { + it('should reset data after generateAuth succeeded then failed', async () => { (useApi as any).mockReturnValue({ - getClusters: mockGetClusters - .mockResolvedValueOnce(getClustersResponse) - .mockRejectedValue({ message: 'fetch clusters failed' }), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), - getObjectsByEntity: - mockGetObjectsByEntity.mockResolvedValue(mockResponse), - }); - const { result, waitForNextUpdate } = renderHook(() => - useKubernetesObjects(entity, 100), - ); - - await waitForNextUpdate(); - - expect(result.current.error).toBeUndefined(); - expect(result.current.kubernetesObjects).not.toBeUndefined(); - - await waitForNextUpdate(); - - expect(result.current.error).toBe('fetch clusters failed'); - expect(result.current.kubernetesObjects).toBeUndefined(); - }); - - it('should reset data after decorateBodyForAuth succeeded then failed', async () => { - (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), - decorateRequestBodyForAuth: mockDecorateRequestBodyForAuth - // this call happens twice per successful hook render - .mockResolvedValueOnce(entityWithAuthToken) - .mockResolvedValueOnce(entityWithAuthToken) - .mockRejectedValue({ message: 'decorate failed' }), + generateAuth: mockGenerateAuth + .mockResolvedValueOnce(entityWithAuthToken.auth) + .mockRejectedValue({ message: 'generateAuth failed' }), getObjectsByEntity: mockGetObjectsByEntity.mockResolvedValue(mockResponse), }); @@ -323,19 +209,18 @@ describe('useKubernetesObjects', () => { await waitForNextUpdate(); expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).not.toBeUndefined(); await waitForNextUpdate(); - expect(result.current.error).toBe('decorate failed'); + expect(result.current.error).toBe('generateAuth failed'); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toBeUndefined(); }); it('should reset data after getObjectsByEntity succeeded then failed', async () => { (useApi as any).mockReturnValue({ - getClusters: mockGetClusters.mockResolvedValue(getClustersResponse), - decorateRequestBodyForAuth: - mockDecorateRequestBodyForAuth.mockResolvedValue(entityWithAuthToken), getObjectsByEntity: mockGetObjectsByEntity .mockResolvedValueOnce(mockResponse) .mockRejectedValue({ message: 'failed to fetch' }), @@ -348,11 +233,13 @@ describe('useKubernetesObjects', () => { await waitForNextUpdate(); expect(result.current.error).toBeUndefined(); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).not.toBeUndefined(); await waitForNextUpdate(); expect(result.current.error).toBe('failed to fetch'); + expect(result.current.loading).toEqual(false); expect(result.current.kubernetesObjects).toBeUndefined(); }); }); diff --git a/plugins/kubernetes/src/hooks/useKubernetesObjects.ts b/plugins/kubernetes/src/hooks/useKubernetesObjects.ts index 421ed787da..87d6a0fe8b 100644 --- a/plugins/kubernetes/src/hooks/useKubernetesObjects.ts +++ b/plugins/kubernetes/src/hooks/useKubernetesObjects.ts @@ -17,16 +17,16 @@ import { Entity } from '@backstage/catalog-model'; import { kubernetesApiRef } from '../api/types'; import { kubernetesAuthProvidersApiRef } from '../kubernetes-auth-provider/types'; -import { useEffect, useState } from 'react'; +import { useCallback } from 'react'; import useInterval from 'react-use/lib/useInterval'; -import { - KubernetesRequestBody, - ObjectsByEntityResponse, -} from '@backstage/plugin-kubernetes-common'; +import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import { useApi } from '@backstage/core-plugin-api'; +import { generateAuth } from './auth'; +import useAsyncRetry from 'react-use/lib/useAsyncRetry'; export interface KubernetesObjects { kubernetesObjects?: ObjectsByEntityResponse; + loading: boolean; error?: string; } @@ -36,68 +36,29 @@ export const useKubernetesObjects = ( ): KubernetesObjects => { const kubernetesApi = useApi(kubernetesApiRef); const kubernetesAuthProvidersApi = useApi(kubernetesAuthProvidersApiRef); - const [result, setResult] = useState({ - kubernetesObjects: undefined, - error: undefined, - }); + const getCustomObjects = + useCallback(async (): Promise => { + const auth = await generateAuth( + entity, + kubernetesApi, + kubernetesAuthProvidersApi, + ); + return await kubernetesApi.getObjectsByEntity({ + auth, + entity, + }); + }, [kubernetesApi, entity, kubernetesAuthProvidersApi]); - const getObjects = async () => { - let clusters = []; + const { value, loading, error, retry } = useAsyncRetry( + () => getCustomObjects(), + [getCustomObjects], + ); - try { - clusters = await kubernetesApi.getClusters(); - } catch (e) { - setResult({ error: e.message }); - return; - } + useInterval(() => retry(), intervalMs); - const authProviders: string[] = [ - ...new Set( - clusters.map( - c => - `${c.authProvider}${ - c.oidcTokenProvider ? `.${c.oidcTokenProvider}` : '' - }`, - ), - ), - ]; - - // For each auth type, invoke decorateRequestBodyForAuth on corresponding KubernetesAuthProvider - let requestBody: KubernetesRequestBody = { - entity, - }; - for (const authProviderStr of authProviders) { - // Multiple asyncs done sequentially instead of all at once to prevent same requestBody from being modified simultaneously - try { - requestBody = - await kubernetesAuthProvidersApi.decorateRequestBodyForAuth( - authProviderStr, - requestBody, - ); - } catch (e) { - setResult({ error: e.message }); - return; - } - } - - try { - const objects = await kubernetesApi.getObjectsByEntity(requestBody); - setResult({ kubernetesObjects: objects }); - } catch (e) { - setResult({ error: e.message }); - return; - } + return { + kubernetesObjects: value, + loading, + error: error?.message, }; - - useEffect(() => { - getObjects(); - /* eslint-disable react-hooks/exhaustive-deps */ - }, [entity.metadata.name, kubernetesApi, kubernetesAuthProvidersApi]); - /* eslint-enable react-hooks/exhaustive-deps */ - - useInterval(() => { - getObjects(); - }, intervalMs); - - return result; }; diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 7cb872e30f..7980f276d6 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-lighthouse +## 0.3.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- f6be17460d: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + ## 0.3.9-next.3 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index cfe7be9b16..78ae3237cb 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse", "description": "A Backstage plugin that integrates towards Lighthouse", - "version": "0.3.9-next.3", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,12 +33,12 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -49,10 +49,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/newrelic-dashboard/CHANGELOG.md b/plugins/newrelic-dashboard/CHANGELOG.md index 8668e588fa..bd9d5cd4ca 100644 --- a/plugins/newrelic-dashboard/CHANGELOG.md +++ b/plugins/newrelic-dashboard/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-newrelic-dashboard +## 0.2.2 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- d669d89206: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.2.2-next.2 ### Patch Changes diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json index d08ce75ef3..986ebd44ab 100644 --- a/plugins/newrelic-dashboard/package.json +++ b/plugins/newrelic-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic-dashboard", - "version": "0.2.2-next.2", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,19 +22,19 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@types/react": "^16.13.1 || ^17.0.0", "cross-fetch": "^3.1.5" diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index 3748b744de..66adaffc65 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-newrelic +## 0.3.27 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + ## 0.3.27-next.3 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 978b0abddd..c76f73c41a 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-newrelic", "description": "A Backstage plugin that integrates towards New Relic", - "version": "0.3.27-next.3", + "version": "0.3.27", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,9 +33,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/theme": "^0.2.16", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -45,10 +45,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 568bba4890..7e535db9e5 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-org +## 0.5.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- ab6650ede9: Added an edit button to the `UserProfileCard` that is enabled when the `backstage.io/edit-url` is present, this matches how the `GroupProfileCard` works +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.5.9-next.3 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index f62d938486..9fb4b45a32 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.5.9-next.3", + "version": "0.5.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,11 +28,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -47,11 +47,11 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/catalog-client": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/org/src/components/Cards/Group/GroupProfile/GroupProfileCard.tsx b/plugins/org/src/components/Cards/Group/GroupProfile/GroupProfileCard.tsx index 174067275d..dfdb566b20 100644 --- a/plugins/org/src/components/Cards/Group/GroupProfile/GroupProfileCard.tsx +++ b/plugins/org/src/components/Cards/Group/GroupProfile/GroupProfileCard.tsx @@ -144,43 +144,52 @@ export const GroupProfileCard = (props: { variant?: InfoCardVariants }) => { - - {profile.email} - + {profile.email}} + secondary="Email" + /> )} - - {parentRelations.length ? ( - - - - - - - - - - - ) : null} - - {childRelations.length ? ( - - - - - - - - - - - ) : null} + + + + + + + + ) : ( + 'N/A' + ) + } + secondary="Parent Group" + /> + + + + + + + + + ) : ( + 'N/A' + ) + } + secondary="Child Groups" + /> + diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 98485e3836..b4fdd60fae 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-pagerduty +## 0.5.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 3f739be9d9: Minor API signatures cleanup +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.5.2-next.3 ### Patch Changes diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index dda460bbb6..4909465deb 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-pagerduty", "description": "A Backstage plugin that integrates towards PagerDuty", - "version": "0.5.2-next.3", + "version": "0.5.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,12 +33,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -52,10 +52,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/periskop-backend/CHANGELOG.md b/plugins/periskop-backend/CHANGELOG.md index adf1520cf9..d23f635d8d 100644 --- a/plugins/periskop-backend/CHANGELOG.md +++ b/plugins/periskop-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-periskop-backend +## 0.1.7 + +### Patch Changes + +- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + ## 0.1.7-next.3 ### Patch Changes diff --git a/plugins/periskop-backend/package.json b/plugins/periskop-backend/package.json index c49d71028e..4704370394 100644 --- a/plugins/periskop-backend/package.json +++ b/plugins/periskop-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop-backend", - "version": "0.1.7-next.3", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,8 +23,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", "@types/express": "*", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -33,7 +33,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^0.47.0", "supertest": "^6.1.6" diff --git a/plugins/periskop/CHANGELOG.md b/plugins/periskop/CHANGELOG.md index 7b5cccc8d4..b4258a2d73 100644 --- a/plugins/periskop/CHANGELOG.md +++ b/plugins/periskop/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-periskop +## 0.1.7 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + ## 0.1.7-next.2 ### Patch Changes diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index b4494bc934..94ec1fdc21 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop", - "version": "0.1.7-next.2", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,12 +23,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -40,10 +40,10 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 615104ee9b..9dad2e00fb 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-permission-backend +## 0.5.11 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 2cbd533426: Uptake the `IdentityApi` change to use `getIdentity` instead of `authenticate` for retrieving the logged in users identity. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + ## 0.5.11-next.2 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 42dd4fcd1c..234e633482 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.5.11-next.2", + "version": "0.5.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/plugin-permission-common": "^0.6.4-next.2", - "@backstage/plugin-permission-node": "^0.6.5-next.3", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", "@types/express": "*", "dataloader": "^2.0.0", "express": "^4.17.1", @@ -39,7 +39,7 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", "@types/supertest": "^2.0.8", "msw": "^0.47.0", diff --git a/plugins/permission-common/CHANGELOG.md b/plugins/permission-common/CHANGELOG.md index 0a64a7faf8..b906ac0038 100644 --- a/plugins/permission-common/CHANGELOG.md +++ b/plugins/permission-common/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-common +## 0.6.4 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.6.4-next.2 ### Patch Changes diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index 5a6d98aba5..e83131387a 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-common", "description": "Isomorphic types and client for Backstage permissions and authorization", - "version": "0.6.4-next.2", + "version": "0.6.4", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -41,14 +41,14 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "cross-fetch": "^3.1.5", "uuid": "^8.0.0", "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "msw": "^0.47.0" } } diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 6ac474d76d..0543ac4e43 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-permission-node +## 0.6.5 + +### Patch Changes + +- 9212439d15: Minor update to tests +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + ## 0.6.5-next.3 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 6cb3f771d5..354aced054 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.6.5-next.3", + "version": "0.6.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,19 +33,19 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/plugin-permission-common": "^0.6.4-next.2", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^0.47.0", "supertest": "^6.1.3" diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index 8cc4c02424..c69fe65e0a 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-permission-react +## 0.4.5 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 817f3196f6: **DEPRECATION**: The `PermissionedRoute` component has been deprecated in favor of the new `RequirePermission` component. This is because the usage pattern of `PermissionedRoute` is not compatible with React Router v6 stable. + + Embed the type from `react-router` instead of exporting it directly. + +- Updated dependencies + - @backstage/core-plugin-api@1.0.6 + - @backstage/config@1.0.2 + - @backstage/plugin-permission-common@0.6.4 + ## 0.4.5-next.2 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 82c5140f86..faaca8829f 100644 --- a/plugins/permission-react/package.json +++ b/plugins/permission-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-react", - "version": "0.4.5-next.2", + "version": "0.4.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-permission-common": "^0.6.4-next.2", + "@backstage/config": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", "cross-fetch": "^3.1.5", "react-use": "^17.2.4", "swr": "^1.1.2" @@ -44,8 +44,8 @@ "react-router": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3" }, diff --git a/plugins/playlist-backend/.eslintrc.js b/plugins/playlist-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/playlist-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/playlist-backend/CHANGELOG.md b/plugins/playlist-backend/CHANGELOG.md new file mode 100644 index 0000000000..ca4ab28ba7 --- /dev/null +++ b/plugins/playlist-backend/CHANGELOG.md @@ -0,0 +1,21 @@ +# @backstage/plugin-playlist-backend + +## 0.1.0 + +### Minor Changes + +- d3737da337: Implement playlist plugin, check out the `README.md` for more details! + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-permission-node@0.6.5 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/backend-test-utils@0.1.28 + - @backstage/plugin-playlist-common@0.1.0 diff --git a/plugins/playlist-backend/README.md b/plugins/playlist-backend/README.md new file mode 100644 index 0000000000..14a39ca741 --- /dev/null +++ b/plugins/playlist-backend/README.md @@ -0,0 +1,88 @@ +# Playlist Backend + +Welcome to the playlist backend plugin! + +# Installation + +## Install the package + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-playlist-backend +``` + +## Adding the plugin to your `packages/backend` + +You'll need to add the plugin to the router in your `backend` package. You can do this by creating a file called `packages/backend/src/plugins/playlist.ts` + +```tsx +import { IdentityClient } from '@backstage/plugin-auth-node'; +import { createRouter } from '@backstage/plugin-playlist-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return await createRouter({ + database: env.database, + discovery: env.discovery, + identity: env.identity, + logger: env.logger, + permissions: env.permissions, + }); +} +``` + +With the `playlist.ts` router setup in place, add the router to `packages/backend/src/index.ts`: + +```diff ++import playlist from './plugins/playlist'; + +async function main() { + ... + const createEnv = makeCreateEnv(config); + + const catalogEnv = useHotMemoize(module, () => createEnv('catalog')); ++ const playlistEnv = useHotMemoize(module, () => createEnv('playlist')); + + const apiRouter = Router(); ++ apiRouter.use('/playlist', await playlist(playlistEnv)); + ... + apiRouter.use(notFoundHandler()); + +``` + +## Setting up plugin permissions + +You configure permissions for specific playlist actions by importing the supported set of permissions from the [playlist-common](../playlist-common/README.md) package along with the custom rules/conditions provided here to incorporate into your [permission policy](https://backstage.io/docs/permissions/writing-a-policy). + +This package also exports a `DefaultPlaylistPermissionPolicy` which contains a recommended default permissions policy you can apply as a "sub-policy" in your app: + +```diff +# packages/backend/src/plugins/permission.ts + ++import { DefaultPlaylistPermissionPolicy, isPlaylistPermission } from '@backstage/plugin-playlist-backend'; +... +class BackstagePermissionPolicy implements PermissionPolicy { ++ private playlistPermissionPolicy = new DefaultPlaylistPermissionPolicy(); + + async handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { ++ if (isPlaylistPermission(request.permission)) { ++ return this.playlistPermissionPolicy.handle(request, user); ++ } + ... + } +} + +export default async function createPlugin(env: PluginEnvironment): Promise { + return await createRouter({ + config: env.config, + logger: env.logger, + discovery: env.discovery, + policy: new BackstagePermissionPolicy(), + ... +``` diff --git a/plugins/playlist-backend/api-report.md b/plugins/playlist-backend/api-report.md new file mode 100644 index 0000000000..c7109efbfb --- /dev/null +++ b/plugins/playlist-backend/api-report.md @@ -0,0 +1,96 @@ +## API Report File for "@backstage/plugin-playlist-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; +import { ConditionalPolicyDecision } from '@backstage/plugin-permission-common'; +import { Conditions } from '@backstage/plugin-permission-node'; +import express from 'express'; +import { IdentityApi } from '@backstage/plugin-auth-node'; +import { Logger } from 'winston'; +import { Permission } from '@backstage/plugin-permission-common'; +import { PermissionCondition } from '@backstage/plugin-permission-common'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; +import { PermissionEvaluator } from '@backstage/plugin-permission-common'; +import { PermissionPolicy } from '@backstage/plugin-permission-node'; +import { PermissionRule } from '@backstage/plugin-permission-node'; +import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; +import { PluginDatabaseManager } from '@backstage/backend-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { PolicyDecision } from '@backstage/plugin-permission-common'; +import { PolicyQuery } from '@backstage/plugin-permission-node'; +import { ResourcePermission } from '@backstage/plugin-permission-common'; + +// @public (undocumented) +export const createPlaylistConditionalDecision: ( + permission: ResourcePermission<'playlist-list'>, + conditions: PermissionCriteria< + PermissionCondition<'playlist-list', unknown[]> + >, +) => ConditionalPolicyDecision; + +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// @public +export class DefaultPlaylistPermissionPolicy implements PermissionPolicy { + // (undocumented) + handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise; +} + +// @public (undocumented) +export const isPlaylistPermission: (permission: Permission) => boolean; + +// @public (undocumented) +export type ListPlaylistsFilter = + | { + allOf: ListPlaylistsFilter[]; + } + | { + anyOf: ListPlaylistsFilter[]; + } + | { + not: ListPlaylistsFilter; + } + | ListPlaylistsMatchFilter; + +// @public (undocumented) +export type ListPlaylistsMatchFilter = { + key: string; + values: any[]; +}; + +// @public (undocumented) +export const playlistConditions: Conditions<{ + isOwner: PermissionRule< + PlaylistMetadata, + ListPlaylistsFilter, + 'playlist-list', + [userOwnershipRefs: string[]] + >; + isPublic: PermissionRule< + PlaylistMetadata, + ListPlaylistsFilter, + 'playlist-list', + [] + >; +}>; + +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + database: PluginDatabaseManager; + // (undocumented) + discovery: PluginEndpointDiscovery; + // (undocumented) + identity: IdentityApi; + // (undocumented) + logger: Logger; + // (undocumented) + permissions: PermissionEvaluator; +} +``` diff --git a/plugins/playlist-backend/migrations/20220701011329_init.js b/plugins/playlist-backend/migrations/20220701011329_init.js new file mode 100644 index 0000000000..be08363dec --- /dev/null +++ b/plugins/playlist-backend/migrations/20220701011329_init.js @@ -0,0 +1,64 @@ +/* + * Copyright 2022 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. + */ + +exports.up = async function up(knex) { + await knex.schema.createTable('playlists', table => { + table.comment('Playlists table'); + table.uuid('id').primary().comment('Automatically generated unique ID'); + table.text('name').notNullable().comment('The name of the playlist'); + table.text('description').comment('The description of the playlist'); + table.string('owner').notNullable().comment('The owner entity ref'); + table + .boolean('public') + .defaultTo(false) + .notNullable() + .comment('Whether the playlist is public'); + }); + + await knex.schema.createTable('entities', table => { + table.comment('The table of playlist entities'); + table + .uuid('playlist_id') + .notNullable() + .references('playlists.id') + .onDelete('CASCADE') + .comment('The id of the playlist this entity belongs to'); + table.string('entity_ref').notNullable().comment('A entity ref'); + table.unique(['playlist_id', 'entity_ref'], { + indexName: 'playlist_entity_composite_index', + }); + }); + + await knex.schema.createTable('followers', table => { + table.comment('The table of playlist followers'); + table + .uuid('playlist_id') + .notNullable() + .references('playlists.id') + .onDelete('CASCADE') + .comment('The id of the playlist being followed'); + table.string('user_ref').notNullable().comment('A user entity ref'); + table.unique(['playlist_id', 'user_ref'], { + indexName: 'playlist_follower_composite_index', + }); + }); +}; + +exports.down = async function down(knex) { + await knex.schema.dropTable('playlists'); + await knex.schema.dropTable('entities'); + await knex.schema.dropTable('followers'); +}; diff --git a/plugins/playlist-backend/package.json b/plugins/playlist-backend/package.json new file mode 100644 index 0000000000..ae472da767 --- /dev/null +++ b/plugins/playlist-backend/package.json @@ -0,0 +1,54 @@ +{ + "name": "@backstage/plugin-playlist-backend", + "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" + }, + "backstage": { + "role": "backend-plugin" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", + "@backstage/plugin-playlist-common": "workspace:^", + "@types/express": "*", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "knex": "^2.0.0", + "node-fetch": "^2.6.7", + "uuid": "^8.2.0", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@types/supertest": "^2.0.8", + "msw": "^0.47.0", + "supertest": "^6.1.3" + }, + "files": [ + "dist", + "migrations/**/*.{js,d.ts}" + ] +} diff --git a/plugins/playlist-backend/src/index.ts b/plugins/playlist-backend/src/index.ts new file mode 100644 index 0000000000..eeccd8913f --- /dev/null +++ b/plugins/playlist-backend/src/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2022 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. + */ + +/** + * Playlist backend plugin + * + * @packageDocumentation + */ +export * from './service'; +export { + createPlaylistConditionalDecision, + DefaultPlaylistPermissionPolicy, + isPlaylistPermission, + playlistConditions, +} from './permissions'; diff --git a/plugins/playlist-backend/src/permissions/DefaultPlaylistPermissionPolicy.test.ts b/plugins/playlist-backend/src/permissions/DefaultPlaylistPermissionPolicy.test.ts new file mode 100644 index 0000000000..179b0d7f9b --- /dev/null +++ b/plugins/playlist-backend/src/permissions/DefaultPlaylistPermissionPolicy.test.ts @@ -0,0 +1,135 @@ +/* + * Copyright 2022 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 { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; +import { + AuthorizeResult, + createPermission, +} from '@backstage/plugin-permission-common'; +import { + permissions, + PLAYLIST_LIST_RESOURCE_TYPE, +} from '@backstage/plugin-playlist-common'; + +import { playlistConditions } from './conditions'; +import { DefaultPlaylistPermissionPolicy } from './DefaultPlaylistPermissionPolicy'; + +describe('DefaultPlaylistPermissionPolicy', () => { + const policy = new DefaultPlaylistPermissionPolicy(); + const mockUser: BackstageIdentityResponse = { + token: 'token', + identity: { + type: 'user', + ownershipEntityRefs: ['user:default/me', 'group:default/owner'], + userEntityRef: 'user:default/me', + }, + }; + + it('should deny non-playlist permissions', async () => { + const mockPermission = createPermission({ + name: 'test.permission', + attributes: { action: 'read' }, + }); + + expect(await policy.handle({ permission: mockPermission })).toEqual({ + result: AuthorizeResult.DENY, + }); + }); + + it('should allow create permissions', async () => { + expect( + await policy.handle({ permission: permissions.playlistListCreate }), + ).toEqual({ result: AuthorizeResult.ALLOW }); + }); + + it('should return a conditional decision for read permissions', async () => { + expect( + await policy.handle( + { permission: permissions.playlistListRead }, + mockUser, + ), + ).toEqual({ + result: AuthorizeResult.CONDITIONAL, + pluginId: 'playlist', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + conditions: { + anyOf: [ + playlistConditions.isOwner([ + 'user:default/me', + 'group:default/owner', + ]), + playlistConditions.isPublic(), + ], + }, + }); + }); + + it('should return a conditional decision for followers update permissions', async () => { + expect( + await policy.handle( + { permission: permissions.playlistFollowersUpdate }, + mockUser, + ), + ).toEqual({ + result: AuthorizeResult.CONDITIONAL, + pluginId: 'playlist', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + conditions: { + anyOf: [ + playlistConditions.isOwner([ + 'user:default/me', + 'group:default/owner', + ]), + playlistConditions.isPublic(), + ], + }, + }); + }); + + it('should return a conditional decision for update permissions', async () => { + expect( + await policy.handle( + { permission: permissions.playlistListUpdate }, + mockUser, + ), + ).toEqual({ + result: AuthorizeResult.CONDITIONAL, + pluginId: 'playlist', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + conditions: playlistConditions.isOwner([ + 'user:default/me', + 'group:default/owner', + ]), + }); + }); + + it('should return a conditional decision for delete permissions', async () => { + expect( + await policy.handle( + { permission: permissions.playlistListDelete }, + mockUser, + ), + ).toEqual({ + result: AuthorizeResult.CONDITIONAL, + pluginId: 'playlist', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + conditions: playlistConditions.isOwner([ + 'user:default/me', + 'group:default/owner', + ]), + }); + }); +}); diff --git a/plugins/playlist-backend/src/permissions/DefaultPlaylistPermissionPolicy.ts b/plugins/playlist-backend/src/permissions/DefaultPlaylistPermissionPolicy.ts new file mode 100644 index 0000000000..86d103e789 --- /dev/null +++ b/plugins/playlist-backend/src/permissions/DefaultPlaylistPermissionPolicy.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2022 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 { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; +import { + AuthorizeResult, + isPermission, + Permission, + PolicyDecision, +} from '@backstage/plugin-permission-common'; +import { + PermissionPolicy, + PolicyQuery, +} from '@backstage/plugin-permission-node'; +import { permissions } from '@backstage/plugin-playlist-common'; + +import { + createPlaylistConditionalDecision, + playlistConditions, +} from './conditions'; + +/** + * @public + */ +export const isPlaylistPermission = (permission: Permission) => + Object.values(permissions).some(playlistPermission => + isPermission(permission, playlistPermission), + ); + +/** + * Default policy for the Playlist plugin. This should be applied as a "sub-policy" + * of your master permission policy for Playlist permission requests only. + * + * @public + */ +export class DefaultPlaylistPermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + user?: BackstageIdentityResponse, + ): Promise { + // Reject permissions we don't know how to evaluate in case this policy was incorrectly applied + if (!isPlaylistPermission(request.permission)) { + return { result: AuthorizeResult.DENY }; + } + + // Anyone should be allowed to create a new playlist + if (isPermission(request.permission, permissions.playlistListCreate)) { + return { result: AuthorizeResult.ALLOW }; + } + + // Reading and following/unfollowing playlists is allowed if it is public or owned + if ( + isPermission(request.permission, permissions.playlistListRead) || + isPermission(request.permission, permissions.playlistFollowersUpdate) + ) { + return createPlaylistConditionalDecision(request.permission, { + anyOf: [ + playlistConditions.isOwner(user?.identity.ownershipEntityRefs ?? []), + playlistConditions.isPublic(), + ], + }); + } + + // Updating or deleting playlists is only allowed for owners + if ( + isPermission(request.permission, permissions.playlistListUpdate) || + isPermission(request.permission, permissions.playlistListDelete) + ) { + return createPlaylistConditionalDecision( + request.permission, + playlistConditions.isOwner(user?.identity.ownershipEntityRefs ?? []), + ); + } + + return { result: AuthorizeResult.ALLOW }; + } +} diff --git a/plugins/playlist-backend/src/permissions/conditions.ts b/plugins/playlist-backend/src/permissions/conditions.ts new file mode 100644 index 0000000000..8162c1046f --- /dev/null +++ b/plugins/playlist-backend/src/permissions/conditions.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2022 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 { PLAYLIST_LIST_RESOURCE_TYPE } from '@backstage/plugin-playlist-common'; +import { + ConditionTransformer, + createConditionExports, + createConditionTransformer, +} from '@backstage/plugin-permission-node'; + +import { ListPlaylistsFilter } from '../service'; +import { rules } from './rules'; + +const { conditions, createConditionalDecision } = createConditionExports({ + pluginId: 'playlist', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + rules, +}); + +/** + * @public + */ +export const playlistConditions = conditions; + +/** + * @public + */ +export const createPlaylistConditionalDecision = createConditionalDecision; + +export const transformConditions: ConditionTransformer = + createConditionTransformer(Object.values(rules)); diff --git a/plugins/playlist-backend/src/permissions/index.ts b/plugins/playlist-backend/src/permissions/index.ts new file mode 100644 index 0000000000..a6bb633a1e --- /dev/null +++ b/plugins/playlist-backend/src/permissions/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 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 './conditions'; +export * from './rules'; +export * from './DefaultPlaylistPermissionPolicy'; diff --git a/plugins/playlist-backend/src/permissions/rules.ts b/plugins/playlist-backend/src/permissions/rules.ts new file mode 100644 index 0000000000..cbf81bee65 --- /dev/null +++ b/plugins/playlist-backend/src/permissions/rules.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2022 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 { makeCreatePermissionRule } from '@backstage/plugin-permission-node'; +import { + PLAYLIST_LIST_RESOURCE_TYPE, + PlaylistMetadata, +} from '@backstage/plugin-playlist-common'; + +import { ListPlaylistsFilter } from '../service'; + +const createPlaylistPermissionRule = makeCreatePermissionRule< + PlaylistMetadata, + ListPlaylistsFilter, + typeof PLAYLIST_LIST_RESOURCE_TYPE +>(); + +const isOwner = createPlaylistPermissionRule({ + name: 'IS_OWNER', + description: 'Should allow only if the playlist belongs to the user', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + apply: (list: PlaylistMetadata, userOwnershipRefs: string[]) => + userOwnershipRefs.includes(list.owner), + toQuery: (userOwnershipRefs: string[]) => ({ + key: 'owner', + values: userOwnershipRefs, + }), +}); + +const isPublic = createPlaylistPermissionRule({ + name: 'IS_PUBLIC', + description: 'Should allow only if the playlist is public', + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + apply: (list: PlaylistMetadata) => list.public, + toQuery: () => ({ key: 'public', values: [true] }), +}); + +/** + * @public + */ +export const rules = { isOwner, isPublic }; diff --git a/plugins/playlist-backend/src/run.ts b/plugins/playlist-backend/src/run.ts new file mode 100644 index 0000000000..d945aa13f0 --- /dev/null +++ b/plugins/playlist-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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 { 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) : 7007; +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/playlist-backend/src/service/DatabaseHandler.test.ts b/plugins/playlist-backend/src/service/DatabaseHandler.test.ts new file mode 100644 index 0000000000..5d6b4aa0ca --- /dev/null +++ b/plugins/playlist-backend/src/service/DatabaseHandler.test.ts @@ -0,0 +1,349 @@ +/* + * Copyright 2022 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 { DatabaseHandler } from './DatabaseHandler'; +import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; +import { BackstageUserIdentity } from '@backstage/plugin-auth-node'; +import { Knex } from 'knex'; +import { v4 as uuid } from 'uuid'; + +describe('DatabaseHandler', () => { + const databases = TestDatabases.create(); + + async function createDatabaseHandler(databaseId: TestDatabaseId) { + const knex = await databases.init(databaseId); + return { + knex, + dbHandler: await DatabaseHandler.create({ database: knex }), + }; + } + + describe.each(databases.eachSupportedId())( + '%p', + databaseId => { + let knex: Knex; + let dbHandler: DatabaseHandler; + const playlist1Id = uuid(); + const playlist2Id = uuid(); + const playlist3Id = uuid(); + const user: BackstageUserIdentity = { + type: 'user', + userEntityRef: 'user:default/foo', + ownershipEntityRefs: ['user:default/foo', 'group:default/foo-group'], + }; + + beforeEach(async () => { + ({ knex, dbHandler } = await createDatabaseHandler(databaseId)); + await knex('playlists').insert([ + { + id: playlist1Id, + name: 'test', + description: 'test description', + owner: 'user:default/foo', + public: true, + }, + { + id: playlist2Id, + name: 'test 2', + description: 'test description 2', + owner: 'group:default/foo-group', + public: false, + }, + { + id: playlist3Id, + name: 'test 3', + description: 'test description 3', + owner: 'user:default/bar', + public: false, + }, + ]); + + await knex('entities').insert([ + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent0', + }, + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent1', + }, + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent2', + }, + { + playlist_id: playlist2Id, + entity_ref: 'component:default/ent1', + }, + ]); + + await knex('followers').insert([ + { + playlist_id: playlist2Id, + user_ref: 'user:default/some-user', + }, + { + playlist_id: playlist2Id, + user_ref: 'user:default/bar', + }, + { + playlist_id: playlist2Id, + user_ref: 'user:default/foo', + }, + { + playlist_id: playlist3Id, + user_ref: 'user:default/foo', + }, + ]); + }, 30000); + + it('listPlaylists', async () => { + const allPlaylists = [ + { + id: playlist1Id, + name: 'test', + description: 'test description', + owner: 'user:default/foo', + public: true, + entities: 3, + followers: 0, + isFollowing: false, + }, + { + id: playlist2Id, + name: 'test 2', + description: 'test description 2', + owner: 'group:default/foo-group', + public: false, + entities: 1, + followers: 3, + isFollowing: true, + }, + { + id: playlist3Id, + name: 'test 3', + description: 'test description 3', + owner: 'user:default/bar', + public: false, + entities: 0, + followers: 1, + isFollowing: true, + }, + ]; + + const playlists = await dbHandler.listPlaylists(user); + expect(playlists.length).toEqual(3); + expect(playlists).toEqual(expect.arrayContaining(allPlaylists)); + + const followedPlaylists = await dbHandler.listPlaylists(user, { + key: 'public', + values: [true], + }); + expect(followedPlaylists).toEqual([allPlaylists[0]]); + + const ownedPlaylists = await dbHandler.listPlaylists(user, { + key: 'owner', + values: user.ownershipEntityRefs, + }); + expect(ownedPlaylists.length).toEqual(2); + expect(ownedPlaylists).toEqual( + expect.arrayContaining([allPlaylists[0], allPlaylists[1]]), + ); + }); + + it('createPlaylist', async () => { + const newList = { + name: 'new list', + description: 'new description', + owner: 'user:default/new', + public: true, + }; + + const newPlaylistId = await dbHandler.createPlaylist(newList); + + const newPlaylist = await knex('playlists').where('id', newPlaylistId); + expect( + newPlaylist.map(list => ({ ...list, public: Boolean(list.public) })), + ).toEqual([ + { + ...newList, + id: newPlaylistId, + }, + ]); + }); + + it('getPlaylist', async () => { + const playlist = await dbHandler.getPlaylist(playlist1Id, user); + expect(playlist).toEqual({ + id: playlist1Id, + name: 'test', + description: 'test description', + owner: 'user:default/foo', + public: true, + entities: 3, + followers: 0, + isFollowing: false, + }); + }); + + it('updatePlaylist', async () => { + const update = { + id: playlist1Id, + name: 'test rename', + description: 'test new description', + owner: 'user:default/new-foo', + public: false, + }; + await dbHandler.updatePlaylist(update); + + const updatedPlaylist = await knex('playlists').where( + 'id', + playlist1Id, + ); + expect( + updatedPlaylist.map(list => ({ + ...list, + public: Boolean(list.public), + })), + ).toEqual([update]); + }); + + it('deletePlaylist', async () => { + await dbHandler.deletePlaylist(playlist1Id); + const deleted = await knex('playlists').where('id', playlist1Id); + expect(deleted.length).toEqual(0); + }); + + it('addPlaylistEntities', async () => { + await dbHandler.addPlaylistEntities(playlist1Id, [ + 'component:default/ent1', + 'component:default/ent3', + 'component:default/ent4', + ]); + + const entities = await knex('entities').where( + 'playlist_id', + playlist1Id, + ); + expect(entities.length).toEqual(5); + expect(entities).toEqual( + expect.arrayContaining([ + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent0', + }, + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent1', + }, + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent2', + }, + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent3', + }, + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent4', + }, + ]), + ); + }); + + it('getPlaylistEntities', async () => { + const entities = await dbHandler.getPlaylistEntities(playlist1Id); + expect(entities.length).toEqual(3); + expect(entities).toEqual( + expect.arrayContaining([ + 'component:default/ent0', + 'component:default/ent1', + 'component:default/ent2', + ]), + ); + }); + + it('removePlaylistEntities', async () => { + await dbHandler.removePlaylistEntities(playlist1Id, [ + 'component:default/ent1', + 'component:default/ent2', + ]); + + const entities = await knex('entities').where( + 'playlist_id', + playlist1Id, + ); + expect(entities).toEqual([ + { + playlist_id: playlist1Id, + entity_ref: 'component:default/ent0', + }, + ]); + }); + + it('followPlaylist', async () => { + await dbHandler.followPlaylist(playlist1Id, user); + + const playlist1Followers = await knex('followers').where( + 'playlist_id', + playlist1Id, + ); + expect(playlist1Followers).toEqual([ + { + playlist_id: playlist1Id, + user_ref: 'user:default/foo', + }, + ]); + + await dbHandler.followPlaylist(playlist3Id, user); + + const playlist3Followers = await knex('followers').where( + 'playlist_id', + playlist3Id, + ); + expect(playlist3Followers).toEqual([ + { + playlist_id: playlist3Id, + user_ref: 'user:default/foo', + }, + ]); + }); + + it('unfollowPlaylist', async () => { + await dbHandler.unfollowPlaylist(playlist2Id, user); + + const followers = await knex('followers').where( + 'playlist_id', + playlist2Id, + ); + expect(followers).toEqual( + expect.arrayContaining([ + { + playlist_id: playlist2Id, + user_ref: 'user:default/some-user', + }, + { + playlist_id: playlist2Id, + user_ref: 'user:default/bar', + }, + ]), + ); + }); + }, + 60000, + ); +}); diff --git a/plugins/playlist-backend/src/service/DatabaseHandler.ts b/plugins/playlist-backend/src/service/DatabaseHandler.ts new file mode 100644 index 0000000000..6118f22ff9 --- /dev/null +++ b/plugins/playlist-backend/src/service/DatabaseHandler.ts @@ -0,0 +1,278 @@ +/* + * Copyright 2022 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 { resolvePackagePath } from '@backstage/backend-common'; +import { BackstageUserIdentity } from '@backstage/plugin-auth-node'; +import { Playlist, PlaylistMetadata } from '@backstage/plugin-playlist-common'; +import { Knex } from 'knex'; +import { v4 as uuid } from 'uuid'; + +import { + ListPlaylistsFilter, + ListPlaylistsMatchFilter, +} from './ListPlaylistsFilter'; + +const migrationsDir = resolvePackagePath( + '@backstage/plugin-playlist-backend', + 'migrations', +); + +function isMatchFilter( + filter: ListPlaylistsMatchFilter | ListPlaylistsFilter, +): filter is ListPlaylistsMatchFilter { + return filter.hasOwnProperty('key'); +} + +function isOrFilter( + filter: { anyOf: ListPlaylistsFilter[] } | ListPlaylistsFilter, +): filter is { anyOf: ListPlaylistsFilter[] } { + return filter.hasOwnProperty('anyOf'); +} + +function isNegationFilter( + filter: { not: ListPlaylistsFilter } | ListPlaylistsFilter, +): filter is { not: ListPlaylistsFilter } { + return filter.hasOwnProperty('not'); +} + +function parseFilter( + filter: ListPlaylistsFilter, + query: Knex.QueryBuilder, + db: Knex, + negate: boolean = false, +): Knex.QueryBuilder { + if (isMatchFilter(filter)) { + return query.andWhere(function filterFunction() { + if (filter.values.length === 1) { + this.where(filter.key, negate ? '!=' : '=', filter.values[0]); + } else { + this.andWhere(filter.key, negate ? 'not in' : 'in', filter.values); + } + }); + } + + if (isNegationFilter(filter)) { + return parseFilter(filter.not, query, db, !negate); + } + + return query[negate ? 'andWhereNot' : 'andWhere'](function filterFunction() { + if (isOrFilter(filter)) { + for (const subFilter of filter.anyOf ?? []) { + this.orWhere(subQuery => parseFilter(subFilter, subQuery, db)); + } + } else { + for (const subFilter of filter.allOf ?? []) { + this.andWhere(subQuery => parseFilter(subFilter, subQuery, db)); + } + } + }); +} + +type Options = { + database: Knex; +}; + +/** + * @public + */ +export class DatabaseHandler { + static async create(options: Options): Promise { + const { database } = options; + + await database.migrate.latest({ + directory: migrationsDir, + }); + + return new DatabaseHandler(options); + } + + private readonly database: Knex; + + private constructor(options: Options) { + this.database = options.database; + } + + private playlistColumns = ['id', 'name', 'description', 'owner', 'public']; + + async listPlaylists( + user: BackstageUserIdentity, + filter?: ListPlaylistsFilter, + ): Promise { + let playlistQuery = this.database>( + 'playlists', + ) + .select( + ...this.playlistColumns, + this.database.raw('COALESCE(entities, 0) AS entities'), + this.database.raw('COALESCE(followers, 0) AS followers'), + ) + .leftOuterJoin( + this.database('entities') + .as('e') + .select('playlist_id') + .count('entity_ref', { as: 'entities' }) + .groupBy('playlist_id'), + 'playlists.id', + 'e.playlist_id', + ) + .leftOuterJoin( + this.database('followers') + .as('f') + .select('playlist_id') + .count('user_ref', { as: 'followers' }) + .groupBy('playlist_id'), + 'playlists.id', + 'f.playlist_id', + ); + + if (filter) { + playlistQuery = parseFilter(filter, playlistQuery, this.database); + } + + const playlists = await playlistQuery; + + const followedPlaylists = ( + await this.database('followers') + .select('playlist_id') + .where('user_ref', user.userEntityRef) + ).map(follows => follows.playlist_id); + + return playlists.map(list => ({ + ...list, + entities: Number(list.entities), + followers: Number(list.followers), + public: Boolean(list.public), + isFollowing: followedPlaylists.includes(list.id), + })); + } + + async createPlaylist( + playlist: Omit, + ): Promise { + const id = uuid(); + const newPlaylist = await this.database('playlists').insert( + { + ...playlist, + id, + }, + ['id'], + ); + + // MySQL does not support returning from inserts so return generated uuid if undefined + return newPlaylist[0].id ?? id; + } + + async getPlaylist( + id: string, + user?: BackstageUserIdentity, + ): Promise { + const playlist = await this.database>( + 'playlists', + ) + .select( + ...this.playlistColumns, + this.database.raw('COALESCE(entities, 0) AS entities'), + this.database.raw('COALESCE(followers, 0) AS followers'), + ) + .where('id', id) + .leftOuterJoin( + this.database('entities') + .as('e') + .select('playlist_id') + .count('entity_ref', { as: 'entities' }) + .groupBy('playlist_id'), + 'playlists.id', + 'e.playlist_id', + ) + .leftOuterJoin( + this.database('followers') + .as('f') + .select('playlist_id') + .count('user_ref', { as: 'followers' }) + .groupBy('playlist_id'), + 'playlists.id', + 'f.playlist_id', + ); + + if (!playlist.length) { + return undefined; + } + + const followedPlaylists = user + ? ( + await this.database('followers') + .select('playlist_id') + .where('user_ref', user.userEntityRef) + ).map(follows => follows.playlist_id) + : []; + + return { + ...playlist[0], + entities: Number(playlist[0].entities), + followers: Number(playlist[0].followers), + public: Boolean(playlist[0].public), + isFollowing: followedPlaylists.includes(playlist[0].id), + }; + } + + async updatePlaylist(playlist: PlaylistMetadata) { + await this.database('playlists').where('id', playlist.id).update(playlist); + } + + async deletePlaylist(id: string) { + await this.database('playlists').where('id', id).del(); + } + + async addPlaylistEntities(playlistId: string, entityRefs: string[]) { + await this.database('entities') + .insert( + entityRefs.map(ref => ({ playlist_id: playlistId, entity_ref: ref })), + ) + .onConflict(['playlist_id', 'entity_ref']) + .ignore(); + } + + async getPlaylistEntities(playlistId: string): Promise { + return ( + await this.database('entities') + .select('entity_ref') + .where('playlist_id', playlistId) + ).map(entity => entity.entity_ref); + } + + async removePlaylistEntities(playlistId: string, entityRefs: string[]) { + await this.database('entities') + .where(builder => + entityRefs.forEach(ref => + builder.orWhere({ playlist_id: playlistId, entity_ref: ref }), + ), + ) + .del(); + } + + async followPlaylist(playlistId: string, user: BackstageUserIdentity) { + await this.database('followers') + .insert({ playlist_id: playlistId, user_ref: user.userEntityRef }) + .onConflict(['playlist_id', 'user_ref']) + .ignore(); + } + + async unfollowPlaylist(playlistId: string, user: BackstageUserIdentity) { + await this.database('followers') + .where({ playlist_id: playlistId, user_ref: user.userEntityRef }) + .del(); + } +} diff --git a/plugins/playlist-backend/src/service/ListPlaylistsFilter.test.ts b/plugins/playlist-backend/src/service/ListPlaylistsFilter.test.ts new file mode 100644 index 0000000000..64405fa6d0 --- /dev/null +++ b/plugins/playlist-backend/src/service/ListPlaylistsFilter.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright 2022 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 { + parseListPlaylistsFilterParams, + parseListPlaylistsFilterString, +} from './ListPlaylistsFilter'; + +describe('parseListPlaylistsFilterParams', () => { + it('translates empty query to empty list', () => { + const result = parseListPlaylistsFilterParams({}); + expect(result).toEqual(undefined); + }); + + it('supports single-string format', () => { + const result = parseListPlaylistsFilterParams({ filter: 'a=1' })!; + expect(result).toEqual({ + anyOf: [{ allOf: [{ key: 'a', values: ['1'] }] }], + }); + }); + + it('supports array-of-strings format', () => { + const result = parseListPlaylistsFilterParams({ + filter: ['a=1', 'b=2'], + }); + expect(result).toEqual({ + anyOf: [ + { allOf: [{ key: 'a', values: ['1'] }] }, + { allOf: [{ key: 'b', values: ['2'] }] }, + ], + }); + }); + + it('merges values within each filter', () => { + const result = parseListPlaylistsFilterParams({ + filter: ['a=1', 'b=2,b=3,c=4'], + }); + expect(result).toEqual({ + anyOf: [ + { allOf: [{ key: 'a', values: ['1'] }] }, + { + allOf: [ + { key: 'b', values: ['2', '3'] }, + { key: 'c', values: ['4'] }, + ], + }, + ], + }); + }); + + it('throws for non-strings', () => { + expect(() => parseListPlaylistsFilterParams({ filter: [3] })).toThrow( + 'Invalid filter', + ); + }); +}); + +describe('parseListPlaylistsFilterString', () => { + it('works for the happy path', () => { + expect(parseListPlaylistsFilterString('')).toBeUndefined(); + expect(parseListPlaylistsFilterString('a=1,b=2,a=3')).toEqual([ + { key: 'a', values: ['1', '3'] }, + { key: 'b', values: ['2'] }, + ]); + }); + + it('trims values', () => { + expect(parseListPlaylistsFilterString(' a = 1 , b = 2 , a = 3 ')).toEqual([ + { key: 'a', values: ['1', '3'] }, + { key: 'b', values: ['2'] }, + ]); + }); + + it('rejects malformed strings', () => { + expect(() => parseListPlaylistsFilterString('x=2,=a')).toThrow( + "Invalid filter, '=a' is not a valid statement (expected a string of the form a=b)", + ); + expect(() => parseListPlaylistsFilterString('x=2,a=')).toThrow( + "Invalid filter, 'a=' is not a valid statement (expected a string of the form a=b)", + ); + }); +}); diff --git a/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts b/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts new file mode 100644 index 0000000000..a746286136 --- /dev/null +++ b/plugins/playlist-backend/src/service/ListPlaylistsFilter.ts @@ -0,0 +1,100 @@ +/* + * Copyright 2022 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'; + +// TODO(kuangp): this filter shape was basically plagiarized from catalog-backend, +// it should probably be abstracted into a common plugin module to allow others to reuse the same pattern + +/** + * @public + */ +export type ListPlaylistsMatchFilter = { + key: string; + values: any[]; +}; + +/** + * @public + */ +export type ListPlaylistsFilter = + | { allOf: ListPlaylistsFilter[] } + | { anyOf: ListPlaylistsFilter[] } + | { not: ListPlaylistsFilter } + | ListPlaylistsMatchFilter; + +export function parseListPlaylistsFilterParams( + params: Record, +): ListPlaylistsFilter | undefined { + if (!params.filter) { + return undefined; + } + + // Each filter string is on the form a=b,c=d + const filterStrings = [params.filter].flat(); + if (filterStrings.some(p => typeof p !== 'string')) { + throw new InputError('Invalid filter'); + } + + // Outer array: "any of the inner ones" + // Inner arrays: "all of these must match" + const filters = (filterStrings as string[]) + .map(parseListPlaylistsFilterString) + .filter(Boolean); + if (!filters.length) { + return undefined; + } + + return { anyOf: filters.map(f => ({ allOf: f! })) }; +} + +export function parseListPlaylistsFilterString( + filterString: string, +): ListPlaylistsMatchFilter[] | undefined { + const statements = filterString + .split(',') + .map(s => s.trim()) + .filter(Boolean); + + if (!statements.length) { + return undefined; + } + + const filtersByKey = new Map(); + + for (const statement of statements) { + const equalsIndex = statement.indexOf('='); + + const key = + equalsIndex === -1 ? statement : statement.substr(0, equalsIndex).trim(); + const value = + equalsIndex === -1 ? undefined : statement.substr(equalsIndex + 1).trim(); + + if (!key || !value) { + throw new InputError( + `Invalid filter, '${statement}' is not a valid statement (expected a string of the form a=b)`, + ); + } + + const f = filtersByKey.has(key) + ? filtersByKey.get(key) + : filtersByKey.set(key, { key, values: [] }).get(key); + + f!.values.push(value); + } + + return [...filtersByKey.values()]; +} diff --git a/plugins/playlist-backend/src/service/index.ts b/plugins/playlist-backend/src/service/index.ts new file mode 100644 index 0000000000..3a1499f495 --- /dev/null +++ b/plugins/playlist-backend/src/service/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2022 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 './router'; +export type { + ListPlaylistsFilter, + ListPlaylistsMatchFilter, +} from './ListPlaylistsFilter'; diff --git a/plugins/playlist-backend/src/service/router.test.ts b/plugins/playlist-backend/src/service/router.test.ts new file mode 100644 index 0000000000..3f90a8b8af --- /dev/null +++ b/plugins/playlist-backend/src/service/router.test.ts @@ -0,0 +1,570 @@ +/* + * Copyright 2022 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 { + DatabaseManager, + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { IdentityApi } from '@backstage/plugin-auth-node'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { permissions } from '@backstage/plugin-playlist-common'; +import express from 'express'; +import request from 'supertest'; + +import { createRouter } from './router'; + +const sampleEntities = [ + { + kind: 'system', + metadata: { + namespace: 'default', + name: 'test-ent-system', + title: 'Test Ent', + description: 'test ent description', + }, + }, + { + kind: 'component', + metadata: { + namespace: 'default', + name: 'test-ent', + title: 'Test Ent 2', + description: 'test ent description 2', + }, + spec: { + type: 'library', + }, + }, +]; +const mockGetEntties = jest + .fn() + .mockImplementation(async () => ({ items: sampleEntities })); +jest.mock('@backstage/catalog-client', () => ({ + CatalogClient: jest + .fn() + .mockImplementation(() => ({ getEntities: mockGetEntties })), +})); + +jest.mock('@backstage/plugin-auth-node', () => ({ + getBearerTokenFromAuthorizationHeader: () => 'token', +})); + +const mockConditionFilter = { key: 'test', values: ['test-val'] }; +jest.mock('../permissions', () => ({ + ...jest.requireActual('../permissions'), + transformConditions: () => mockConditionFilter, +})); + +const mockPlaylist = { + id: 'playlist-id', + name: 'test-playlist', + owner: 'group:default/owner', + public: true, + entities: 2, + followers: 4, + isFollowing: false, +}; + +const mockEntities = [ + 'component:default/test-ent', + 'system:default/test-ent-system', +]; + +const mockDbHandler = { + listPlaylists: jest.fn().mockImplementation(async () => [mockPlaylist]), + createPlaylist: jest.fn().mockImplementation(async () => 'playlist-id'), + getPlaylist: jest.fn().mockImplementation(async () => mockPlaylist), + updatePlaylist: jest.fn().mockImplementation(async () => {}), + deletePlaylist: jest.fn().mockImplementation(async () => {}), + addPlaylistEntities: jest.fn().mockImplementation(async () => {}), + getPlaylistEntities: jest.fn().mockImplementation(async () => mockEntities), + removePlaylistEntities: jest.fn().mockImplementation(async () => {}), + followPlaylist: jest.fn().mockImplementation(async () => {}), + unfollowPlaylist: jest.fn().mockImplementation(async () => {}), +}; + +jest.mock('./DatabaseHandler', () => ({ + DatabaseHandler: { create: async () => mockDbHandler }, +})); + +describe('createRouter', () => { + let app: express.Express; + + const createDatabase = () => + DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'better-sqlite3', + connection: ':memory:', + }, + }, + }), + ).forPlugin('playlist'); + + const mockedAuthorize = jest + .fn() + .mockImplementation(async () => [{ result: AuthorizeResult.ALLOW }]); + const mockedAuthorizeConditional = jest + .fn() + .mockImplementation(async () => [{ result: AuthorizeResult.ALLOW }]); + const mockPermissionEvaluator = { + authorize: mockedAuthorize, + authorizeConditional: mockedAuthorizeConditional, + }; + + const mockUser = { + type: 'user', + ownershipEntityRefs: ['user:default/me', 'group:default/owner'], + userEntityRef: 'user:default/me', + }; + const mockIdentityClient = { + getIdentity: jest + .fn() + .mockImplementation(async () => ({ identity: mockUser })), + } as unknown as IdentityApi; + + const discovery: jest.Mocked = { + getBaseUrl: jest.fn(), + getExternalBaseUrl: jest.fn(), + }; + + beforeEach(async () => { + const router = await createRouter({ + database: createDatabase(), + discovery, + identity: mockIdentityClient, + logger: getVoidLogger(), + permissions: mockPermissionEvaluator, + }); + + app = express().use(router); + jest.clearAllMocks(); + }); + + describe('GET /', () => { + const mockRequestFilter = { + anyOf: [ + { allOf: [{ key: 'mock', values: ['test'] }] }, + { allOf: [{ key: 'foo', values: ['bar'] }] }, + ], + }; + + it('should respond correctly if unauthorized', async () => { + mockedAuthorizeConditional.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app).get('/').send(); + + expect(mockedAuthorizeConditional).toHaveBeenCalledWith( + [{ permission: permissions.playlistListRead }], + { token: 'token' }, + ); + expect(mockDbHandler.listPlaylists).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should get playlists correctly', async () => { + let response = await request(app).get('/').send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith( + mockUser, + undefined, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + + mockedAuthorizeConditional.mockImplementationOnce(async () => [ + { result: AuthorizeResult.CONDITIONAL }, + ]); + response = await request(app).get('/').send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith( + mockUser, + mockConditionFilter, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + }); + + it('should get filtered playlists correctly', async () => { + let response = await request(app) + .get('/?filter=mock=test&filter=foo=bar') + .send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith( + mockUser, + mockRequestFilter, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + + mockedAuthorizeConditional.mockImplementationOnce(async () => [ + { result: AuthorizeResult.CONDITIONAL }, + ]); + response = await request(app) + .get('/?filter=mock=test&filter=foo=bar') + .send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith(mockUser, { + allOf: [mockRequestFilter, mockConditionFilter], + }); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + }); + + it('should get editable playlists correctly', async () => { + let response = await request(app).get('/?editable=true').send(); + expect(mockedAuthorizeConditional).toHaveBeenCalledWith( + [{ permission: permissions.playlistListUpdate }], + { token: 'token' }, + ); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith( + mockUser, + undefined, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + + response = await request(app) + .get('/?editable=true&filter=mock=test&filter=foo=bar') + .send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith( + mockUser, + mockRequestFilter, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + + mockedAuthorizeConditional.mockImplementation(async () => [ + { result: AuthorizeResult.CONDITIONAL }, + ]); + response = await request(app).get('/?editable=true').send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith(mockUser, { + allOf: [mockConditionFilter, mockConditionFilter], + }); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + + mockedAuthorizeConditional.mockImplementation(async () => [ + { result: AuthorizeResult.CONDITIONAL }, + ]); + response = await request(app) + .get('/?editable=true&filter=mock=test&filter=foo=bar') + .send(); + expect(mockDbHandler.listPlaylists).toHaveBeenLastCalledWith(mockUser, { + allOf: [ + { allOf: [mockRequestFilter, mockConditionFilter] }, + mockConditionFilter, + ], + }); + expect(response.status).toEqual(200); + expect(response.body).toEqual([mockPlaylist]); + }); + }); + + describe('POST /', () => { + const body = { name: 'new-playlist' }; + + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app).post('/').send(body); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [{ permission: permissions.playlistListCreate }], + { token: 'token' }, + ); + expect(mockDbHandler.createPlaylist).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should create a playlist correctly', async () => { + const response = await request(app).post('/').send(body); + expect(mockDbHandler.createPlaylist).toHaveBeenCalledWith(body); + expect(response.status).toEqual(201); + expect(response.body).toEqual('playlist-id'); + }); + }); + + describe('GET /:playlistId', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app).get('/playlist-id').send(); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistListRead, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.getPlaylist).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should get a playlist correctly', async () => { + const response = await request(app).get('/playlist-id').send(); + expect(mockDbHandler.getPlaylist).toHaveBeenCalledWith( + 'playlist-id', + mockUser, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual(mockPlaylist); + }); + }); + + describe('PUT /:playlistId', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app) + .put('/playlist-id') + .send(mockPlaylist); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistListUpdate, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.updatePlaylist).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should update a playlist correctly', async () => { + const response = await request(app) + .put('/playlist-id') + .send(mockPlaylist); + expect(mockDbHandler.updatePlaylist).toHaveBeenCalledWith(mockPlaylist); + expect(response.status).toEqual(200); + }); + }); + + describe('DELETE /:playlistId', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app).delete('/playlist-id').send(); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistListDelete, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.deletePlaylist).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should delete a playlist correctly', async () => { + const response = await request(app).delete('/playlist-id').send(); + expect(mockDbHandler.deletePlaylist).toHaveBeenCalledWith('playlist-id'); + expect(response.status).toEqual(200); + }); + }); + + describe('POST /:playlistId/entities', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app) + .post('/playlist-id/entities') + .send(['component:default/test-ent']); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistListUpdate, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.addPlaylistEntities).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should add entities to a playlist correctly', async () => { + const response = await request(app) + .post('/playlist-id/entities') + .send(mockEntities); + expect(mockDbHandler.addPlaylistEntities).toHaveBeenCalledWith( + 'playlist-id', + mockEntities, + ); + expect(response.status).toEqual(200); + }); + }); + + describe('GET /:playlistId/entities', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app).get('/playlist-id/entities').send(); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistListRead, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.getPlaylistEntities).not.toHaveBeenCalled(); + expect(mockGetEntties).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should get entities from a playlist correctly', async () => { + const response = await request(app).get('/playlist-id/entities').send(); + expect(mockDbHandler.getPlaylistEntities).toHaveBeenCalledWith( + 'playlist-id', + ); + expect(mockGetEntties).toHaveBeenCalledWith( + { + filter: [ + { + kind: 'component', + 'metadata.namespace': 'default', + 'metadata.name': 'test-ent', + }, + { + kind: 'system', + 'metadata.namespace': 'default', + 'metadata.name': 'test-ent-system', + }, + ], + }, + { token: 'token' }, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual(sampleEntities); + }); + }); + + describe('DELETE /:playlistId/entities', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app) + .delete('/playlist-id/entities') + .send(mockEntities); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistListUpdate, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.removePlaylistEntities).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should delete entities from a playlist correctly', async () => { + const response = await request(app) + .delete('/playlist-id/entities') + .send(mockEntities); + expect(mockDbHandler.removePlaylistEntities).toHaveBeenCalledWith( + 'playlist-id', + mockEntities, + ); + expect(response.status).toEqual(200); + }); + }); + + describe('POST /:playlistId/followers', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app).post('/playlist-id/followers').send(); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistFollowersUpdate, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.followPlaylist).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should follow a playlist correctly', async () => { + const response = await request(app).post('/playlist-id/followers').send(); + expect(mockDbHandler.followPlaylist).toHaveBeenCalledWith( + 'playlist-id', + mockUser, + ); + expect(response.status).toEqual(200); + }); + }); + + describe('DELETE /:playlistId/followers', () => { + it('should respond correctly if unauthorized', async () => { + mockedAuthorize.mockImplementationOnce(async () => [ + { result: AuthorizeResult.DENY }, + ]); + const response = await request(app) + .delete('/playlist-id/followers') + .send(); + + expect(mockedAuthorize).toHaveBeenCalledWith( + [ + { + permission: permissions.playlistFollowersUpdate, + resourceRef: 'playlist-id', + }, + ], + { token: 'token' }, + ); + expect(mockDbHandler.unfollowPlaylist).not.toHaveBeenCalled(); + expect(response.status).toEqual(403); + }); + + it('should unfollow a playlist correctly', async () => { + const response = await request(app) + .delete('/playlist-id/followers') + .send(); + expect(mockDbHandler.unfollowPlaylist).toHaveBeenCalledWith( + 'playlist-id', + mockUser, + ); + expect(response.status).toEqual(200); + }); + }); +}); diff --git a/plugins/playlist-backend/src/service/router.ts b/plugins/playlist-backend/src/service/router.ts new file mode 100644 index 0000000000..bf0b789b7a --- /dev/null +++ b/plugins/playlist-backend/src/service/router.ts @@ -0,0 +1,273 @@ +/* + * Copyright 2022 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, + PluginDatabaseManager, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { CatalogClient } from '@backstage/catalog-client'; +import { parseEntityRef } from '@backstage/catalog-model'; +import { NotAllowedError } from '@backstage/errors'; +import { + getBearerTokenFromAuthorizationHeader, + IdentityApi, +} from '@backstage/plugin-auth-node'; +import { + AuthorizePermissionRequest, + AuthorizeResult, + PermissionEvaluator, + QueryPermissionRequest, +} from '@backstage/plugin-permission-common'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; +import { + PLAYLIST_LIST_RESOURCE_TYPE, + permissions, +} from '@backstage/plugin-playlist-common'; +import express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; + +import { rules, transformConditions } from '../permissions'; +import { DatabaseHandler } from './DatabaseHandler'; +import { parseListPlaylistsFilterParams } from './ListPlaylistsFilter'; + +/** + * @public + */ +export interface RouterOptions { + database: PluginDatabaseManager; + discovery: PluginEndpointDiscovery; + identity: IdentityApi; + logger: Logger; + permissions: PermissionEvaluator; +} + +/** + * @public + */ +export async function createRouter( + options: RouterOptions, +): Promise { + const { + database, + discovery, + identity, + logger, + permissions: permissionEvaluator, + } = options; + + logger.info('Initializing Playlist backend'); + + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + const db = await database.getClient(); + const dbHandler = await DatabaseHandler.create({ database: db }); + + const evaluateRequestPermission = async ( + request: express.Request, + permission: AuthorizePermissionRequest | QueryPermissionRequest, + conditional: boolean = false, + ) => { + const token = getBearerTokenFromAuthorizationHeader( + request.header('authorization'), + ); + + const user = await identity.getIdentity({ request }); + if (!user) { + throw new NotAllowedError('Unauthorized'); + } + + const decision = conditional + ? ( + await permissionEvaluator.authorizeConditional( + [permission as QueryPermissionRequest], + { token }, + ) + )[0] + : ( + await permissionEvaluator.authorize( + [permission as AuthorizePermissionRequest], + { token }, + ) + )[0]; + + if (decision.result === AuthorizeResult.DENY) { + throw new NotAllowedError('Unauthorized'); + } + + return { decision, user: user.identity }; + }; + + const permissionIntegrationRouter = createPermissionIntegrationRouter({ + getResources: resourceRefs => + Promise.all(resourceRefs.map(ref => dbHandler.getPlaylist(ref))), + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, + rules: Object.values(rules), + }); + + const router = Router(); + router.use(express.json()); + router.use(permissionIntegrationRouter); + + router.get('/', async (req, res) => { + const { decision, user } = await evaluateRequestPermission( + req, + { permission: permissions.playlistListRead }, + true, + ); + + let filter = parseListPlaylistsFilterParams(req.query); + if (decision.result === AuthorizeResult.CONDITIONAL) { + const conditionsFilter = transformConditions(decision.conditions); + filter = filter + ? { allOf: [filter, conditionsFilter] } + : conditionsFilter; + } + + if (req.query.editable) { + const { decision: updatePermissionDecision } = + await evaluateRequestPermission( + req, + { permission: permissions.playlistListUpdate }, + true, + ); + + if (updatePermissionDecision.result === AuthorizeResult.CONDITIONAL) { + const updateConditionsFilter = transformConditions( + updatePermissionDecision.conditions, + ); + filter = filter + ? { allOf: [filter, updateConditionsFilter] } + : updateConditionsFilter; + } + } + + const playlists = await dbHandler.listPlaylists(user, filter); + res.json(playlists); + }); + + router.post('/', async (req, res) => { + await evaluateRequestPermission(req, { + permission: permissions.playlistListCreate, + }); + const playlistId = await dbHandler.createPlaylist(req.body); + res.status(201).json(playlistId); + }); + + router.get('/:playlistId', async (req, res) => { + const { user } = await evaluateRequestPermission(req, { + permission: permissions.playlistListRead, + resourceRef: req.params.playlistId, + }); + const playlist = await dbHandler.getPlaylist(req.params.playlistId, user); + res.json(playlist); + }); + + router.put('/:playlistId', async (req, res) => { + await evaluateRequestPermission(req, { + permission: permissions.playlistListUpdate, + resourceRef: req.params.playlistId, + }); + await dbHandler.updatePlaylist({ ...req.body, id: req.params.playlistId }); + res.status(200).end(); + }); + + router.delete('/:playlistId', async (req, res) => { + await evaluateRequestPermission(req, { + permission: permissions.playlistListDelete, + resourceRef: req.params.playlistId, + }); + await dbHandler.deletePlaylist(req.params.playlistId); + res.status(200).end(); + }); + + router.post('/:playlistId/entities', async (req, res) => { + await evaluateRequestPermission(req, { + permission: permissions.playlistListUpdate, + resourceRef: req.params.playlistId, + }); + await dbHandler.addPlaylistEntities(req.params.playlistId, req.body); + res.status(200).end(); + }); + + router.get('/:playlistId/entities', async (req, res) => { + await evaluateRequestPermission(req, { + permission: permissions.playlistListRead, + resourceRef: req.params.playlistId, + }); + + const entityRefs = await dbHandler.getPlaylistEntities( + req.params.playlistId, + ); + if (!entityRefs.length) { + res.json([]); + return; + } + + const filter = entityRefs.map(ref => { + const compoundRef = parseEntityRef(ref); + return { + kind: compoundRef.kind, + 'metadata.namespace': compoundRef.namespace, + 'metadata.name': compoundRef.name, + }; + }); + + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + + // TODO(kuanpg): entities in this playlist that no longer exist in the catalog will be + // excluded from this response, we need a way to clean up these orphaned refs potentially + // via catalog events (https://github.com/backstage/backstage/issues/8219) + // + // Note: This will also enforce catalog permissions and will only return entities for which the current user has access to + const entities = (await catalogClient.getEntities({ filter }, { token })) + .items; + + res.json(entities); + }); + + router.delete('/:playlistId/entities', async (req, res) => { + await evaluateRequestPermission(req, { + permission: permissions.playlistListUpdate, + resourceRef: req.params.playlistId, + }); + await dbHandler.removePlaylistEntities(req.params.playlistId, req.body); + res.status(200).end(); + }); + + router.post('/:playlistId/followers', async (req, res) => { + const { user } = await evaluateRequestPermission(req, { + permission: permissions.playlistFollowersUpdate, + resourceRef: req.params.playlistId, + }); + await dbHandler.followPlaylist(req.params.playlistId, user); + res.status(200).end(); + }); + + router.delete('/:playlistId/followers', async (req, res) => { + const { user } = await evaluateRequestPermission(req, { + permission: permissions.playlistFollowersUpdate, + resourceRef: req.params.playlistId, + }); + await dbHandler.unfollowPlaylist(req.params.playlistId, user); + res.status(200).end(); + }); + + router.use(errorHandler()); + return router; +} diff --git a/plugins/playlist-backend/src/service/standaloneServer.ts b/plugins/playlist-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..3b6acfd09b --- /dev/null +++ b/plugins/playlist-backend/src/service/standaloneServer.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2022 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 { + createServiceBuilder, + DatabaseManager, + loadBackendConfig, + ServerTokenManager, + SingleHostDiscovery, + useHotMemoize, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; +import { ServerPermissionClient } from '@backstage/plugin-permission-node'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'playlist-backend' }); + const config = await loadBackendConfig({ logger, argv: process.argv }); + const discovery = SingleHostDiscovery.fromConfig(config); + + const database = useHotMemoize(module, () => { + const manager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { client: 'better-sqlite3', connection: ':memory:' }, + }, + }), + ); + return manager.forPlugin('playlist'); + }); + + const identity = DefaultIdentityClient.create({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }); + + const tokenManager = ServerTokenManager.fromConfig(config, { + logger, + }); + const permissions = ServerPermissionClient.fromConfig(config, { + discovery, + tokenManager, + }); + + logger.debug('Starting application server...'); + const router = await createRouter({ + database, + discovery, + identity, + logger, + permissions, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/playlist', 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/playlist-backend/src/setupTests.ts b/plugins/playlist-backend/src/setupTests.ts new file mode 100644 index 0000000000..813cdeaae3 --- /dev/null +++ b/plugins/playlist-backend/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 {}; diff --git a/plugins/playlist-common/.eslintrc.js b/plugins/playlist-common/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/playlist-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/playlist-common/CHANGELOG.md b/plugins/playlist-common/CHANGELOG.md new file mode 100644 index 0000000000..1c7ae8af12 --- /dev/null +++ b/plugins/playlist-common/CHANGELOG.md @@ -0,0 +1,12 @@ +# @backstage/plugin-playlist-common + +## 0.1.0 + +### Minor Changes + +- d3737da337: Implement playlist plugin, check out the `README.md` for more details! + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.4 diff --git a/plugins/playlist-common/README.md b/plugins/playlist-common/README.md new file mode 100644 index 0000000000..72f502386c --- /dev/null +++ b/plugins/playlist-common/README.md @@ -0,0 +1,3 @@ +# Playlist Common + +Common functionalities, types, and permissions for the playlist plugin. diff --git a/plugins/playlist-common/api-report.md b/plugins/playlist-common/api-report.md new file mode 100644 index 0000000000..e97cb3dc64 --- /dev/null +++ b/plugins/playlist-common/api-report.md @@ -0,0 +1,36 @@ +## API Report File for "@backstage/plugin-playlist-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BasicPermission } from '@backstage/plugin-permission-common'; +import { ResourcePermission } from '@backstage/plugin-permission-common'; + +// @public (undocumented) +export const permissions: { + playlistListCreate: BasicPermission; + playlistListRead: ResourcePermission<'playlist-list'>; + playlistListUpdate: ResourcePermission<'playlist-list'>; + playlistListDelete: ResourcePermission<'playlist-list'>; + playlistFollowersUpdate: ResourcePermission<'playlist-list'>; +}; + +// @public (undocumented) +export type Playlist = PlaylistMetadata & { + entities: number; + followers: number; + isFollowing: boolean; +}; + +// @public (undocumented) +export const PLAYLIST_LIST_RESOURCE_TYPE = 'playlist-list'; + +// @public (undocumented) +export type PlaylistMetadata = { + id: string; + name: string; + description?: string; + owner: string; + public: boolean; +}; +``` diff --git a/plugins/playlist-common/package.json b/plugins/playlist-common/package.json new file mode 100644 index 0000000000..87dbc7d9f5 --- /dev/null +++ b/plugins/playlist-common/package.json @@ -0,0 +1,34 @@ +{ + "name": "@backstage/plugin-playlist-common", + "description": "Common functionalities for the playlist plugin", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/plugin-permission-common": "workspace:^" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/playlist-common/src/index.ts b/plugins/playlist-common/src/index.ts new file mode 100644 index 0000000000..92cf1eb461 --- /dev/null +++ b/plugins/playlist-common/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2022 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. + */ + +/** + * Common functionalities for the playlist plugin. + * + * @packageDocumentation + */ + +export * from './types'; +export * from './permissions'; diff --git a/plugins/playlist-common/src/permissions.ts b/plugins/playlist-common/src/permissions.ts new file mode 100644 index 0000000000..89d526127e --- /dev/null +++ b/plugins/playlist-common/src/permissions.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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 { createPermission } from '@backstage/plugin-permission-common'; + +/** + * @public + */ +export const PLAYLIST_LIST_RESOURCE_TYPE = 'playlist-list'; + +const playlistListCreate = createPermission({ + name: 'playlist.list.create', + attributes: { action: 'create' }, +}); + +const playlistListRead = createPermission({ + name: 'playlist.list.read', + attributes: { action: 'read' }, + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, +}); + +const playlistListUpdate = createPermission({ + name: 'playlist.list.update', + attributes: { action: 'update' }, + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, +}); + +const playlistListDelete = createPermission({ + name: 'playlist.list.delete', + attributes: { action: 'delete' }, + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, +}); + +const playlistFollowersUpdate = createPermission({ + name: 'playlist.followers.update', + attributes: { action: 'update' }, + resourceType: PLAYLIST_LIST_RESOURCE_TYPE, +}); + +/** + * @public + */ +export const permissions = { + playlistListCreate, + playlistListRead, + playlistListUpdate, + playlistListDelete, + playlistFollowersUpdate, +}; diff --git a/plugins/playlist-common/src/setupTests.ts b/plugins/playlist-common/src/setupTests.ts new file mode 100644 index 0000000000..8b9b6bd586 --- /dev/null +++ b/plugins/playlist-common/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 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 {}; diff --git a/plugins/playlist-common/src/types.ts b/plugins/playlist-common/src/types.ts new file mode 100644 index 0000000000..667cd35ece --- /dev/null +++ b/plugins/playlist-common/src/types.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2022 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. + */ + +/** + * @public + */ +export type PlaylistMetadata = { + id: string; + name: string; + description?: string; + owner: string; + public: boolean; +}; + +/** + * @public + */ +export type Playlist = PlaylistMetadata & { + entities: number; + followers: number; + isFollowing: boolean; +}; diff --git a/plugins/playlist/.eslintrc.js b/plugins/playlist/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/playlist/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/playlist/CHANGELOG.md b/plugins/playlist/CHANGELOG.md new file mode 100644 index 0000000000..f4be6fc149 --- /dev/null +++ b/plugins/playlist/CHANGELOG.md @@ -0,0 +1,21 @@ +# @backstage/plugin-playlist + +## 0.1.0 + +### Minor Changes + +- d3737da337: Implement playlist plugin, check out the `README.md` for more details! + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/plugin-search-react@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/errors@1.1.1 + - @backstage/plugin-permission-common@0.6.4 + - @backstage/plugin-playlist-common@0.1.0 + - @backstage/plugin-catalog-common@1.0.6 diff --git a/plugins/playlist/README.md b/plugins/playlist/README.md new file mode 100644 index 0000000000..7c814d302b --- /dev/null +++ b/plugins/playlist/README.md @@ -0,0 +1,128 @@ +# Playlist Plugin + +Welcome to the playlist plugin! + +This plugin allows you to create, share, and follow custom collections of entities available in the Backstage catalog. + +## Setup + +Install this plugin: + +```bash +# From your Backstage root directory +yarn --cwd packages/app add @backstage/plugin-playlist +``` + +### Add the plugin to your `packages/app` + +Add the root page that the playlist plugin provides to your app. You can +choose any path for the route, but we recommend the following: + +```diff +// packages/app/src/App.tsx ++import { PlaylistIndexPage } from '@backstage/plugin-playlist'; + + + + } /> + }> + {entityPage} + ++ } /> + ... + +``` + +You may also want to add a link to the playlist page to your application sidebar: + +```diff +// packages/app/src/components/Root/Root.tsx ++import PlaylistPlayIcon from '@material-ui/icons/PlaylistPlay'; + +export const Root = ({ children }: PropsWithChildren<{}>) => ( + + ++ + ... + +``` + +### Entity Pages + +You can also make the following changes to add the playlist context menu to your `EntityPage.tsx` +to be able to add entities to playlists directly from your entity pages: + +First we need to add the following imports: + +```ts +import { EntityPlaylistDialog } from '@backstage/plugin-playlist'; +import PlaylistAddIcon from '@material-ui/icons/PlaylistAdd'; +``` + +Next we'll update the React import that looks like this: + +```ts +import React from 'react'; +``` + +To look like this: + +```ts +import React, { ReactNode, useMemo, useState } from 'react'; +``` + +Then we have to add this chunk of code after all the imports but before any of the other code: + +```ts +const EntityLayoutWrapper = (props: { children?: ReactNode }) => { + const [playlistDialogOpen, setPlaylistDialogOpen] = useState(false); + + const extraMenuItems = useMemo(() => { + return [ + { + title: 'Add to playlist', + Icon: PlaylistAddIcon, + onClick: () => setPlaylistDialogOpen(true), + }, + ]; + }, []); + + return ( + <> + + {props.children} + + setPlaylistDialogOpen(false)} + /> + + ); +}; +``` + +The last step is to wrap all the entity pages in the `EntityLayoutWrapper` like this: + +```diff +const defaultEntityPage = ( ++ + + {overviewContent} + + + + + + + + + ++ +); +``` + +Note: the above only shows an example for the `defaultEntityPage` for a full example of this you can look at [this EntityPage](../../packages/app/src/components/catalog/EntityPage.tsx) + +## Links + +- [playlist-backend](../playlist-backend) provides the backend API for this frontend. diff --git a/plugins/playlist/api-report.md b/plugins/playlist/api-report.md new file mode 100644 index 0000000000..7a732a4a53 --- /dev/null +++ b/plugins/playlist/api-report.md @@ -0,0 +1,106 @@ +## API Report File for "@backstage/plugin-playlist" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +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 { FetchApi } from '@backstage/core-plugin-api'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; +import { RouteRef } from '@backstage/core-plugin-api'; + +// @public (undocumented) +export const EntityPlaylistDialog: ( + props: EntityPlaylistDialogProps, +) => JSX.Element; + +// @public (undocumented) +export type EntityPlaylistDialogProps = { + open: boolean; + onClose: () => void; +}; + +// @public (undocumented) +export interface GetAllPlaylistsRequest { + // (undocumented) + editable?: boolean; + filter?: + | Record[] + | Record; +} + +// @public (undocumented) +export interface PlaylistApi { + // (undocumented) + addPlaylistEntities(playlistId: string, entityRefs: string[]): Promise; + // (undocumented) + createPlaylist(playlist: Omit): Promise; + // (undocumented) + deletePlaylist(playlistId: string): Promise; + // (undocumented) + followPlaylist(playlistId: string): Promise; + // (undocumented) + getAllPlaylists(req: GetAllPlaylistsRequest): Promise; + // (undocumented) + getPlaylist(playlistId: string): Promise; + // (undocumented) + getPlaylistEntities(playlistId: string): Promise; + // (undocumented) + removePlaylistEntities( + playlistId: string, + entityRefs: string[], + ): Promise; + // (undocumented) + unfollowPlaylist(playlistId: string): Promise; + // (undocumented) + updatePlaylist(playlist: PlaylistMetadata): Promise; +} + +// @public (undocumented) +export const playlistApiRef: ApiRef; + +// @public (undocumented) +export class PlaylistClient implements PlaylistApi { + constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }); + // (undocumented) + addPlaylistEntities(playlistId: string, entityRefs: string[]): Promise; + // (undocumented) + createPlaylist(playlist: Omit): Promise; + // (undocumented) + deletePlaylist(playlistId: string): Promise; + // (undocumented) + followPlaylist(playlistId: string): Promise; + // (undocumented) + getAllPlaylists(req?: GetAllPlaylistsRequest): Promise; + // (undocumented) + getPlaylist(playlistId: string): Promise; + // (undocumented) + getPlaylistEntities(playlistId: string): Promise; + // (undocumented) + removePlaylistEntities( + playlistId: string, + entityRefs: string[], + ): Promise; + // (undocumented) + unfollowPlaylist(playlistId: string): Promise; + // (undocumented) + updatePlaylist(playlist: PlaylistMetadata): Promise; +} + +// @public (undocumented) +export const PlaylistIndexPage: () => JSX.Element; + +// @public (undocumented) +export const playlistPlugin: BackstagePlugin< + { + root: RouteRef; + }, + {}, + {} +>; +``` diff --git a/plugins/playlist/dev/index.tsx b/plugins/playlist/dev/index.tsx new file mode 100644 index 0000000000..c9c626ab38 --- /dev/null +++ b/plugins/playlist/dev/index.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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 { createDevApp } from '@backstage/dev-utils'; +import { playlistPlugin, PlaylistIndexPage } from '../src/plugin'; + +createDevApp() + .registerPlugin(playlistPlugin) + .addPage({ + element: , + title: 'Root Page', + path: '/playlist', + }) + .render(); diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json new file mode 100644 index 0000000000..7da8d4ba08 --- /dev/null +++ b/plugins/playlist/package.json @@ -0,0 +1,66 @@ +{ + "name": "@backstage/plugin-playlist", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/plugin-playlist-common": "workspace:^", + "@backstage/plugin-search-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.57", + "lodash": "^4.17.21", + "qs": "^6.9.4", + "react-hook-form": "^7.13.0", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0", + "react-router": "6.0.0-beta.0 || ^6.3.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/react-hooks": "^8.0.0", + "@testing-library/user-event": "^14.0.0", + "@types/node": "*", + "cross-fetch": "^3.1.5", + "msw": "^0.47.0", + "swr": "^1.1.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/playlist/src/api/PlaylistApi.ts b/plugins/playlist/src/api/PlaylistApi.ts new file mode 100644 index 0000000000..9a9da1badb --- /dev/null +++ b/plugins/playlist/src/api/PlaylistApi.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2022 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 { createApiRef } from '@backstage/core-plugin-api'; +import { Playlist, PlaylistMetadata } from '@backstage/plugin-playlist-common'; + +/** + * @public + */ +export const playlistApiRef = createApiRef({ + id: 'plugin.playlist.service', +}); + +/** + * @public + */ +export interface GetAllPlaylistsRequest { + /** + * If multiple filter sets are given as an array, then there is effectively an + * OR between each filter set. + * + * Within one filter set, there is effectively an AND between the various + * keys. + * + * Within one key, if there are more than one value, then there is effectively + * an OR between them. + */ + filter?: + | Record[] + | Record; + + // If true, will filter results that satisfies the playlist.list.update permission + editable?: boolean; +} + +/** + * @public + */ +export interface PlaylistApi { + getAllPlaylists(req: GetAllPlaylistsRequest): Promise; + + createPlaylist(playlist: Omit): Promise; + + getPlaylist(playlistId: string): Promise; + + updatePlaylist(playlist: PlaylistMetadata): Promise; + + deletePlaylist(playlistId: string): Promise; + + addPlaylistEntities(playlistId: string, entityRefs: string[]): Promise; + + getPlaylistEntities(playlistId: string): Promise; + + removePlaylistEntities( + playlistId: string, + entityRefs: string[], + ): Promise; + + followPlaylist(playlistId: string): Promise; + + unfollowPlaylist(playlistId: string): Promise; +} diff --git a/plugins/playlist/src/api/PlaylistClient.test.ts b/plugins/playlist/src/api/PlaylistClient.test.ts new file mode 100644 index 0000000000..20d4a0bcc8 --- /dev/null +++ b/plugins/playlist/src/api/PlaylistClient.test.ts @@ -0,0 +1,294 @@ +/* + * Copyright 2022 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 { MockFetchApi, setupRequestMockHandlers } from '@backstage/test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; + +import { PlaylistClient } from './PlaylistClient'; + +const server = setupServer(); + +describe('PlaylistClient', () => { + setupRequestMockHandlers(server); + const mockBaseUrl = 'http://backstage/api/playlist'; + const discoveryApi = { getBaseUrl: async () => mockBaseUrl }; + const fetchApi = new MockFetchApi(); + + let client: PlaylistClient; + beforeEach(() => { + client = new PlaylistClient({ discoveryApi, fetchApi }); + }); + + describe('getAllPlaylists', () => { + const expectedResp = [ + { + id: 'id', + name: 'name', + description: 'description', + owner: 'owner', + public: true, + entities: 1, + followers: 2, + isFollowing: true, + }, + ]; + + it('should fetch playlists from correct endpoint', async () => { + server.use( + rest.get(`${mockBaseUrl}/`, (_, res, ctx) => + res(ctx.json(expectedResp)), + ), + ); + const response = await client.getAllPlaylists(); + expect(response).toEqual(expectedResp); + }); + + it('should fetch editable playlists correctly', async () => { + expect.assertions(2); + + server.use( + rest.get(`${mockBaseUrl}/`, (req, res, ctx) => { + expect(req.url.search).toBe('?editable=true'); + return res(ctx.json(expectedResp)); + }), + ); + + const response = await client.getAllPlaylists({ editable: true }); + expect(response).toEqual(expectedResp); + }); + + it('builds multiple search filters properly', async () => { + expect.assertions(2); + + server.use( + rest.get(`${mockBaseUrl}/`, (req, res, ctx) => { + expect(req.url.search).toBe( + '?filter=a=1,b=2,b=3,%C3%B6=%3D&filter=a=2&editable=true', + ); + return res(ctx.json(expectedResp)); + }), + ); + + const response = await client.getAllPlaylists({ + editable: true, + filter: [ + { + a: '1', + b: ['2', '3'], + ö: '=', + }, + { + a: '2', + }, + ], + }); + + expect(response).toEqual(expectedResp); + }); + + it('builds single search filter properly', async () => { + expect.assertions(2); + + server.use( + rest.get(`${mockBaseUrl}/`, (req, res, ctx) => { + expect(req.url.search).toBe('?filter=a=1,b=2,b=3,%C3%B6=%3D'); + return res(ctx.json(expectedResp)); + }), + ); + + const response = await client.getAllPlaylists({ + filter: { + a: '1', + b: ['2', '3'], + ö: '=', + }, + }); + + expect(response).toEqual(expectedResp); + }); + }); + + it('createPlaylist', async () => { + expect.assertions(2); + + const newPlaylist = { + name: 'name', + description: 'description', + owner: 'owner', + public: true, + }; + + server.use( + rest.post(`${mockBaseUrl}/`, (req, res, ctx) => { + expect(req.body).toEqual(newPlaylist); + return res(ctx.json('123')); + }), + ); + + const response = await client.createPlaylist(newPlaylist); + + expect(response).toEqual('123'); + }); + + it('getPlaylist', async () => { + const playlist = { + id: '123', + name: 'name', + description: 'description', + owner: 'owner', + public: true, + entities: 1, + followers: 2, + isFollowing: true, + }; + + server.use( + rest.get(`${mockBaseUrl}/123`, (_, res, ctx) => res(ctx.json(playlist))), + ); + + const response = await client.getPlaylist('123'); + expect(response).toEqual(playlist); + }); + + it('updatePlaylist', async () => { + expect.assertions(1); + + const playlist = { + id: 'id', + name: 'name', + description: 'description', + owner: 'owner', + public: true, + }; + + server.use( + rest.put(`${mockBaseUrl}/id`, (req, res) => { + expect(req.body).toEqual(playlist); + return res(); + }), + ); + + await client.updatePlaylist(playlist); + }); + + it('deletePlaylist', async () => { + expect.assertions(1); + + server.use( + rest.delete(`${mockBaseUrl}/id`, (_, res) => { + // eslint requires at least 1 assertion so this is here is verify this handler is called + expect(true).toBe(true); + return res(); + }), + ); + + await client.deletePlaylist('id'); + }); + + it('addPlaylistEntities', async () => { + expect.assertions(1); + + const entities = ['component:default/ent1', 'component:default/ent2']; + + server.use( + rest.post(`${mockBaseUrl}/id/entities`, (req, res) => { + expect(req.body).toEqual(entities); + return res(); + }), + ); + + await client.addPlaylistEntities('id', entities); + }); + + it('getPlaylistEntities', async () => { + const entities = [ + { + kind: 'system', + metadata: { + namespace: 'default', + name: 'test-ent', + title: 'Test Ent', + description: 'test ent description', + }, + }, + { + kind: 'component', + metadata: { + namespace: 'foo', + name: 'test-ent2', + title: 'Test Ent 2', + description: 'test ent description 2', + }, + spec: { + type: 'library', + }, + }, + ]; + + server.use( + rest.get(`${mockBaseUrl}/id/entities`, (_, res, ctx) => + res(ctx.json(entities)), + ), + ); + + const response = await client.getPlaylistEntities('id'); + expect(response).toEqual(entities); + }); + + it('removePlaylistEntities', async () => { + expect.assertions(1); + + const entities = ['component:default/ent1', 'component:default/ent2']; + + server.use( + rest.delete(`${mockBaseUrl}/id/entities`, (req, res) => { + expect(req.body).toEqual(entities); + return res(); + }), + ); + + await client.removePlaylistEntities('id', entities); + }); + + it('followPlaylist', async () => { + expect.assertions(1); + + server.use( + rest.post(`${mockBaseUrl}/id/followers`, (_, res) => { + // eslint requires at least 1 assertion so this is here is verify this handler is called + expect(true).toBe(true); + return res(); + }), + ); + + await client.followPlaylist('id'); + }); + + it('unfollowPlaylist', async () => { + expect.assertions(1); + + server.use( + rest.delete(`${mockBaseUrl}/id/followers`, (_, res) => { + // eslint requires at least 1 assertion so this is here is verify this handler is called + expect(true).toBe(true); + return res(); + }), + ); + + await client.unfollowPlaylist('id'); + }); +}); diff --git a/plugins/playlist/src/api/PlaylistClient.ts b/plugins/playlist/src/api/PlaylistClient.ts new file mode 100644 index 0000000000..425b185de8 --- /dev/null +++ b/plugins/playlist/src/api/PlaylistClient.ts @@ -0,0 +1,199 @@ +/* + * Copyright 2022 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 { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; +import { Playlist, PlaylistMetadata } from '@backstage/plugin-playlist-common'; + +import { GetAllPlaylistsRequest, PlaylistApi } from './PlaylistApi'; + +/** + * @public + */ +export class PlaylistClient implements PlaylistApi { + private readonly discoveryApi: DiscoveryApi; + private readonly fetchApi: FetchApi; + + constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) { + this.discoveryApi = options.discoveryApi; + this.fetchApi = options.fetchApi; + } + + async getAllPlaylists(req: GetAllPlaylistsRequest = {}): Promise { + const { filter = [], editable } = req; + const params: string[] = []; + + // the "outer array" defined by `filter` occurrences corresponds to "anyOf" filters + // the "inner array" defined within a `filter` param corresponds to "allOf" filters + for (const filterItem of [filter].flat()) { + const filterParts: string[] = []; + for (const [key, value] of Object.entries(filterItem)) { + for (const v of [value].flat()) { + if (typeof v === 'string') { + filterParts.push( + `${encodeURIComponent(key)}=${encodeURIComponent(v)}`, + ); + } + } + } + + if (filterParts.length) { + params.push(`filter=${filterParts.join(',')}`); + } + } + + if (editable) { + params.push('editable=true'); + } + + const query = params.length ? `?${params.join('&')}` : ''; + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch(`${baseUrl}/${query}`, { + method: 'GET', + }); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + + return await resp.json(); + } + + async createPlaylist( + playlist: Omit, + ): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch(`${baseUrl}/`, { + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + body: JSON.stringify(playlist), + }); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + + return await resp.json(); + } + + async getPlaylist(playlistId: string): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch(`${baseUrl}/${playlistId}`, { + method: 'GET', + }); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + + return await resp.json(); + } + + async updatePlaylist(playlist: PlaylistMetadata) { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch(`${baseUrl}/${playlist.id}`, { + headers: { 'Content-Type': 'application/json' }, + method: 'PUT', + body: JSON.stringify(playlist), + }); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + } + + async deletePlaylist(playlistId: string) { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch(`${baseUrl}/${playlistId}`, { + method: 'DELETE', + }); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + } + + async addPlaylistEntities(playlistId: string, entityRefs: string[]) { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch( + `${baseUrl}/${playlistId}/entities`, + { + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + body: JSON.stringify(entityRefs), + }, + ); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + } + + async getPlaylistEntities(playlistId: string): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch( + `${baseUrl}/${playlistId}/entities`, + { method: 'GET' }, + ); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + + return await resp.json(); + } + + async removePlaylistEntities(playlistId: string, entityRefs: string[]) { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch( + `${baseUrl}/${playlistId}/entities`, + { + headers: { 'Content-Type': 'application/json' }, + method: 'DELETE', + body: JSON.stringify(entityRefs), + }, + ); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + } + + async followPlaylist(playlistId: string) { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch( + `${baseUrl}/${playlistId}/followers`, + { method: 'POST' }, + ); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + } + + async unfollowPlaylist(playlistId: string) { + const baseUrl = await this.discoveryApi.getBaseUrl('playlist'); + const resp = await this.fetchApi.fetch( + `${baseUrl}/${playlistId}/followers`, + { method: 'DELETE' }, + ); + + if (!resp.ok) { + throw await ResponseError.fromResponse(resp); + } + } +} diff --git a/plugins/playlist/src/api/index.ts b/plugins/playlist/src/api/index.ts new file mode 100644 index 0000000000..b53cbd99ef --- /dev/null +++ b/plugins/playlist/src/api/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 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 './PlaylistClient'; +export * from './PlaylistApi'; diff --git a/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.test.tsx b/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.test.tsx new file mode 100644 index 0000000000..e00f7dc582 --- /dev/null +++ b/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.test.tsx @@ -0,0 +1,142 @@ +/* + * Copyright 2022 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 { ErrorApi, errorApiRef } from '@backstage/core-plugin-api'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionApi, + permissionApiRef, +} from '@backstage/plugin-permission-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { Button } from '@material-ui/core'; +import { fireEvent, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; +import { SWRConfig } from 'swr'; + +import { PlaylistApi, playlistApiRef } from '../../api'; +import { rootRouteRef } from '../../routes'; +import { CreatePlaylistButton } from './CreatePlaylistButton'; + +jest.mock('../PlaylistEditDialog', () => ({ + PlaylistEditDialog: ({ onSave, open }: { onSave: Function; open: boolean }) => + open ? ( + + )} + setOpenDialog(false)} + onSave={savePlaylist} + /> + + ); +}; diff --git a/plugins/playlist/src/components/CreatePlaylistButton/index.ts b/plugins/playlist/src/components/CreatePlaylistButton/index.ts new file mode 100644 index 0000000000..ac28bbc509 --- /dev/null +++ b/plugins/playlist/src/components/CreatePlaylistButton/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './CreatePlaylistButton'; diff --git a/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.test.tsx b/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.test.tsx new file mode 100644 index 0000000000..9c61105ef9 --- /dev/null +++ b/plugins/playlist/src/components/EntityPlaylistDialog/EntityPlaylistDialog.test.tsx @@ -0,0 +1,200 @@ +/* + * Copyright 2022 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 { AlertApi, alertApiRef } from '@backstage/core-plugin-api'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionApi, + permissionApiRef, +} from '@backstage/plugin-permission-react'; +import { Button } from '@material-ui/core'; +import { fireEvent, getByRole, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; +import { SWRConfig } from 'swr'; + +import { PlaylistApi, playlistApiRef } from '../../api'; +import { rootRouteRef } from '../../routes'; +import { EntityPlaylistDialog } from './EntityPlaylistDialog'; + +const navigateMock = jest.fn(); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => navigateMock, +})); + +jest.mock('../PlaylistEditDialog', () => ({ + PlaylistEditDialog: ({ onSave, open }: { onSave: Function; open: boolean }) => + open ? ( + + + + setOpenEditDialog(false)} + onSave={createNewPlaylist} + /> + + ); +}; diff --git a/plugins/playlist/src/components/EntityPlaylistDialog/index.ts b/plugins/playlist/src/components/EntityPlaylistDialog/index.ts new file mode 100644 index 0000000000..77d7e73ae0 --- /dev/null +++ b/plugins/playlist/src/components/EntityPlaylistDialog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './EntityPlaylistDialog'; diff --git a/plugins/playlist/src/components/PersonalListPicker/PersonalListPicker.test.tsx b/plugins/playlist/src/components/PersonalListPicker/PersonalListPicker.test.tsx new file mode 100644 index 0000000000..9255ee1bdb --- /dev/null +++ b/plugins/playlist/src/components/PersonalListPicker/PersonalListPicker.test.tsx @@ -0,0 +1,286 @@ +/* + * Copyright 2022 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 { ApiProvider } from '@backstage/core-app-api'; +import { + ConfigApi, + configApiRef, + IdentityApi, + identityApiRef, + storageApiRef, +} from '@backstage/core-plugin-api'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { MockStorageApi, TestApiRegistry } from '@backstage/test-utils'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { MockPlaylistListProvider } from '../../testUtils'; +import { PlaylistOwnerFilter } from '../PlaylistOwnerPicker'; +import { PersonalListPicker } from './PersonalListPicker'; + +const mockConfigApi = { + getOptionalString: () => 'Test Company', +} as Partial; + +const mockIdentityApi = { + getBackstageIdentity: async () => ({ + ownershipEntityRefs: ['user:default/owner', 'group:default/some-owner'], + }), +} as Partial; + +const apis = TestApiRegistry.from( + [configApiRef, mockConfigApi], + [identityApiRef, mockIdentityApi], + [storageApiRef, MockStorageApi.create()], +); + +const backendPlaylists: Playlist[] = [ + { + id: 'id1', + name: 'playlist-1', + owner: 'group:default/some-owner', + public: true, + entities: 1, + followers: 2, + isFollowing: false, + }, + { + id: 'id2', + name: 'playlist-2', + owner: 'group:default/another-owner', + public: true, + entities: 2, + followers: 1, + isFollowing: true, + }, + { + id: 'id3', + name: 'playlist-3', + owner: 'group:default/another-owner', + public: true, + entities: 2, + followers: 1, + isFollowing: true, + }, + { + id: 'id4', + name: 'playlist-4', + owner: 'user:default/owner', + public: true, + entities: 2, + followers: 1, + isFollowing: true, + }, +]; + +describe('', () => { + const updateFilters = jest.fn() as any; + + beforeEach(() => { + updateFilters.mockClear(); + }); + + it('renders filter groups', async () => { + const { queryByText } = render( + + + + + , + ); + + await waitFor(() => { + expect(queryByText('Personal')).toBeInTheDocument(); + expect(queryByText('Test Company')).toBeInTheDocument(); + }); + }); + + it('renders filters', async () => { + const { getAllByRole } = render( + + + + + , + ); + + await waitFor(() => { + expect( + getAllByRole('menuitem').map(({ textContent }) => textContent), + ).toEqual(['Owned 2', 'Following 3', 'All 4']); + }); + }); + + it('respects other frontend filters in counts', async () => { + const { getAllByRole } = render( + + + + + , + ); + + await waitFor(() => { + expect( + getAllByRole('menuitem').map(({ textContent }) => textContent), + ).toEqual(['Owned -', 'Following 2', 'All 2']); + }); + }); + + it('respects the query parameter filter value', async () => { + const queryParameters = { personal: 'owned' }; + render( + + + + + , + ); + + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).toBe('owned'); + }); + }); + + it('updates personal filter when a menuitem is selected', async () => { + const { getByText } = render( + + + + + , + ); + + fireEvent.click(getByText('Following')); + + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).toBe('following'); + }); + }); + + it('responds to external queryParameters changes', async () => { + const rendered = render( + + + + + , + ); + + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).toBe('all'); + }); + + rendered.rerender( + + + + + , + ); + + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).toBe('owned'); + }); + }); + + describe.each(['owned', 'following'])( + 'filter resetting for %s playlists', + type => { + const picker = (props: { loading: boolean; mockData: any }) => ( + + ({ + ...p, + ...props.mockData, + })), + queryParameters: { personal: type }, + updateFilters, + loading: props.loading, + }} + > + + + + ); + + describe(`when there are no ${type} playlists`, () => { + const mockData = + type === 'owned' + ? { owner: 'group:default/no-owner' } + : { isFollowing: false }; + + it('does not reset the filter while playlists are loading', async () => { + render(picker({ loading: true, mockData })); + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).not.toBe( + 'all', + ); + }); + }); + + it('resets the filter to "all" when playlists are loaded', async () => { + render(picker({ loading: false, mockData })); + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).toBe('all'); + }); + }); + }); + + describe(`when there are some ${type} playlists present`, () => { + const mockData = {}; + + it('does not reset the filter while playlists are loading', async () => { + render(picker({ loading: true, mockData })); + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).not.toBe( + 'all', + ); + }); + }); + + it('does not reset the filter when playlists are loaded', async () => { + render(picker({ loading: false, mockData })); + await waitFor(() => { + expect(updateFilters.mock.lastCall[0].personal.value).toBe(type); + }); + }); + }); + }, + ); +}); diff --git a/plugins/playlist/src/components/PersonalListPicker/PersonalListPicker.tsx b/plugins/playlist/src/components/PersonalListPicker/PersonalListPicker.tsx new file mode 100644 index 0000000000..1172f2f6fa --- /dev/null +++ b/plugins/playlist/src/components/PersonalListPicker/PersonalListPicker.tsx @@ -0,0 +1,293 @@ +/* + * Copyright 2022 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 { + configApiRef, + IconComponent, + identityApiRef, + useApi, +} from '@backstage/core-plugin-api'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { + Card, + List, + ListItemIcon, + ListItemSecondaryAction, + ListItemText, + makeStyles, + MenuItem, + Theme, + Typography, +} from '@material-ui/core'; +import PlaylistPlayIcon from '@material-ui/icons/PlaylistPlay'; +import SettingsIcon from '@material-ui/icons/Settings'; +import { compact } from 'lodash'; +import React, { Fragment, useEffect, useMemo, useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; + +import { usePlaylistList } from '../../hooks'; +import { PlaylistFilter } from '../../types'; + +export const enum PersonalListFilterValue { + owned = 'owned', + following = 'following', + all = 'all', +} + +export class PersonalListFilter implements PlaylistFilter { + constructor( + readonly value: PersonalListFilterValue, + readonly isOwnedPlaylist: (playlist: Playlist) => boolean, + ) {} + + filterPlaylist(playlist: Playlist): boolean { + switch (this.value) { + case PersonalListFilterValue.owned: + return this.isOwnedPlaylist(playlist); + case PersonalListFilterValue.following: + return playlist.isFollowing; + default: + return true; + } + } + + toQueryValue(): string { + return this.value; + } +} + +const useStyles = makeStyles(theme => ({ + root: { + backgroundColor: 'rgba(0, 0, 0, .11)', + boxShadow: 'none', + margin: theme.spacing(1, 0, 1, 0), + }, + title: { + margin: theme.spacing(1, 0, 0, 1), + textTransform: 'uppercase', + fontSize: 12, + fontWeight: 'bold', + }, + listIcon: { + minWidth: 30, + color: theme.palette.text.primary, + }, + menuItem: { + minHeight: theme.spacing(6), + }, + groupWrapper: { + margin: theme.spacing(1, 1, 2, 1), + }, +})); + +type ButtonGroup = { + name: string; + items: { + id: PersonalListFilterValue; + label: string; + icon?: IconComponent; + }[]; +}; + +function getFilterGroups(orgName: string | undefined): ButtonGroup[] { + return [ + { + name: 'Personal', + items: [ + { + id: PersonalListFilterValue.owned, + label: 'Owned', + icon: SettingsIcon, + }, + { + id: PersonalListFilterValue.following, + label: 'Following', + icon: PlaylistPlayIcon, + }, + ], + }, + { + name: orgName ?? 'Company', + items: [ + { + id: PersonalListFilterValue.all, + label: 'All', + }, + ], + }, + ]; +} + +export const PersonalListPicker = () => { + const classes = useStyles(); + const configApi = useApi(configApiRef); + const identityApi = useApi(identityApiRef); + const orgName = configApi.getOptionalString('organization.name') ?? 'Company'; + const filterGroups = getFilterGroups(orgName); + + const { loading: loadingOwnership, value: ownershipRefs } = + useAsync(async () => { + const { ownershipEntityRefs } = await identityApi.getBackstageIdentity(); + return ownershipEntityRefs; + }, []); + + const isOwnedPlaylist = useMemo(() => { + const myOwnerRefs = new Set(ownershipRefs ?? []); + return (playlist: Playlist) => myOwnerRefs.has(playlist.owner); + }, [ownershipRefs]); + + const { + filters, + updateFilters, + backendPlaylists, + queryParameters: { personal: personalParameter }, + loading: loadingBackendPlaylists, + } = usePlaylistList(); + + const loading = loadingBackendPlaylists || loadingOwnership; + + // Static filters; used for generating counts of potentially unselected options + const ownedFilter = useMemo( + () => + new PersonalListFilter(PersonalListFilterValue.owned, isOwnedPlaylist), + [isOwnedPlaylist], + ); + const followingFilter = useMemo( + () => + new PersonalListFilter( + PersonalListFilterValue.following, + isOwnedPlaylist, + ), + [isOwnedPlaylist], + ); + + const queryParamPersonalFilter = useMemo( + () => [personalParameter].flat()[0], + [personalParameter], + ); + + const [selectedPersonalFilter, setSelectedPersonalFilter] = useState( + queryParamPersonalFilter ?? PersonalListFilterValue.all, + ); + + // To show proper counts for each section, apply all other frontend filters _except_ the personal + // filter that's controlled by this picker. + const playlistsWithoutPersonalFilter = useMemo( + () => + backendPlaylists.filter(playlist => + compact(Object.values({ ...filters, personal: undefined })).every( + (filter: PlaylistFilter) => + !filter.filterPlaylist || filter.filterPlaylist(playlist), + ), + ), + [filters, backendPlaylists], + ); + + const filterCounts = useMemo>( + () => ({ + all: playlistsWithoutPersonalFilter.length, + following: playlistsWithoutPersonalFilter.filter(playlist => + followingFilter.filterPlaylist(playlist), + ).length, + owned: playlistsWithoutPersonalFilter.filter(playlist => + ownedFilter.filterPlaylist(playlist), + ).length, + }), + [playlistsWithoutPersonalFilter, followingFilter, ownedFilter], + ); + + // Set selected personal filter on query parameter updates; this happens at initial page load and from + // external updates to the page location. + useEffect(() => { + if (queryParamPersonalFilter) { + setSelectedPersonalFilter(queryParamPersonalFilter); + } + }, [queryParamPersonalFilter]); + + useEffect(() => { + if ( + !loading && + !!selectedPersonalFilter && + selectedPersonalFilter !== PersonalListFilterValue.all && + filterCounts[selectedPersonalFilter] === 0 + ) { + setSelectedPersonalFilter(PersonalListFilterValue.all); + } + }, [ + loading, + filterCounts, + selectedPersonalFilter, + setSelectedPersonalFilter, + ]); + + useEffect(() => { + updateFilters({ + personal: selectedPersonalFilter + ? new PersonalListFilter( + selectedPersonalFilter as PersonalListFilterValue, + isOwnedPlaylist, + ) + : undefined, + }); + }, [selectedPersonalFilter, isOwnedPlaylist, updateFilters]); + + return ( + + {filterGroups.map(group => ( + + + {group.name} + + + + {group.items.map(item => ( + setSelectedPersonalFilter(item.id)} + selected={item.id === selectedPersonalFilter} + className={classes.menuItem} + disabled={filterCounts[item.id] === 0} + data-testid={`personal-picker-${item.id}`} + tabIndex={0} + ContainerProps={{ role: 'menuitem' }} + > + {item.icon && ( + + + + )} + + {item.label} + + + {filterCounts[item.id] || '-'} + + + ))} + + + + ))} + + ); +}; diff --git a/plugins/playlist/src/components/PersonalListPicker/index.ts b/plugins/playlist/src/components/PersonalListPicker/index.ts new file mode 100644 index 0000000000..f67f18ab39 --- /dev/null +++ b/plugins/playlist/src/components/PersonalListPicker/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PersonalListPicker'; diff --git a/plugins/playlist/src/components/PlaylistCard/PlaylistCard.test.tsx b/plugins/playlist/src/components/PlaylistCard/PlaylistCard.test.tsx new file mode 100644 index 0000000000..177cc23b81 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistCard/PlaylistCard.test.tsx @@ -0,0 +1,57 @@ +/* + * Copyright 2022 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 { entityRouteRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { lightTheme } from '@backstage/theme'; +import { ThemeProvider } from '@material-ui/core'; +import React from 'react'; + +import { rootRouteRef } from '../../routes'; +import { PlaylistCard } from './PlaylistCard'; + +describe('', () => { + it('renders playlist info', async () => { + const rendered = await renderInTestApp( + + + , + { + mountedRoutes: { + '/playlists': rootRouteRef, + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + expect(rendered.getByText('playlist-1')).toBeInTheDocument(); + expect(rendered.getByText('test description')).toBeInTheDocument(); + expect(rendered.getByText('some-owner')).toBeInTheDocument(); + expect(rendered.getByText('3 entities')).toBeInTheDocument(); + expect(rendered.getByText('2 followers')).toBeInTheDocument(); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx b/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx new file mode 100644 index 0000000000..2db7e85f7e --- /dev/null +++ b/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx @@ -0,0 +1,131 @@ +/* + * Copyright 2022 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, + ItemCardHeader, + MarkdownContent, +} from '@backstage/core-components'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { EntityRefLinks } from '@backstage/plugin-catalog-react'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { BackstageTheme } from '@backstage/theme'; +import { + Box, + Card, + CardActions, + CardContent, + CardMedia, + Chip, + makeStyles, + Tooltip, + Typography, +} from '@material-ui/core'; +import LockIcon from '@material-ui/icons/Lock'; +import React from 'react'; + +import { playlistRouteRef } from '../../routes'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + cardHeader: { + position: 'relative', + }, + title: { + backgroundImage: theme.getPageTheme({ themeId: 'home' }).backgroundImage, + }, + box: { + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + '-webkit-line-clamp': 10, + '-webkit-box-orient': 'vertical', + paddingBottom: '0.8em', + }, + label: { + color: theme.palette.text.secondary, + textTransform: 'uppercase', + fontSize: '0.65rem', + fontWeight: 'bold', + letterSpacing: 0.5, + lineHeight: 1, + paddingBottom: '0.2rem', + }, + chip: { + marginRight: 'auto', + }, + privateIcon: { + position: 'absolute', + top: theme.spacing(0.5), + right: theme.spacing(0.5), + padding: '0.25rem', + }, +})); + +export type PlaylistCardProps = { + playlist: Playlist; +}; + +export const PlaylistCard = ({ playlist }: PlaylistCardProps) => { + const classes = useStyles(); + const playlistRoute = useRouteRef(playlistRouteRef); + + return ( + + + {!playlist.public && ( + + + + )} + + + + + + + + + Description + + + + + + Owner + + + + + + + + + ); +}; diff --git a/plugins/playlist/src/components/PlaylistCard/index.ts b/plugins/playlist/src/components/PlaylistCard/index.ts new file mode 100644 index 0000000000..d043c8b6d0 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistCard/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistCard'; diff --git a/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.test.tsx b/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.test.tsx new file mode 100644 index 0000000000..9f30d7608c --- /dev/null +++ b/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2022 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 { IdentityApi, identityApiRef } from '@backstage/core-plugin-api'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { fireEvent, getByRole, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; + +import { PlaylistEditDialog } from './PlaylistEditDialog'; + +describe('', () => { + it('handle saving with an edited playlist', async () => { + const identityApi: Partial = { + getBackstageIdentity: async () => ({ + type: 'user', + userEntityRef: 'user:default/me', + ownershipEntityRefs: ['group:default/test-owner', 'user:default/me'], + }), + }; + + const mockOnSave = jest.fn().mockImplementation(async () => {}); + const rendered = await renderInTestApp( + + + , + ); + + act(() => { + fireEvent.input( + getByRole(rendered.getByTestId('edit-dialog-name-input'), 'textbox'), + { + target: { + value: 'test playlist', + }, + }, + ); + + fireEvent.input( + getByRole( + rendered.getByTestId('edit-dialog-description-input'), + 'textbox', + ), + { + target: { + value: 'test description', + }, + }, + ); + + fireEvent.mouseDown( + getByRole(rendered.getByTestId('edit-dialog-owner-select'), 'button'), + ); + + fireEvent.click(rendered.getByText('test-owner')); + + fireEvent.click( + getByRole(rendered.getByTestId('edit-dialog-public-option'), 'radio'), + ); + + fireEvent.click(rendered.getByTestId('edit-dialog-save-button')); + }); + + await waitFor(() => { + expect(mockOnSave).toHaveBeenCalledWith({ + name: 'test playlist', + description: 'test description', + owner: 'group:default/test-owner', + public: true, + }); + }); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx b/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx new file mode 100644 index 0000000000..27cc13fede --- /dev/null +++ b/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx @@ -0,0 +1,217 @@ +/* + * Copyright 2022 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 { parseEntityRef } from '@backstage/catalog-model'; +import { identityApiRef, useApi } from '@backstage/core-plugin-api'; +import { humanizeEntityRef } from '@backstage/plugin-catalog-react'; +import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; +import { + Button, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + FormControl, + FormControlLabel, + InputLabel, + makeStyles, + MenuItem, + LinearProgress, + Radio, + RadioGroup, + Select, + TextField, +} from '@material-ui/core'; +import React from 'react'; +import { useForm, Controller } from 'react-hook-form'; +import useAsync from 'react-use/lib/useAsync'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; + +const useStyles = makeStyles({ + buttonWrapper: { + position: 'relative', + }, + buttonProgress: { + position: 'absolute', + top: '50%', + left: '50%', + marginTop: -12, + marginLeft: -12, + }, +}); + +export type PlaylistEditDialogProps = { + open: boolean; + onClose: () => void; + onSave: (playlist: Omit) => Promise; + playlist?: Omit; +}; + +export const PlaylistEditDialog = ({ + open, + onClose, + onSave, + playlist = { + name: '', + description: '', + owner: '', + public: false, + }, +}: PlaylistEditDialogProps) => { + const classes = useStyles(); + const identityApi = useApi(identityApiRef); + + const { loading: loadingOwnership, value: ownershipRefs } = + useAsync(async () => { + const { ownershipEntityRefs } = await identityApi.getBackstageIdentity(); + return ownershipEntityRefs; + }, []); + + const defaultValues = { + ...playlist, + public: playlist.public.toString(), + }; + + const { + control, + formState: { errors }, + handleSubmit, + reset, + } = useForm({ defaultValues }); + + const [saving, savePlaylist] = useAsyncFn( + formValues => + onSave({ ...formValues, public: JSON.parse(formValues.public) }), + [onSave], + ); + + const closeDialog = () => { + if (!saving.loading) { + onClose(); + reset(defaultValues); + } + }; + + return ( +

+ + ( + + )} + /> + ( + + )} + /> + {loadingOwnership ? ( + + ) : ( + ( + + Owner + + + )} + /> + )} + ( + + + } + /> + } + /> + + + )} + /> + + + +
+ + {saving.loading && ( + + )} +
+
+
+ ); +}; diff --git a/plugins/playlist/src/components/PlaylistEditDialog/index.ts b/plugins/playlist/src/components/PlaylistEditDialog/index.ts new file mode 100644 index 0000000000..3355443b82 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistEditDialog/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistEditDialog'; diff --git a/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.test.tsx b/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.test.tsx new file mode 100644 index 0000000000..6dae17d3bc --- /dev/null +++ b/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.test.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2022 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 { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionApi, + permissionApiRef, +} from '@backstage/plugin-permission-react'; +import React from 'react'; + +import { PlaylistApi, playlistApiRef } from '../../api'; +import { rootRouteRef } from '../../routes'; +import { PlaylistIndexPage } from './PlaylistIndexPage'; + +const playlistApi: Partial = { + getAllPlaylists: async () => [], +}; + +const permissionApi: Partial = { + authorize: async () => ({ result: AuthorizeResult.ALLOW }), +}; + +describe('PlaylistIndexPage', () => { + it('should render', async () => { + const rendered = await renderInTestApp( + + + , + { mountedRoutes: { '/playlists': rootRouteRef } }, + ); + expect(rendered.getByText('Playlists')).toBeInTheDocument(); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx b/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx new file mode 100644 index 0000000000..0ee829018a --- /dev/null +++ b/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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 { + PageWithHeader, + Content, + ContentHeader, + SupportButton, +} from '@backstage/core-components'; +import { CatalogFilterLayout } from '@backstage/plugin-catalog-react'; + +import { PlaylistListProvider } from '../../hooks'; +import { CreatePlaylistButton } from '../CreatePlaylistButton'; +import { PersonalListPicker } from '../PersonalListPicker'; +import { PlaylistList } from '../PlaylistList'; +import { PlaylistOwnerPicker } from '../PlaylistOwnerPicker'; +import { PlaylistSearchBar } from '../PlaylistSearchBar'; +import { PlaylistSortPicker } from '../PlaylistSortPicker'; + +export const PlaylistIndexPage = () => ( + + + + + + + + + + + + + + + + + + + + + +); diff --git a/plugins/playlist/src/components/PlaylistIndexPage/index.ts b/plugins/playlist/src/components/PlaylistIndexPage/index.ts new file mode 100644 index 0000000000..06858a8262 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistIndexPage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 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 { PlaylistIndexPage } from './PlaylistIndexPage'; diff --git a/plugins/playlist/src/components/PlaylistList/PlaylistList.test.tsx b/plugins/playlist/src/components/PlaylistList/PlaylistList.test.tsx new file mode 100644 index 0000000000..27030ec1c7 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistList/PlaylistList.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2022 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 { Playlist } from '@backstage/plugin-playlist-common'; +import { render } from '@testing-library/react'; +import React from 'react'; + +import { MockPlaylistListProvider } from '../../testUtils'; +import { PlaylistList } from './PlaylistList'; + +jest.mock('../PlaylistCard', () => ({ + PlaylistCard: ({ playlist }: { playlist: Playlist }) => ( +
{playlist.name}
+ ), +})); + +describe('', () => { + it('renders error on error', () => { + const rendered = render( + + + , + ); + + expect(rendered.getByText('Test Error')).toBeInTheDocument(); + }); + + it('handles no playlists', () => { + const rendered = render( + + + , + ); + + expect( + rendered.getByText('No playlists found that match your filter.'), + ).toBeInTheDocument(); + }); + + it('renders playlists', () => { + const rendered = render( + + + , + ); + + expect(rendered.getByText('playlist-1')).toBeInTheDocument(); + expect(rendered.getByText('playlist-2')).toBeInTheDocument(); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx b/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx new file mode 100644 index 0000000000..fcc3971542 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx @@ -0,0 +1,58 @@ +/* + * Copyright 2022 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 { + Content, + ItemCardGrid, + Progress, + WarningPanel, +} from '@backstage/core-components'; +import { Typography } from '@material-ui/core'; + +import { usePlaylistList } from '../../hooks'; +import { PlaylistCard } from '../PlaylistCard'; + +export const PlaylistList = () => { + const { loading, error, playlists } = usePlaylistList(); + + return ( + <> + {loading && } + + {error && ( + + {error.message} + + )} + + {!error && !loading && !playlists.length && ( + + No playlists found that match your filter. + + )} + + + + {playlists?.length > 0 && + playlists.map(playlist => ( + + ))} + + + + ); +}; diff --git a/plugins/playlist/src/components/PlaylistList/index.ts b/plugins/playlist/src/components/PlaylistList/index.ts new file mode 100644 index 0000000000..59fa5cf0fb --- /dev/null +++ b/plugins/playlist/src/components/PlaylistList/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistList'; diff --git a/plugins/playlist/src/components/PlaylistOwnerPicker/PlaylistOwnerPicker.test.tsx b/plugins/playlist/src/components/PlaylistOwnerPicker/PlaylistOwnerPicker.test.tsx new file mode 100644 index 0000000000..c74e6f6853 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistOwnerPicker/PlaylistOwnerPicker.test.tsx @@ -0,0 +1,215 @@ +/* + * Copyright 2022 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 { parseEntityRef } from '@backstage/catalog-model'; +import { humanizeEntityRef } from '@backstage/plugin-catalog-react'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { MockPlaylistListProvider } from '../../testUtils'; +import { + PlaylistOwnerFilter, + PlaylistOwnerPicker, +} from './PlaylistOwnerPicker'; + +const samplePlaylists: Playlist[] = [ + { + id: 'id1', + name: 'playlist-1', + owner: 'group:default/some-owner', + public: true, + entities: 1, + followers: 2, + isFollowing: false, + }, + { + id: 'id2', + name: 'playlist-2', + owner: 'group:default/another-owner', + public: true, + entities: 2, + followers: 1, + isFollowing: true, + }, + { + id: 'id3', + name: 'playlist-3', + owner: 'group:default/another-owner', + public: true, + entities: 2, + followers: 1, + isFollowing: true, + }, + { + id: 'id4', + name: 'playlist-4', + owner: 'user:default/owner', + public: true, + entities: 2, + followers: 1, + isFollowing: true, + }, +]; + +describe('', () => { + it('renders all owners', () => { + const rendered = render( + + + , + ); + expect(rendered.getByText('Owner')).toBeInTheDocument(); + + fireEvent.click(rendered.getByTestId('owner-picker-expand')); + samplePlaylists + .map(p => + humanizeEntityRef(parseEntityRef(p.owner), { defaultKind: 'group' }), + ) + .forEach(owner => { + expect(rendered.getByText(owner as string)).toBeInTheDocument(); + }); + }); + + it('renders unique owners in alphabetical order', () => { + const rendered = render( + + + , + ); + expect(rendered.getByText('Owner')).toBeInTheDocument(); + + fireEvent.click(rendered.getByTestId('owner-picker-expand')); + + expect(rendered.getAllByRole('option').map(o => o.textContent)).toEqual([ + 'another-owner', + 'some-owner', + 'user:owner', + ]); + }); + + it('respects the query parameter filter value', () => { + const updateFilters = jest.fn(); + const queryParameters = { owners: ['group:default/another-owner'] }; + render( + + + , + ); + + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new PlaylistOwnerFilter(['group:default/another-owner']), + }); + }); + + it('adds owners to filters', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: undefined, + }); + + fireEvent.click(rendered.getByTestId('owner-picker-expand')); + fireEvent.click(rendered.getByText('some-owner')); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new PlaylistOwnerFilter(['group:default/some-owner']), + }); + }); + + it('removes owners from filters', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new PlaylistOwnerFilter(['group:default/some-owner']), + }); + fireEvent.click(rendered.getByTestId('owner-picker-expand')); + expect(rendered.getByLabelText('some-owner')).toBeChecked(); + + fireEvent.click(rendered.getByLabelText('some-owner')); + expect(updateFilters).toHaveBeenLastCalledWith({ + owner: undefined, + }); + }); + + it('responds to external queryParameters changes', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new PlaylistOwnerFilter(['group:default/team-a']), + }); + rendered.rerender( + + + , + ); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new PlaylistOwnerFilter(['group:default/team-b']), + }); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistOwnerPicker/PlaylistOwnerPicker.tsx b/plugins/playlist/src/components/PlaylistOwnerPicker/PlaylistOwnerPicker.tsx new file mode 100644 index 0000000000..7def794c6b --- /dev/null +++ b/plugins/playlist/src/components/PlaylistOwnerPicker/PlaylistOwnerPicker.tsx @@ -0,0 +1,134 @@ +/* + * Copyright 2022 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 { parseEntityRef } from '@backstage/catalog-model'; +import { humanizeEntityRef } from '@backstage/plugin-catalog-react'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { + Box, + Checkbox, + Chip, + FormControlLabel, + TextField, + Typography, +} from '@material-ui/core'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { Autocomplete } from '@material-ui/lab'; +import React, { useEffect, useMemo, useState } from 'react'; + +import { usePlaylistList } from '../../hooks'; +import { PlaylistFilter } from '../../types'; + +export class PlaylistOwnerFilter implements PlaylistFilter { + constructor(readonly values: string[]) {} + + filterPlaylist(playlist: Playlist): boolean { + return this.values.some(v => playlist.owner === v); + } + + toQueryValue(): string[] { + return this.values; + } +} + +const icon = ; +const checkedIcon = ; + +export const PlaylistOwnerPicker = () => { + const { + updateFilters, + backendPlaylists, + filters, + queryParameters: { owners: ownersParameter }, + } = usePlaylistList(); + + const queryParamOwners = useMemo( + () => [ownersParameter].flat().filter(Boolean) as string[], + [ownersParameter], + ); + + const [selectedOwners, setSelectedOwners] = useState( + queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [], + ); + + // Set selected owners on query parameter updates; this happens at initial page load and from + // external updates to the page location. + useEffect(() => { + if (queryParamOwners.length) { + setSelectedOwners(queryParamOwners); + } + }, [queryParamOwners]); + + useEffect(() => { + updateFilters({ + owners: selectedOwners.length + ? new PlaylistOwnerFilter(selectedOwners) + : undefined, + }); + }, [selectedOwners, updateFilters]); + + const availableOwners = useMemo( + () => [...new Set(backendPlaylists.map(p => p.owner))].sort(), + [backendPlaylists], + ); + + if (!availableOwners.length) return null; + + return ( + + + Owner + setSelectedOwners(value)} + renderTags={(values: string[], getTagProps: Function) => + values.map((val, index) => ( + + )) + } + renderOption={(option, { selected }) => ( + + } + label={humanizeEntityRef(parseEntityRef(option), { + defaultKind: 'group', + })} + /> + )} + size="small" + popupIcon={} + renderInput={params => } + /> + + + ); +}; diff --git a/plugins/playlist/src/components/PlaylistOwnerPicker/index.ts b/plugins/playlist/src/components/PlaylistOwnerPicker/index.ts new file mode 100644 index 0000000000..fcde27e956 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistOwnerPicker/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistOwnerPicker'; diff --git a/plugins/playlist/src/components/PlaylistPage/AddEntitiesDrawer.test.tsx b/plugins/playlist/src/components/PlaylistPage/AddEntitiesDrawer.test.tsx new file mode 100644 index 0000000000..d1bdf1d46a --- /dev/null +++ b/plugins/playlist/src/components/PlaylistPage/AddEntitiesDrawer.test.tsx @@ -0,0 +1,159 @@ +/* + * Copyright 2022 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 { + CatalogApi, + catalogApiRef, + entityRouteRef, +} from '@backstage/plugin-catalog-react'; +import { SearchApi, searchApiRef } from '@backstage/plugin-search-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { fireEvent, getByText } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; + +import { AddEntitiesDrawer } from './AddEntitiesDrawer'; + +describe('AddEntitiesDrawer', () => { + const catalogApi: Partial = { + getEntityFacets: jest.fn().mockImplementation(async () => ({ + facets: { + kind: [{ value: 'component' }], + }, + })), + }; + + const mockOnAdd = jest.fn(); + const sampleCurrentEntities = [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'system', + metadata: { namespace: 'default', name: 'test-ent' }, + }, + ]; + + const mockSearchResults = [ + { + type: 'software-catalog', + document: { + title: 'Test Ent', + text: 'This is test ent', + location: '/catalog/default/system/test-ent', + kind: 'system', + }, + }, + { + type: 'software-catalog', + document: { + title: 'Test Ent 2', + text: 'This is test ent 2', + location: '/catalog/foo/component/test-ent2', + kind: 'component', + type: 'library', + }, + }, + { + type: 'software-catalog', + document: { + title: 'Test Ent 3', + text: 'This is test ent 3', + location: '/catalog/bar/api/test-ent3', + kind: 'api', + type: 'openapi', + }, + }, + ]; + + const searchApi: Partial = { + query: jest + .fn() + .mockImplementation(async () => ({ results: mockSearchResults })), + }; + + const element = ( + + + + ); + + const render = async () => + renderInTestApp(element, { + mountedRoutes: { '/catalog/:namespace/:kind/:name': entityRouteRef }, + }); + + beforeEach(() => { + mockOnAdd.mockClear(); + }); + + it('should render available entities correctly', async () => { + const rendered = await render(); + expect(searchApi.query).toHaveBeenLastCalledWith({ + filters: {}, + term: '', + types: ['software-catalog'], + }); + + expect(rendered.getByText('Test Ent')).toBeInTheDocument(); + expect(rendered.getByText('This is test ent')).toBeInTheDocument(); + expect(rendered.getByText('Kind: system')).toBeInTheDocument(); + + expect(rendered.getByText('Test Ent 2')).toBeInTheDocument(); + expect(rendered.getByText('This is test ent 2')).toBeInTheDocument(); + expect(rendered.getByText('Kind: component')).toBeInTheDocument(); + expect(rendered.getByText('Type: library')).toBeInTheDocument(); + + expect(rendered.getByText('Test Ent 3')).toBeInTheDocument(); + expect(rendered.getByText('This is test ent 3')).toBeInTheDocument(); + expect(rendered.getByText('Kind: api')).toBeInTheDocument(); + expect(rendered.getByText('Type: openapi')).toBeInTheDocument(); + }); + + it('should disable options that are already added', async () => { + const rendered = await render(); + + const addButtons = rendered.getAllByTestId('entity-drawer-add-button'); + expect(addButtons.length).toEqual(3); + + expect(getByText(addButtons[0], 'Added')).toBeInTheDocument(); + expect(addButtons[0]).toBeDisabled(); + + expect(getByText(addButtons[1], 'Add')).toBeInTheDocument(); + expect(addButtons[1]).not.toBeDisabled(); + + expect(getByText(addButtons[2], 'Add')).toBeInTheDocument(); + expect(addButtons[2]).not.toBeDisabled(); + }); + + it('should add entities correctly', async () => { + const rendered = await render(); + + act(() => { + fireEvent.click(rendered.getAllByTestId('entity-drawer-add-button')[1]); + }); + + expect(mockOnAdd).toHaveBeenCalledWith('component:foo/test-ent2'); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistPage/AddEntitiesDrawer.tsx b/plugins/playlist/src/components/PlaylistPage/AddEntitiesDrawer.tsx new file mode 100644 index 0000000000..05dd4da231 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistPage/AddEntitiesDrawer.tsx @@ -0,0 +1,239 @@ +/* + * Copyright 2022 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, + getCompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; +import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; +import { + SearchBar, + SearchContextProvider, + SearchFilter, + SearchResult, + SearchResultPager, + useSearch, +} from '@backstage/plugin-search-react'; +import { + Box, + Button, + Chip, + createStyles, + Divider, + Drawer, + Grid, + List, + ListItem, + ListItemSecondaryAction, + ListItemText, + makeStyles, + Paper, + Theme, + Typography, +} from '@material-ui/core'; +import React, { useCallback, useEffect, useMemo } from 'react'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + paper: { + width: '50%', + padding: theme.spacing(2.5), + }, + searchBarContainer: { + borderRadius: 30, + display: 'flex', + height: '2.4em', + }, + gridContainer: { + height: '100%', + }, + searchResults: { + overflow: 'auto', + }, + itemContainer: { + flexWrap: 'wrap', + paddingRight: '75px', + }, + itemText: { + width: '100%', + wordBreak: 'break-word', + marginBottom: '1rem', + }, + }), +); + +const RestrictCatalogIndexResults = () => { + const { setTypes } = useSearch(); + useEffect(() => setTypes(['software-catalog']), [setTypes]); + return null; +}; + +export type AddEntitiesDrawerProps = { + currentEntities: Entity[]; + open: boolean; + onAdd: (entityRef: string) => void; + onClose: () => void; +}; + +export const AddEntitiesDrawer = ({ + currentEntities, + open, + onAdd, + onClose, +}: AddEntitiesDrawerProps) => { + const classes = useStyles(); + const catalogApi = useApi(catalogApiRef); + const entityRoute = useRouteRef(entityRouteRef); + const entityLocationRegex = useMemo(() => { + const forwardSlashRegex = new RegExp('/', 'g'); + const locationRegex = entityRoute({ + namespace: '(?.+?)', + kind: '(?.+?)', + name: '(?.+?)', + }).replace(forwardSlashRegex, '\\/'); + + return new RegExp(`${locationRegex}$`); + }, [entityRoute]); + + const currentEntityLocations = useMemo( + () => + currentEntities.map(entity => + entityRoute(getCompoundEntityRef(entity)).toLocaleLowerCase('en-US'), + ), + [currentEntities, entityRoute], + ); + + const getEntityKinds = async () => { + return ( + await catalogApi.getEntityFacets({ facets: ['kind'] }) + ).facets.kind.map(f => f.value); + }; + + const addEntity = useCallback( + entityResult => { + // TODO (kuangp): this parsing of the location is not great. Ideally `CatalogEntityDocument` + // contains the `metadata.name` field so we can derive the full ref and we only fall back to + // parsing location if it's missing (ie. for older versions) + const { groups } = entityResult.location.match(entityLocationRegex); + if (groups) { + onAdd(stringifyEntityRef(groups)); + } else { + // eslint-disable-next-line no-console + console.error( + `Failed to parse entity ref from entity location: ${entityResult.location}`, + ); + } + }, + [entityLocationRegex, onAdd], + ); + + return ( + + + + + + + Let's find something for your playlist + + + + + + + + + + + + + {({ results }) => ( + + {results.map(({ document }) => ( + + +
+ + + + + + {(document as CatalogEntityDocument).kind && ( + + )} + {(document as CatalogEntityDocument).type && ( + + )} + +
+
+ +
+ ))} +
+ )} +
+ +
+
+
+
+ ); +}; diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx new file mode 100644 index 0000000000..f7720b25c1 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.test.tsx @@ -0,0 +1,175 @@ +/* + * Copyright 2022 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 { ErrorApi, errorApiRef } from '@backstage/core-plugin-api'; +import { entityRouteRef } from '@backstage/plugin-catalog-react'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionApi, + permissionApiRef, +} from '@backstage/plugin-permission-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { Button } from '@material-ui/core'; +import { fireEvent, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; +import { SWRConfig } from 'swr'; + +import { PlaylistApi, playlistApiRef } from '../../api'; +import { PlaylistEntitiesTable } from './PlaylistEntitiesTable'; + +jest.mock('./AddEntitiesDrawer', () => ({ + AddEntitiesDrawer: ({ onAdd, open }: { onAdd: Function; open: boolean }) => + open ? ( + +
+ + {deleting.loading && ( + + )} +
+ + +
+ ); +}; diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistPage.test.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistPage.test.tsx new file mode 100644 index 0000000000..65d3d79be6 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistPage.test.tsx @@ -0,0 +1,141 @@ +/* + * Copyright 2022 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 { ErrorApi, errorApiRef } from '@backstage/core-plugin-api'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionApi, + permissionApiRef, +} from '@backstage/plugin-permission-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { fireEvent, getByText, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; +import { SWRConfig } from 'swr'; + +import { PlaylistApi, playlistApiRef } from '../../api'; +import { PlaylistPage } from './PlaylistPage'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ playlistId: 'id1' }), +})); + +// Mock out nested components to simplify tests +jest.mock('./PlaylistEntitiesTable', () => ({ + PlaylistEntitiesTable: () => null, +})); + +jest.mock('./PlaylistHeader', () => ({ + PlaylistHeader: () => null, +})); + +describe('PlaylistPage', () => { + const testPlaylist = { + id: 'id1', + name: 'playlist-1', + description: 'test description', + owner: 'group:default/some-owner', + public: true, + entities: 1, + followers: 2, + isFollowing: false, + }; + + const errorApi: Partial = { post: jest.fn() }; + const playlistApi: Partial = { + getPlaylist: jest.fn().mockImplementation(async () => testPlaylist), + followPlaylist: jest.fn().mockImplementation(async () => {}), + unfollowPlaylist: jest.fn().mockImplementation(async () => {}), + }; + const mockAuthorize = jest + .fn() + .mockImplementation(async () => ({ result: AuthorizeResult.ALLOW })); + const permissionApi: Partial = { authorize: mockAuthorize }; + + // SWR used by the usePermission hook needs cache to be reset for each test + const element = ( + new Map() }}> + + + + + ); + + beforeEach(() => { + mockAuthorize.mockClear(); + }); + + it('show render the playlist info', async () => { + const rendered = await renderInTestApp(element); + expect(playlistApi.getPlaylist).toHaveBeenCalledWith('id1'); + expect(rendered.getByText('test description')).toBeInTheDocument(); + expect( + rendered.getByTestId('playlist-page-follow-button'), + ).toBeInTheDocument(); + expect( + getByText(rendered.getByTestId('playlist-page-follow-button'), 'Follow'), + ).toBeInTheDocument(); + }); + + it('should not render the follow button if unauthorized', async () => { + mockAuthorize.mockImplementationOnce(async () => ({ + result: AuthorizeResult.DENY, + })); + const rendered = await renderInTestApp(element); + expect(rendered.queryByTestId('playlist-page-follow-button')).toBeNull(); + }); + + it('should reflect and toggle the following state of the playlist', async () => { + const rendered = await renderInTestApp(element); + + act(() => { + fireEvent.click(rendered.getByTestId('playlist-page-follow-button')); + testPlaylist.isFollowing = true; + }); + + await waitFor(() => { + expect(playlistApi.followPlaylist).toHaveBeenCalledWith('id1'); + expect( + getByText( + rendered.getByTestId('playlist-page-follow-button'), + 'Following', + ), + ).toBeInTheDocument(); + }); + + act(() => { + fireEvent.click(rendered.getByTestId('playlist-page-follow-button')); + testPlaylist.isFollowing = false; + }); + + await waitFor(() => { + expect(playlistApi.unfollowPlaylist).toHaveBeenCalledWith('id1'); + expect( + getByText( + rendered.getByTestId('playlist-page-follow-button'), + 'Follow', + ), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx new file mode 100644 index 0000000000..4c9a0edbb7 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx @@ -0,0 +1,133 @@ +/* + * Copyright 2022 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 { Content, ErrorPage, Page } from '@backstage/core-components'; +import { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; +import { usePermission } from '@backstage/plugin-permission-react'; +import { permissions } from '@backstage/plugin-playlist-common'; +import { + Button, + Card, + CardContent, + CardHeader, + Divider, + LinearProgress, + makeStyles, + Typography, +} from '@material-ui/core'; +import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; + +import { playlistApiRef } from '../../api'; +import { PlaylistEntitiesTable } from './PlaylistEntitiesTable'; +import { PlaylistHeader } from './PlaylistHeader'; + +const useStyles = makeStyles({ + followButton: { + top: '6px', + right: '8px', + }, +}); + +export const PlaylistPage = () => { + const classes = useStyles(); + const errorApi = useApi(errorApiRef); + const playlistApi = useApi(playlistApiRef); + const { playlistId } = useParams(); + + const [{ value: playlist, loading, error }, loadPlaylist] = useAsyncFn( + () => playlistApi.getPlaylist(playlistId!), + [playlistApi], + ); + + useEffect(() => { + loadPlaylist(); + }, [loadPlaylist]); + + const { allowed: followAllowed } = usePermission({ + permission: permissions.playlistFollowersUpdate, + resourceRef: playlist?.id, + }); + + const [{ loading: loadingFollowRequest }, followPlaylist] = + useAsyncFn(async () => { + try { + if (playlist!.isFollowing) { + await playlistApi.unfollowPlaylist(playlist!.id); + } else { + await playlistApi.followPlaylist(playlist!.id); + } + + loadPlaylist(); + } catch (e) { + errorApi.post(e); + } + }, [errorApi, loadPlaylist, playlist, playlistApi]); + + if (error) { + return ( + + ); + } + + return ( + <> + {loading && } + + {playlist && ( + <> + + + + + {playlist.isFollowing ? 'Following' : 'Follow'} + + ) + } + /> + + + + {playlist.description} + + + +
+ +
+ + )} +
+ + ); +}; diff --git a/plugins/playlist/src/components/PlaylistPage/index.ts b/plugins/playlist/src/components/PlaylistPage/index.ts new file mode 100644 index 0000000000..695b243cd0 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistPage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistPage'; diff --git a/plugins/playlist/src/components/PlaylistSearchBar/PlaylistSearchBar.test.tsx b/plugins/playlist/src/components/PlaylistSearchBar/PlaylistSearchBar.test.tsx new file mode 100644 index 0000000000..84650e4f41 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistSearchBar/PlaylistSearchBar.test.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2022 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 { fireEvent, render, waitFor } from '@testing-library/react'; +import { DefaultPlaylistFilters } from '../../hooks'; +import { MockPlaylistListProvider } from '../../testUtils'; +import { PlaylistSearchBar, PlaylistTextFilter } from './PlaylistSearchBar'; + +describe('PlaylistSearchBar', () => { + it('should display search value and execute set callback', async () => { + const updateFilters = jest.fn(); + + const filters: DefaultPlaylistFilters = { + text: new PlaylistTextFilter('hello'), + }; + + const { getByDisplayValue } = render( + + + , + ); + + const searchInput = getByDisplayValue('hello'); + expect(searchInput).toBeInTheDocument(); + + fireEvent.change(searchInput, { target: { value: 'world' } }); + await waitFor(() => expect(updateFilters.mock.calls.length).toBe(1)); + expect(updateFilters).toHaveBeenCalledWith({ + text: new PlaylistTextFilter('world'), + }); + + fireEvent.change(searchInput, { target: { value: '' } }); + await waitFor(() => expect(updateFilters.mock.calls.length).toBe(2)); + expect(updateFilters).toHaveBeenCalledWith({ + text: undefined, + }); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistSearchBar/PlaylistSearchBar.tsx b/plugins/playlist/src/components/PlaylistSearchBar/PlaylistSearchBar.tsx new file mode 100644 index 0000000000..94a22a23f5 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistSearchBar/PlaylistSearchBar.tsx @@ -0,0 +1,102 @@ +/* + * Copyright 2022 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 { Playlist } from '@backstage/plugin-playlist-common'; +import { + FormControl, + IconButton, + Input, + InputAdornment, + makeStyles, + Toolbar, +} from '@material-ui/core'; +import Clear from '@material-ui/icons/Clear'; +import Search from '@material-ui/icons/Search'; +import React, { useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; + +import { usePlaylistList } from '../../hooks'; +import { PlaylistFilter } from '../../types'; + +export class PlaylistTextFilter implements PlaylistFilter { + constructor(readonly value: string) {} + + filterPlaylist(playlist: Playlist): boolean { + const upperCaseValue = this.value.toLocaleUpperCase('en-US'); + return ( + playlist.name.toLocaleUpperCase('en-US').includes(upperCaseValue) || + !!playlist.description + ?.toLocaleUpperCase('en-US') + .includes(upperCaseValue) + ); + } +} + +const useStyles = makeStyles(_theme => ({ + searchToolbar: { + paddingLeft: 0, + paddingRight: 0, + }, +})); + +export const PlaylistSearchBar = () => { + const classes = useStyles(); + + const { filters, updateFilters } = usePlaylistList(); + const [search, setSearch] = useState(filters.text?.value ?? ''); + + useDebounce( + () => { + updateFilters({ + text: search.length ? new PlaylistTextFilter(search) : undefined, + }); + }, + 250, + [search, updateFilters], + ); + + return ( + + + setSearch(event.target.value)} + value={search} + startAdornment={ + + + + } + endAdornment={ + + setSearch('')} + edge="end" + disabled={search.length === 0} + > + + + + } + /> + + + ); +}; diff --git a/plugins/playlist/src/components/PlaylistSearchBar/index.ts b/plugins/playlist/src/components/PlaylistSearchBar/index.ts new file mode 100644 index 0000000000..65b7e181cf --- /dev/null +++ b/plugins/playlist/src/components/PlaylistSearchBar/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistSearchBar'; diff --git a/plugins/playlist/src/components/PlaylistSortPicker/PlaylistSortPicker.test.tsx b/plugins/playlist/src/components/PlaylistSortPicker/PlaylistSortPicker.test.tsx new file mode 100644 index 0000000000..9317bfd4c1 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistSortPicker/PlaylistSortPicker.test.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2022 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 { fireEvent, getByRole, render, waitFor } from '@testing-library/react'; +import { act } from '@testing-library/react-hooks'; +import React from 'react'; + +import { MockPlaylistListProvider } from '../../testUtils'; +import { + DefaultPlaylistSortTypes, + DefaultSortCompareFunctions, + PlaylistSortPicker, +} from './PlaylistSortPicker'; + +describe('PlaylistSortPicker', () => { + it('should update sort based on selection', async () => { + const updateSort = jest.fn(); + + const { getByText, getByTestId } = render( + + + , + ); + + act(() => { + fireEvent.mouseDown( + getByRole(getByTestId('sort-picker-select'), 'button'), + ); + }); + const abcSort = getByText('Alphabetical'); + expect(abcSort).toBeInTheDocument(); + + fireEvent.click(abcSort); + await waitFor(() => { + expect(updateSort).toHaveBeenLastCalledWith( + DefaultSortCompareFunctions[DefaultPlaylistSortTypes.alphabetical], + ); + }); + + act(() => { + fireEvent.mouseDown( + getByRole(getByTestId('sort-picker-select'), 'button'), + ); + }); + const entitiesSort = getByText('# Entities'); + expect(entitiesSort).toBeInTheDocument(); + + fireEvent.click(entitiesSort); + await waitFor(() => { + expect(updateSort).toHaveBeenLastCalledWith( + DefaultSortCompareFunctions[DefaultPlaylistSortTypes.numEntities], + ); + }); + }); +}); diff --git a/plugins/playlist/src/components/PlaylistSortPicker/PlaylistSortPicker.tsx b/plugins/playlist/src/components/PlaylistSortPicker/PlaylistSortPicker.tsx new file mode 100644 index 0000000000..21c961eb91 --- /dev/null +++ b/plugins/playlist/src/components/PlaylistSortPicker/PlaylistSortPicker.tsx @@ -0,0 +1,115 @@ +/* + * Copyright 2022 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 { Playlist } from '@backstage/plugin-playlist-common'; +import { + ListSubheader, + makeStyles, + MenuItem, + Select, + Typography, +} from '@material-ui/core'; +import SwapVertIcon from '@material-ui/icons/SwapVert'; +import React from 'react'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; + +import { usePlaylistList } from '../../hooks'; +import { PlaylistSortCompareFunction } from '../../types'; + +export const enum DefaultPlaylistSortTypes { + popular = 'popular', + alphabetical = 'alphabetical', + numEntities = 'numEntities', +} + +export const DefaultSortCompareFunctions: { + [type in DefaultPlaylistSortTypes]: PlaylistSortCompareFunction; +} = { + [DefaultPlaylistSortTypes.popular]: (a: Playlist, b: Playlist) => { + if (a.followers < b.followers) { + return 1; + } else if (a.followers > b.followers) { + return -1; + } + return 0; + }, + [DefaultPlaylistSortTypes.alphabetical]: (a: Playlist, b: Playlist) => + a.name.localeCompare(b.name), + [DefaultPlaylistSortTypes.numEntities]: (a: Playlist, b: Playlist) => { + if (a.entities < b.entities) { + return 1; + } else if (a.entities > b.entities) { + return -1; + } + return 0; + }, +}; + +const sortTypeLabels = { + [DefaultPlaylistSortTypes.popular]: 'Popular', + [DefaultPlaylistSortTypes.alphabetical]: 'Alphabetical', + [DefaultPlaylistSortTypes.numEntities]: '# Entities', +}; + +const useStyles = makeStyles({ + select: { + width: '10rem', + marginRight: '1rem', + }, + icon: { + verticalAlign: 'bottom', + }, +}); + +export const PlaylistSortPicker = () => { + const classes = useStyles(); + const { updateSort } = usePlaylistList(); + + useEffectOnce(() => + updateSort(DefaultSortCompareFunctions[DefaultPlaylistSortTypes.popular]), + ); + + return ( + + ); +}; diff --git a/plugins/playlist/src/components/PlaylistSortPicker/index.ts b/plugins/playlist/src/components/PlaylistSortPicker/index.ts new file mode 100644 index 0000000000..04ea5268ef --- /dev/null +++ b/plugins/playlist/src/components/PlaylistSortPicker/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './PlaylistSortPicker'; diff --git a/plugins/playlist/src/components/Router/Router.test.tsx b/plugins/playlist/src/components/Router/Router.test.tsx new file mode 100644 index 0000000000..ea8620ec42 --- /dev/null +++ b/plugins/playlist/src/components/Router/Router.test.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2022 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 { PlaylistIndexPage } from '../PlaylistIndexPage'; +import { PlaylistPage } from '../PlaylistPage'; +import { Router } from './Router'; +import { renderInTestApp } from '@backstage/test-utils'; + +jest.mock('../PlaylistIndexPage', () => ({ + PlaylistIndexPage: jest.fn(() => null), +})); + +jest.mock('../PlaylistPage', () => ({ + PlaylistPage: jest.fn(() => null), +})); + +describe('Router', () => { + beforeEach(() => { + (PlaylistPage as jest.Mock).mockClear(); + (PlaylistIndexPage as jest.Mock).mockClear(); + }); + describe('/', () => { + it('should render the PlaylistIndexPage', async () => { + await renderInTestApp(); + expect(PlaylistIndexPage).toHaveBeenCalled(); + }); + }); + + describe('/:playlistId', () => { + it('should render the PlaylistPage page', async () => { + await renderInTestApp(, { + routeEntries: ['/my-playlist'], + }); + expect(PlaylistPage).toHaveBeenCalled(); + }); + }); +}); diff --git a/plugins/playlist/src/components/Router/Router.tsx b/plugins/playlist/src/components/Router/Router.tsx new file mode 100644 index 0000000000..5791a0817c --- /dev/null +++ b/plugins/playlist/src/components/Router/Router.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2022 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 { Routes, Route } from 'react-router'; + +import { playlistRouteRef } from '../../routes'; +import { PlaylistIndexPage } from '../PlaylistIndexPage'; +import { PlaylistPage } from '../PlaylistPage'; + +export const Router = () => ( + + } /> + } /> + +); diff --git a/plugins/playlist/src/components/Router/index.ts b/plugins/playlist/src/components/Router/index.ts new file mode 100644 index 0000000000..6606629fee --- /dev/null +++ b/plugins/playlist/src/components/Router/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './Router'; diff --git a/plugins/playlist/src/components/index.ts b/plugins/playlist/src/components/index.ts new file mode 100644 index 0000000000..2be300ac51 --- /dev/null +++ b/plugins/playlist/src/components/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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 './CreatePlaylistButton'; +export * from './EntityPlaylistDialog'; +export * from './PersonalListPicker'; +export * from './PlaylistCard'; +export * from './PlaylistEditDialog'; +export * from './PlaylistIndexPage'; +export * from './PlaylistList'; +export * from './PlaylistOwnerPicker'; +export * from './PlaylistPage'; +export * from './PlaylistSearchBar'; +export * from './PlaylistSortPicker'; diff --git a/plugins/playlist/src/hooks/index.ts b/plugins/playlist/src/hooks/index.ts new file mode 100644 index 0000000000..316121b515 --- /dev/null +++ b/plugins/playlist/src/hooks/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './usePlaylistList'; diff --git a/plugins/playlist/src/hooks/usePlaylistList.test.tsx b/plugins/playlist/src/hooks/usePlaylistList.test.tsx new file mode 100644 index 0000000000..d04996513e --- /dev/null +++ b/plugins/playlist/src/hooks/usePlaylistList.test.tsx @@ -0,0 +1,225 @@ +/* + * Copyright 2022 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 { + ConfigApi, + configApiRef, + IdentityApi, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { TestApiProvider } from '@backstage/test-utils'; +import { act, renderHook } from '@testing-library/react-hooks'; +import qs from 'qs'; +import React, { PropsWithChildren } from 'react'; +import { MemoryRouter } from 'react-router'; +import { PlaylistApi, playlistApiRef } from '../api'; +import { + DefaultSortCompareFunctions, + DefaultPlaylistSortTypes, + PersonalListFilter, + PersonalListFilterValue, + PersonalListPicker, + PlaylistOwnerPicker, +} from '../components'; +import { PlaylistListProvider, usePlaylistList } from './usePlaylistList'; + +const playlists: Playlist[] = [ + { + id: 'id1', + name: 'list-1', + owner: 'user:default/guest', + public: true, + entities: 2, + followers: 5, + isFollowing: false, + }, + { + id: 'id2', + name: 'list-2', + owner: 'group:default/foo', + public: true, + entities: 3, + followers: 2, + isFollowing: true, + }, +]; + +const mockConfigApi = { + getOptionalString: () => '', +} as Partial; + +const mockIdentityApi: Partial = { + getBackstageIdentity: async () => ({ + type: 'user', + userEntityRef: 'user:default/guest', + ownershipEntityRefs: [], + }), +}; + +const mockPlaylistApi: Partial = { + getAllPlaylists: jest.fn().mockImplementation(async () => playlists), +}; + +const wrapper = ({ + location, + children, +}: PropsWithChildren<{ + location?: string; +}>) => { + return ( + + + + + + {children} + + + + ); +}; + +describe('', () => { + const origReplaceState = window.history.replaceState; + beforeEach(() => { + window.history.replaceState = jest.fn(); + }); + afterEach(() => { + window.history.replaceState = origReplaceState; + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('resolves backend filters', async () => { + const { result, waitForValueToChange } = renderHook( + () => usePlaylistList(), + { + wrapper, + }, + ); + await waitForValueToChange(() => result.current.backendPlaylists); + expect(result.current.backendPlaylists.length).toBe(2); + expect(mockPlaylistApi.getAllPlaylists).toHaveBeenCalled(); + }); + + it('resolves frontend filters', async () => { + const { result, waitFor } = renderHook(() => usePlaylistList(), { + wrapper, + }); + await waitFor(() => !!result.current.playlists.length); + expect(result.current.backendPlaylists.length).toBe(2); + + act(() => + result.current.updateFilters({ + personal: new PersonalListFilter( + PersonalListFilterValue.owned, + playlist => playlist.name === 'list-1', + ), + }), + ); + + await waitFor(() => { + expect(result.current.backendPlaylists.length).toBe(2); + expect(result.current.playlists.length).toBe(1); + expect(mockPlaylistApi.getAllPlaylists).toHaveBeenCalledTimes(1); + }); + }); + + it('resolves query param filter values', async () => { + const query = qs.stringify({ + filters: { personal: 'all', owners: ['user:default/guest'] }, + }); + const { result, waitFor } = renderHook(() => usePlaylistList(), { + wrapper, + initialProps: { + location: `/playlist?${query}`, + }, + }); + await waitFor(() => !!result.current.queryParameters); + expect(result.current.queryParameters).toEqual({ + personal: 'all', + owners: ['user:default/guest'], + }); + }); + + it('does not fetch when only frontend filters change', async () => { + const { result, waitFor } = renderHook(() => usePlaylistList(), { + wrapper, + }); + + await waitFor(() => { + expect(result.current.playlists.length).toBe(2); + expect(mockPlaylistApi.getAllPlaylists).toHaveBeenCalledTimes(1); + }); + + act(() => + result.current.updateFilters({ + personal: new PersonalListFilter( + PersonalListFilterValue.owned, + playlist => playlist.name === 'list-1', + ), + }), + ); + + await waitFor(() => { + expect(result.current.playlists.length).toBe(1); + expect(mockPlaylistApi.getAllPlaylists).toHaveBeenCalledTimes(1); + }); + }); + + it('applies custom sorting', async () => { + const { result, waitFor } = renderHook(() => usePlaylistList(), { + wrapper, + }); + + await waitFor(() => { + expect(result.current.playlists.length).toBe(2); + expect(result.current.playlists[0].name).toEqual('list-1'); + expect(result.current.playlists[1].name).toEqual('list-2'); + }); + + act(() => + result.current.updateSort( + DefaultSortCompareFunctions[DefaultPlaylistSortTypes.numEntities], + ), + ); + + await waitFor(() => { + expect(result.current.playlists.length).toBe(2); + expect(result.current.playlists[0].name).toEqual('list-2'); + expect(result.current.playlists[1].name).toEqual('list-1'); + }); + }); + + it('returns an error on playlistApi failure', async () => { + mockPlaylistApi.getAllPlaylists = jest.fn().mockRejectedValue('error'); + const { result, waitFor } = renderHook(() => usePlaylistList(), { + wrapper, + }); + await waitFor(() => { + expect(result.current.error).toBeDefined(); + }); + }); +}); diff --git a/plugins/playlist/src/hooks/usePlaylistList.tsx b/plugins/playlist/src/hooks/usePlaylistList.tsx new file mode 100644 index 0000000000..93c0becbfd --- /dev/null +++ b/plugins/playlist/src/hooks/usePlaylistList.tsx @@ -0,0 +1,284 @@ +/* + * Copyright 2022 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 { useApi } from '@backstage/core-plugin-api'; +import { Playlist } from '@backstage/plugin-playlist-common'; +import { compact, isEqual } from 'lodash'; +import qs from 'qs'; +import React, { + createContext, + PropsWithChildren, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; +import { useLocation } from 'react-router'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; +import useDebounce from 'react-use/lib/useDebounce'; +import useMountedState from 'react-use/lib/useMountedState'; + +import { playlistApiRef } from '../api'; +import { PersonalListFilter } from '../components/PersonalListPicker'; +import { PlaylistOwnerFilter } from '../components/PlaylistOwnerPicker'; +import { PlaylistTextFilter } from '../components/PlaylistSearchBar'; +import { + DefaultPlaylistSortTypes, + DefaultSortCompareFunctions, +} from '../components/PlaylistSortPicker'; +import { PlaylistFilter, PlaylistSortCompareFunction } from '../types'; + +class NoopFilter implements PlaylistFilter { + getBackendFilters() { + return { '': null }; + } +} + +export type DefaultPlaylistFilters = { + noop?: NoopFilter; + owners?: PlaylistOwnerFilter; + personal?: PersonalListFilter; + text?: PlaylistTextFilter; +}; + +export type PlaylistListContextProps< + PlaylistFilters extends DefaultPlaylistFilters = DefaultPlaylistFilters, +> = { + /** + * The currently registered filters, adhering to the shape of DefaultPlaylistFilters + */ + filters: PlaylistFilters; + + /** + * The resolved list of playlists, after all filters are applied. + */ + playlists: Playlist[]; + + /** + * The resolved list of playlists, after _only playlist-backend_ filters are applied. + */ + backendPlaylists: Playlist[]; + + /** + * Update one or more of the registered filters. Optional filters can be set to `undefined` to + * reset the filter. + */ + updateFilters: ( + filters: + | Partial + | ((prevFilters: PlaylistFilters) => Partial), + ) => void; + + /** + * Update the compare function used to sort playlists. + */ + updateSort: (compareFn: PlaylistSortCompareFunction) => void; + + /** + * Filter values from query parameters. + */ + queryParameters: Partial>; + + loading: boolean; + error?: Error; +}; + +export const PlaylistListContext = createContext< + PlaylistListContextProps | undefined +>(undefined); + +const reduceBackendFilters = ( + filters: PlaylistFilter[], +): Record => { + return filters.reduce((compoundFilter, filter) => { + return { + ...compoundFilter, + ...(filter.getBackendFilters ? filter.getBackendFilters() : {}), + }; + }, {} as Record); +}; + +type OutputState = { + appliedFilters: PlaylistFilters; + playlists: Playlist[]; + backendPlaylists: Playlist[]; +}; + +export const PlaylistListProvider = < + PlaylistFilters extends DefaultPlaylistFilters, +>({ + children, +}: PropsWithChildren<{}>) => { + const isMounted = useMountedState(); + const playlistApi = useApi(playlistApiRef); + const [sortCompareFn, setSortCompareFn] = + useState( + () => DefaultSortCompareFunctions[DefaultPlaylistSortTypes.popular], + ); + const [requestedFilters, setRequestedFilters] = useState( + {} as PlaylistFilters, + ); + + // We use react-router's useLocation hook so updates from external sources trigger an update to + // the queryParameters in outputState. Updates from this hook use replaceState below and won't + // trigger a useLocation change; this would instead come from an external source, such as a manual + // update of the URL or two sidebar links with different filters. + const location = useLocation(); + const queryParameters = useMemo( + () => + (qs.parse(location.search, { + ignoreQueryPrefix: true, + }).filters ?? {}) as Record, + [location], + ); + + const [outputState, setOutputState] = useState>({ + appliedFilters: { + noop: new NoopFilter(), // Init with a noop filter to trigger intial request + } as PlaylistFilters, + playlists: [], + backendPlaylists: [], + }); + + // The main async filter worker. Note that while it has a lot of dependencies + // in terms of its implementation, the triggering only happens (debounced) + // based on the requested filters/sortCompareFn changing. + const [{ loading, error }, refresh] = useAsyncFn( + async () => { + const compacted: PlaylistFilter[] = compact( + Object.values(requestedFilters), + ); + const playlistFilter = (p: Playlist) => + compacted.every( + filter => !filter.filterPlaylist || filter.filterPlaylist(p), + ); + const backendFilter = reduceBackendFilters(compacted); + const previousBackendFilter = reduceBackendFilters( + compact(Object.values(outputState.appliedFilters)), + ); + + const queryParams = Object.keys(requestedFilters).reduce( + (params, key) => { + const filter: PlaylistFilter | undefined = + requestedFilters[key as keyof PlaylistFilters]; + if (filter?.toQueryValue) { + params[key] = filter.toQueryValue(); + } + return params; + }, + {} as Record, + ); + + if (!isEqual(previousBackendFilter, backendFilter)) { + const response = await playlistApi.getAllPlaylists({ + filter: backendFilter, + }); + setOutputState({ + appliedFilters: requestedFilters, + backendPlaylists: response, + playlists: response.filter(playlistFilter).sort(sortCompareFn), + }); + } else { + setOutputState({ + appliedFilters: requestedFilters, + backendPlaylists: outputState.backendPlaylists, + playlists: outputState.backendPlaylists + .filter(playlistFilter) + .sort(sortCompareFn), + }); + } + + if (isMounted()) { + const oldParams = qs.parse(location.search, { + ignoreQueryPrefix: true, + }); + const newParams = qs.stringify( + { ...oldParams, filters: queryParams }, + { addQueryPrefix: true, arrayFormat: 'repeat' }, + ); + const newUrl = `${window.location.pathname}${newParams}`; + // We use direct history manipulation since useSearchParams and + // useNavigate in react-router-dom cause unnecessary extra rerenders. + // Also make sure to replace the state rather than pushing, since we + // don't want there to be back/forward slots for every single filter + // change. + window.history?.replaceState(null, document.title, newUrl); + } + }, + [ + playlistApi, + queryParameters, + requestedFilters, + sortCompareFn, + outputState, + ], + { loading: true }, + ); + + // Slight debounce on the refresh, since (especially on page load) several + // filters will be calling this in rapid succession. + useDebounce(refresh, 10, [requestedFilters, sortCompareFn]); + + const updateFilters = useCallback( + ( + update: + | Partial + | ((prevFilters: PlaylistFilters) => Partial), + ) => { + setRequestedFilters(prevFilters => { + const newFilters = + typeof update === 'function' ? update(prevFilters) : update; + return { ...prevFilters, ...newFilters }; + }); + }, + [], + ); + + const updateSort = useCallback( + (compareFn: PlaylistSortCompareFunction) => + setSortCompareFn(() => compareFn), + [], + ); + + const value = useMemo( + () => ({ + filters: outputState.appliedFilters, + playlists: outputState.playlists, + backendPlaylists: outputState.backendPlaylists, + updateFilters, + updateSort, + queryParameters, + loading, + error, + }), + [outputState, updateFilters, updateSort, queryParameters, loading, error], + ); + + return ( + + {children} + + ); +}; + +export function usePlaylistList< + PlaylistFilters extends DefaultPlaylistFilters = DefaultPlaylistFilters, +>(): PlaylistListContextProps { + const context = useContext(PlaylistListContext); + if (!context) + throw new Error('usePlaylistList must be used within PlaylistListProvider'); + return context; +} diff --git a/plugins/playlist/src/index.ts b/plugins/playlist/src/index.ts new file mode 100644 index 0000000000..3b7004ce22 --- /dev/null +++ b/plugins/playlist/src/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2022 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. + */ + +/** + * Playlist frontend plugin + * + * @packageDocumentation + */ +export type { EntityPlaylistDialogProps } from './components'; +export { + EntityPlaylistDialog, + playlistPlugin, + PlaylistIndexPage, +} from './plugin'; +export * from './api'; diff --git a/plugins/playlist/src/plugin.test.ts b/plugins/playlist/src/plugin.test.ts new file mode 100644 index 0000000000..a2dfc7fcd7 --- /dev/null +++ b/plugins/playlist/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2022 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 { playlistPlugin } from './plugin'; + +describe('playlist', () => { + it('should export plugin', () => { + expect(playlistPlugin).toBeDefined(); + }); +}); diff --git a/plugins/playlist/src/plugin.ts b/plugins/playlist/src/plugin.ts new file mode 100644 index 0000000000..8e73fffe37 --- /dev/null +++ b/plugins/playlist/src/plugin.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2022 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 { + createApiFactory, + createComponentExtension, + createPlugin, + createRoutableExtension, + discoveryApiRef, + fetchApiRef, +} from '@backstage/core-plugin-api'; + +import { playlistApiRef, PlaylistClient } from './api'; +import { rootRouteRef } from './routes'; + +/** + * @public + */ +export const playlistPlugin = createPlugin({ + id: 'playlist', + routes: { + root: rootRouteRef, + }, + apis: [ + createApiFactory({ + api: playlistApiRef, + deps: { + discoveryApi: discoveryApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ discoveryApi, fetchApi }) => + new PlaylistClient({ discoveryApi, fetchApi }), + }), + ], +}); + +/** + * @public + */ +export const PlaylistIndexPage = playlistPlugin.provide( + createRoutableExtension({ + name: 'PlaylistIndexPage', + component: () => import('./components/Router').then(m => m.Router), + mountPoint: rootRouteRef, + }), +); + +/** + * @public + */ +export const EntityPlaylistDialog = playlistPlugin.provide( + createComponentExtension({ + name: 'EntityPlaylistDialog', + component: { + lazy: () => + import('./components/EntityPlaylistDialog').then( + m => m.EntityPlaylistDialog, + ), + }, + }), +); diff --git a/plugins/playlist/src/routes.ts b/plugins/playlist/src/routes.ts new file mode 100644 index 0000000000..632dc06439 --- /dev/null +++ b/plugins/playlist/src/routes.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2022 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 { createRouteRef, createSubRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'playlist:index-page', +}); + +export const playlistRouteRef = createSubRouteRef({ + id: 'playlist:playlist-page', + parent: rootRouteRef, + path: '/:playlistId', +}); diff --git a/plugins/playlist/src/setupTests.ts b/plugins/playlist/src/setupTests.ts new file mode 100644 index 0000000000..9bb3e72355 --- /dev/null +++ b/plugins/playlist/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/playlist/src/testUtils/MockPlaylistListProvider.tsx b/plugins/playlist/src/testUtils/MockPlaylistListProvider.tsx new file mode 100644 index 0000000000..2acef15e14 --- /dev/null +++ b/plugins/playlist/src/testUtils/MockPlaylistListProvider.tsx @@ -0,0 +1,102 @@ +/* + * Copyright 2022 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, { + PropsWithChildren, + useCallback, + useMemo, + useState, +} from 'react'; +import { + DefaultPlaylistSortTypes, + DefaultSortCompareFunctions, +} from '../components'; +import { + DefaultPlaylistFilters, + PlaylistListContext, + PlaylistListContextProps, +} from '../hooks'; +import { PlaylistSortCompareFunction } from '../types'; + +export const MockPlaylistListProvider = ({ + children, + value, +}: PropsWithChildren<{ + value?: Partial; +}>) => { + const [filters, setFilters] = useState( + value?.filters ?? {}, + ); + + const [_, setSortCompareFn] = useState( + () => DefaultSortCompareFunctions[DefaultPlaylistSortTypes.popular], + ); + + const updateFilters = useCallback( + ( + update: + | Partial + | (( + prevFilters: DefaultPlaylistFilters, + ) => Partial), + ) => { + setFilters(prevFilters => { + const newFilters = + typeof update === 'function' ? update(prevFilters) : update; + return { ...prevFilters, ...newFilters }; + }); + }, + [], + ); + + const updateSort = useCallback( + (compareFn: PlaylistSortCompareFunction) => + setSortCompareFn(() => compareFn), + [], + ); + + // Memoize the default values since pickers have useEffect triggers on these; naively defaulting + // below with `?? ` breaks referential equality on subsequent updates. + const defaultValues = useMemo( + () => ({ + playlists: [], + backendPlaylists: [], + queryParameters: {}, + }), + [], + ); + + const resolvedValue: PlaylistListContextProps = useMemo( + () => ({ + playlists: value?.playlists ?? defaultValues.playlists, + backendPlaylists: + value?.backendPlaylists ?? defaultValues.backendPlaylists, + updateFilters: value?.updateFilters ?? updateFilters, + filters, + updateSort: value?.updateSort ?? updateSort, + loading: value?.loading ?? false, + queryParameters: value?.queryParameters ?? defaultValues.queryParameters, + error: value?.error, + }), + [value, defaultValues, filters, updateFilters, updateSort], + ); + + return ( + + {children} + + ); +}; diff --git a/plugins/playlist/src/testUtils/index.ts b/plugins/playlist/src/testUtils/index.ts new file mode 100644 index 0000000000..9b6f333a59 --- /dev/null +++ b/plugins/playlist/src/testUtils/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 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 './MockPlaylistListProvider'; diff --git a/plugins/playlist/src/types.ts b/plugins/playlist/src/types.ts new file mode 100644 index 0000000000..fbd348f98a --- /dev/null +++ b/plugins/playlist/src/types.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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 { Playlist } from '@backstage/plugin-playlist-common'; + +export type PlaylistFilter = { + getBackendFilters?: () => Record; + + filterPlaylist?: (playlist: Playlist) => boolean; + + toQueryValue?: () => string | string[]; +}; + +export type PlaylistSortCompareFunction = (a: Playlist, b: Playlist) => number; diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 08e89d6910..329827d9de 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-proxy-backend +## 0.2.30 + +### Patch Changes + +- f6be17460d: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + ## 0.2.30-next.2 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 1870c1ec7d..553c43051d 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.2.30-next.2", + "version": "0.2.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -46,7 +46,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index 8f821f6e8f..6700252ae2 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-rollbar-backend +## 0.1.33 + +### Patch Changes + +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/config@1.0.2 + ## 0.1.33-next.3 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index eb3b74e69a..aca54df1b2 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar-backend", "description": "A Backstage backend plugin that integrates towards Rollbar", - "version": "0.1.33-next.3", + "version": "0.1.33", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,8 +33,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", "@types/express": "^4.17.6", "camelcase-keys": "^7.0.1", "compression": "^1.7.4", @@ -49,8 +49,8 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^0.47.0", "supertest": "^6.1.3" diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 6d1464aa02..c6c7e427ed 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-rollbar +## 0.4.9 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 7d47def9c4: Removed dependency on `@types/jest`. +- d669d89206: Minor API signatures cleanup +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/catalog-model@1.1.1 + ## 0.4.9-next.3 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 9cf5aa4964..6c31befc6b 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar", "description": "A Backstage plugin that integrates towards Rollbar", - "version": "0.4.9-next.3", + "version": "0.4.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/theme": "^0.2.16", + "@backstage/catalog-model": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -51,10 +51,10 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 77c0282940..e8f57c5426 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.11 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/integration@1.3.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.2.11-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 5c19a3d4cf..200b7bffa0 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", - "version": "0.2.11-next.2", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,19 +23,19 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-scaffolder-backend": "^1.6.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/types": "workspace:^", "command-exists": "^1.2.9", "fs-extra": "10.1.0", "winston": "^3.2.1", "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index ceb954110b..649d08dca0 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.4 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/integration@1.3.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index e60e643880..9418190ece 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", "description": "A module for the scaffolder backend that lets you template projects using Rails", - "version": "0.4.4-next.1", + "version": "0.4.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,17 +23,17 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-scaffolder-backend": "^1.6.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/types": "workspace:^", "command-exists": "^1.2.9", "fs-extra": "^10.0.1" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", + "@backstage/cli": "workspace:^", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 1769ad172e..6a0ddacab6 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.2.9 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.6.0 + - @backstage/config@1.0.2 + ## 0.2.9-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index eac08016f8..a7fa872f6d 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.2.9-next.1", + "version": "0.2.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,15 +22,15 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/config": "^1.0.2-next.0", - "@backstage/plugin-scaffolder-backend": "^1.6.0-next.3", - "@backstage/types": "^1.0.0", + "@backstage/config": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/types": "workspace:^", "winston": "^3.2.1", "yeoman-environment": "^3.9.1" }, "devDependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/cli": "^0.19.0-next.3" + "@backstage/backend-common": "workspace:^", + "@backstage/cli": "workspace:^" }, "files": [ "dist" diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index bf0b46c509..3c7624553c 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-scaffolder-backend +## 1.6.0 + +### Minor Changes + +- ea2eee9e6a: Add the option for a homepage when using the `github:publish` action +- 8872cc735d: Fixed a bug in plugin-scaffolder-backend where it ignores the skip migration database options. + + To use this new implementation you need to create the instance of `DatabaseTaskStore` using the `PluginDatabaseManager` instead of `Knex`; + + ``` + import { DatabaseManager, getRootLogger, loadBackendConfig } from '@backstage/backend-common'; + import { DatabaseTaskStore } from '@backstage/plugin-scaffolder-backend'; + + const config = await loadBackendConfig({ argv: process.argv, logger: getRootLogger() }); + const databaseManager = DatabaseManager.fromConfig(config, { migrations: { skip: true } }); + const databaseTaskStore = await DatabaseTaskStore.create(databaseManager); + ``` + +- 7db9613671: Added `projectId` for gitlab projects to be displayed in the `gitlab:publish` output +- d1f7ba58e3: Added `repositoryId` output when create a repository in Azure +- 1ff817b3f0: add entity metadata to the template info type + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- de8ee4afe3: Provide information about the user into scaffolder template action's context +- 096631e571: Added support for handling broken symlinks within the scaffolder backend. This is intended for templates that may hold a symlink that is invalid at build time but valid within the destination repo. +- 0d8d650e32: Applied the fix from version 1.5.1 of this package, which is part of the v1.5.1 release of Backstage. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 2df9955f4a: Removed the depreacated `publish:file` action, use the template editor to test templates instead. +- 0ecc9a6784: Properly set `ctx.isDryRun` when running actions in dry run mode. Also always log action inputs for debugging purposes when running in dry run mode. +- 6b9f6c0a4d: Added alpha `scaffolderPlugin` to be used with experimental backend system. +- 83c037cd46: Disable octokit throttling in publish:github:pull-request +- 2cbd533426: Uptake the `IdentityApi` change to use `getIdentity` instead of `authenticate` for retrieving the logged in users identity. +- ef9ab322de: Minor API signatures cleanup +- 50467bc15b: The number of task workers used to execute templates now default to 3, rather than 1. +- Updated dependencies + - @backstage/backend-plugin-api@0.1.2 + - @backstage/backend-common@0.15.1 + - @backstage/plugin-auth-node@0.2.5 + - @backstage/plugin-catalog-node@1.1.0 + - @backstage/integration@1.3.1 + - @backstage/plugin-catalog-backend@1.4.0 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/plugin-scaffolder-common@1.2.0 + ## 1.6.0-next.3 ### Patch Changes diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 0d5fbcb31f..35e3dab0b5 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -23,6 +23,7 @@ import { Logger } from 'winston'; import { Observable } from '@backstage/types'; import { Octokit } from 'octokit'; import { PluginDatabaseManager } from '@backstage/backend-common'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { Schema } from 'jsonschema'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ScmIntegrations } from '@backstage/integration'; @@ -499,6 +500,8 @@ export class DatabaseTaskStore implements TaskStore { taskId: string; }[]; }>; + // (undocumented) + shutdownTask({ taskId }: TaskStoreShutDownTaskOptions): Promise; } // @public @@ -547,6 +550,8 @@ export interface RouterOptions { // (undocumented) reader: UrlReader; // (undocumented) + scheduler?: PluginTaskScheduler; + // (undocumented) taskBroker?: TaskBroker; // (undocumented) taskWorkers?: number; @@ -741,6 +746,8 @@ export interface TaskStore { taskId: string; }[]; }>; + // (undocumented) + shutdownTask?({ taskId }: TaskStoreShutDownTaskOptions): Promise; } // @public @@ -767,6 +774,11 @@ export type TaskStoreListEventsOptions = { after?: number | undefined; }; +// @public +export type TaskStoreShutDownTaskOptions = { + taskId: string; +}; + // @public export class TaskWorker { // (undocumented) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 5b72ab2f07..35f7fe01e2 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "1.6.0-next.3", + "version": "1.6.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,18 +34,19 @@ "build:assets": "node scripts/build-nunjucks.js" }, "dependencies": { - "@backstage/backend-common": "^0.15.1-next.3", - "@backstage/backend-plugin-api": "^0.1.2-next.2", - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/plugin-auth-node": "^0.2.5-next.3", - "@backstage/plugin-catalog-backend": "^1.4.0-next.3", - "@backstage/plugin-catalog-node": "^1.1.0-next.2", - "@backstage/plugin-scaffolder-common": "^1.2.0-next.1", - "@backstage/types": "^1.0.0", + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/plugin-scaffolder-common": "workspace:^", + "@backstage/types": "workspace:^", "@gitbeaker/core": "^35.6.0", "@gitbeaker/node": "^35.1.0", "@octokit/webhooks": "^10.0.0", @@ -79,8 +80,8 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.28-next.3", - "@backstage/cli": "^0.19.0-next.3", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", "@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/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 206dc2ac1d..b61c3d2676 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -32,6 +32,7 @@ import { TaskStoreListEventsOptions, TaskStoreCreateTaskOptions, TaskStoreCreateTaskResult, + TaskStoreShutDownTaskOptions, } from './types'; import { DateTime } from 'luxon'; @@ -268,8 +269,7 @@ export class DatabaseTaskStore implements TaskStore { '<=', this.db.client.config.client.includes('sqlite3') ? this.db.raw(`datetime('now', ?)`, [`-${timeoutS} seconds`]) - : this.db.raw(`dateadd('second', ?, ?)`, [ - `-${timeoutS}`, + : this.db.raw(`? - interval '${timeoutS} seconds'`, [ this.db.fn.now(), ]), ); @@ -380,4 +380,42 @@ export class DatabaseTaskStore implements TaskStore { }); return { events }; } + + async shutdownTask({ taskId }: TaskStoreShutDownTaskOptions): Promise { + const message = `This task was marked as stale as it exceeded its timeout`; + + const statusStepEvents = (await this.listEvents({ taskId })).events.filter( + ({ body }) => body?.stepId, + ); + + const completedSteps = statusStepEvents + .filter( + ({ body: { status } }) => status === 'failed' || status === 'completed', + ) + .map(step => step.body.stepId); + + const hungProcessingSteps = statusStepEvents + .filter(({ body: { status } }) => status === 'processing') + .map(event => event.body.stepId) + .filter(step => !completedSteps.includes(step)); + + for (const step of hungProcessingSteps) { + await this.emitLogEvent({ + taskId, + body: { + message, + stepId: step, + status: 'failed', + }, + }); + } + + await this.completeTask({ + taskId, + status: 'failed', + eventBody: { + message, + }, + }); + } } diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index 8745b7afdf..24a35078e2 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -56,9 +56,11 @@ describe('DefaultWorkflowRunner', () => { const createMockTaskWithSpec = ( spec: TaskSpec, secrets?: TaskSecrets, + isDryRun?: boolean, ): TaskContext => ({ spec, secrets, + isDryRun, complete: async () => {}, done: false, emitLog: async () => {}, @@ -85,6 +87,7 @@ describe('DefaultWorkflowRunner', () => { actionRegistry.register({ id: 'jest-validated-action', description: 'Mock action for testing', + supportsDryRun: true, handler: fakeActionHandler, schema: { input: { @@ -640,4 +643,32 @@ describe('DefaultWorkflowRunner', () => { }); }); }); + + describe('dry run', () => { + it('sets isDryRun flag correctly', async () => { + const task = createMockTaskWithSpec( + { + apiVersion: 'scaffolder.backstage.io/v1beta3', + parameters: {}, + output: {}, + steps: [ + { + id: 'test', + name: 'name', + action: 'jest-validated-action', + input: { foo: 1 }, + }, + ], + }, + { + backstageToken: 'secret', + }, + true, + ); + + await runner.execute(task); + + expect(fakeActionHandler.mock.calls[0][0].isDryRun).toEqual(true); + }); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 6e35f7c69b..009ea1357d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -236,25 +236,53 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const action = this.options.actionRegistry.get(step.action); const { taskLogger, streamLogger } = createStepLogger({ task, step }); - if (task.isDryRun && !action.supportsDryRun) { - task.emitLog( - `Skipping because ${action.id} does not support dry-run`, - { - stepId: step.id, - status: 'skipped', - }, + if (task.isDryRun) { + const redactedSecrets = Object.fromEntries( + Object.entries(task.secrets ?? {}).map(secret => [ + secret[0], + '[REDACTED]', + ]), ); - const outputSchema = action.schema?.output; - if (outputSchema) { - context.steps[step.id] = { - output: generateExampleOutput(outputSchema) as { - [name in string]: JsonValue; + const debugInput = + (step.input && + this.render( + step.input, + { + ...context, + secrets: redactedSecrets, + }, + renderTemplate, + )) ?? + {}; + taskLogger.info( + `Running ${ + action.id + } in dry-run mode with inputs (secrets redacted): ${JSON.stringify( + debugInput, + undefined, + 2, + )}`, + ); + if (!action.supportsDryRun) { + task.emitLog( + `Skipping because ${action.id} does not support dry-run`, + { + stepId: step.id, + status: 'skipped', }, - }; - } else { - context.steps[step.id] = { output: {} }; + ); + const outputSchema = action.schema?.output; + if (outputSchema) { + context.steps[step.id] = { + output: generateExampleOutput(outputSchema) as { + [name in string]: JsonValue; + }, + }; + } else { + context.steps[step.id] = { output: {} }; + } + continue; } - continue; } // Secrets are only passed when templating the input to actions for security reasons @@ -301,6 +329,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { }, templateInfo: task.spec.templateInfo, user: task.spec.user, + isDryRun: task.isDryRun, }); // Remove all temporary directories that were created when executing the action diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts index 457c32d9b5..2af7d6a340 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/index.ts @@ -24,6 +24,7 @@ export type { TaskCompletionState, TaskStoreEmitOptions, TaskStoreListEventsOptions, + TaskStoreShutDownTaskOptions, SerializedTask, SerializedTaskEvent, TaskStatus, diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 4b63a0cdd3..5d3f8113ee 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -156,6 +156,15 @@ export type TaskStoreListEventsOptions = { after?: number | undefined; }; +/** + * TaskStoreShutDownTaskOptions + * + * @public + */ +export type TaskStoreShutDownTaskOptions = { + taskId: string; +}; + /** * The options passed to {@link TaskStore.createTask} * @public @@ -201,6 +210,7 @@ export interface TaskStore { taskId, after, }: TaskStoreListEventsOptions): Promise<{ events: SerializedTaskEvent[] }>; + shutdownTask?({ taskId }: TaskStoreShutDownTaskOptions): Promise; } export type WorkflowResponse = { output: { [key: string]: JsonValue } }; diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index f8e24d1acf..28dd1ca413 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -15,6 +15,7 @@ */ import { PluginDatabaseManager, UrlReader } from '@backstage/backend-common'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { CatalogApi } from '@backstage/catalog-client'; import { Entity, @@ -63,6 +64,7 @@ export interface RouterOptions { reader: UrlReader; database: PluginDatabaseManager; catalogClient: CatalogApi; + scheduler?: PluginTaskScheduler; actions?: TemplateAction[]; taskWorkers?: number; @@ -156,6 +158,7 @@ export async function createRouter( catalogClient, actions, taskWorkers, + scheduler, additionalTemplateFilters, } = options; @@ -166,11 +169,29 @@ export async function createRouter( const workingDirectory = await getWorkingDirectory(config, logger); const integrations = ScmIntegrations.fromConfig(config); - let taskBroker: TaskBroker; + let taskBroker: TaskBroker; if (!options.taskBroker) { const databaseTaskStore = await DatabaseTaskStore.create({ database }); taskBroker = new StorageTaskBroker(databaseTaskStore, logger); + + if (scheduler && databaseTaskStore.listStaleTasks) { + await scheduler.scheduleTask({ + id: 'close_stale_tasks', + frequency: { cron: '*/5 * * * *' }, // every 5 minutes, also supports Duration + timeout: { minutes: 15 }, + fn: async () => { + const { tasks } = await databaseTaskStore.listStaleTasks({ + timeoutS: 86400, + }); + + for (const task of tasks) { + await databaseTaskStore.shutdownTask(task); + logger.info(`Successfully closed stale task ${task.taskId}`); + } + }, + }); + } } else { taskBroker = options.taskBroker; } diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index f7cdcfe3b0..4d05c26cee 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-common +## 1.2.0 + +### Minor Changes + +- 1ff817b3f0: add entity metadata to the template info type + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.1 + ## 1.2.0-next.1 ### Patch Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 40ef29a3ae..ab7508d215 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-common", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", - "version": "1.2.0-next.1", + "version": "1.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -38,10 +38,10 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/types": "^1.0.0" + "@backstage/catalog-model": "workspace:^", + "@backstage/types": "workspace:^" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3" + "@backstage/cli": "workspace:^" } } diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index d30be1c8fc..2c1640c5e8 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-scaffolder +## 1.6.0 + +### Minor Changes + +- 3424a8075d: Added support for `async` validation for the `next` version of the plugin +- ad036784e9: Ability to modify the layout of the step form +- 192d856495: Implementing review step for the scaffolder under `create/next` + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 9ffb75616d: Fix bug with empty strings in `EntityPicker` +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- a66d44b72b: Fixing bug when the workspace would not be automatically saved when using `allowedOwners` +- 7d47def9c4: Removed dependency on `@types/jest`. +- 6522e459aa: Support displaying and ordering by counts in `EntityTagPicker` field. Add the `showCounts` option to enable this. Also support configuring `helperText`. +- f0510a20b5: Addition of a dismissible Error Banner in Scaffolder page +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- 9097278df2: Updated dependency `json-schema-library` to `^7.0.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- de336de9cd: fix uiSchema generation when using complex dependencies +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-permission-react@0.4.5 + - @backstage/integration@1.3.1 + - @backstage/catalog-client@1.1.0 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-scaffolder-common@1.2.0 + - @backstage/plugin-catalog-common@1.0.6 + ## 1.6.0-next.3 ### Patch Changes diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 9541098934..e88d2861a7 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -16,6 +16,7 @@ import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FetchApi } from '@backstage/core-plugin-api'; import { FieldProps } from '@rjsf/core'; import { FieldValidation } from '@rjsf/core'; +import type { FormProps } from '@rjsf/core'; import { IdentityApi } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; @@ -37,6 +38,11 @@ export function createScaffolderFieldExtension< options: FieldExtensionOptions, ): Extension>; +// @public +export function createScaffolderLayout( + options: LayoutOptions, +): Extension>; + // @public export type CustomFieldValidator = ( data: TFieldReturnValue, @@ -66,6 +72,8 @@ export interface EntityPickerUiOptions { allowedKinds?: string[]; // (undocumented) defaultKind?: string; + // (undocumented) + defaultNamespace?: string | false; } // @public @@ -110,6 +118,20 @@ export type FieldExtensionOptions< validation?: CustomFieldValidator; }; +// @public +export type LayoutComponent<_TInputProps> = () => null; + +// @public +export interface LayoutOptions

{ + // (undocumented) + component: LayoutTemplate

; + // (undocumented) + name: string; +} + +// @public +export type LayoutTemplate = FormProps['ObjectFieldTemplate']; + // @public export type ListActionsResponse = Array<{ id: string; @@ -163,6 +185,8 @@ export interface OwnedEntityPickerUiOptions { allowedKinds?: string[]; // (undocumented) defaultKind?: string; + // (undocumented) + defaultNamespace?: string | false; } // @public @@ -177,6 +201,8 @@ export interface OwnerPickerUiOptions { allowArbitraryValues?: boolean; // (undocumented) allowedKinds?: string[]; + // (undocumented) + defaultNamespace?: string | false; } // @public @@ -349,6 +375,9 @@ export interface ScaffolderGetIntegrationsListResponse { }[]; } +// @public +export const ScaffolderLayouts: React.ComponentType; + // @public (undocumented) export type ScaffolderOutputLink = { title?: string; diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index d0387ef4fe..0debc7dd23 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "1.6.0-next.3", + "version": "1.6.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,20 +33,20 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.1.0-next.2", - "@backstage/catalog-model": "^1.1.1-next.0", - "@backstage/config": "^1.0.2-next.0", - "@backstage/core-components": "^0.11.1-next.3", - "@backstage/core-plugin-api": "^1.0.6-next.3", - "@backstage/errors": "^1.1.1-next.0", - "@backstage/integration": "^1.3.1-next.2", - "@backstage/integration-react": "^1.1.4-next.2", - "@backstage/plugin-catalog-common": "^1.0.6-next.0", - "@backstage/plugin-catalog-react": "^1.1.4-next.2", - "@backstage/plugin-permission-react": "^0.4.5-next.2", - "@backstage/plugin-scaffolder-common": "^1.2.0-next.1", - "@backstage/theme": "^0.2.16", - "@backstage/types": "^1.0.0", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/integration-react": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/plugin-scaffolder-common": "workspace:^", + "@backstage/theme": "workspace:^", + "@backstage/types": "workspace:^", "@codemirror/language": "^6.0.0", "@codemirror/legacy-modes": "^6.1.0", "@codemirror/view": "^6.0.0", @@ -79,11 +79,11 @@ "react-router-dom": "6.0.0-beta.0 || ^6.3.0" }, "devDependencies": { - "@backstage/cli": "^0.19.0-next.3", - "@backstage/core-app-api": "^1.1.0-next.3", - "@backstage/dev-utils": "^1.0.6-next.2", - "@backstage/plugin-catalog": "^1.5.1-next.3", - "@backstage/test-utils": "^1.2.0-next.3", + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/plugin-catalog": "workspace:^", + "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index 43e12ae85f..8c71df67b6 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -36,6 +36,7 @@ import { transformSchemaToProps } from './schema'; import { Content, StructuredMetadataTable } from '@backstage/core-components'; import cloneDeep from 'lodash/cloneDeep'; import * as fieldOverrides from './FieldOverrides'; +import { LayoutOptions } from '../../layouts'; const Form = withTheme(MuiTheme); type Step = { @@ -55,6 +56,7 @@ type Props = { widgets?: FormProps['widgets']; fields?: FormProps['fields']; finishButtonLabel?: string; + layouts: LayoutOptions[]; }; export function getUiSchemasFromSteps(steps: Step[]): UiSchema[] { @@ -119,6 +121,7 @@ export const MultistepJsonForm = (props: Props) => { fields, widgets, finishButtonLabel, + layouts, } = props; const [activeStep, setActiveStep] = useState(0); const [disableButtons, setDisableButtons] = useState(false); @@ -214,7 +217,7 @@ export const MultistepJsonForm = (props: Props) => { if (e.errors.length === 0) handleNext(); }} {...formProps} - {...transformSchemaToProps(schema)} + {...transformSchemaToProps(schema, layouts)} >