diff --git a/.changeset/blue-forks-cry.md b/.changeset/blue-forks-cry.md new file mode 100644 index 0000000000..1bcc1d0b59 --- /dev/null +++ b/.changeset/blue-forks-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-aws-alb-provider': patch +--- + +Fix a bug where the signer was checked from the payload instead of the header diff --git a/.changeset/breezy-flowers-dance.md b/.changeset/breezy-flowers-dance.md new file mode 100644 index 0000000000..faa9fd0697 --- /dev/null +++ b/.changeset/breezy-flowers-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Deprecate the `featureDiscoveryServiceFactory` in favor of using `@backstage/backend-defaults#discoveryFeatureLoader` instead. diff --git a/.changeset/brown-actors-clap.md b/.changeset/brown-actors-clap.md new file mode 100644 index 0000000000..af25a6e87f --- /dev/null +++ b/.changeset/brown-actors-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +**BREAKING**: The deprecated identity and token manager services have been removed. This means that `coreServices.identity` and `coreServices.tokenManager` are gone, along with related types and utilities in other packages. diff --git a/.changeset/brown-worms-sneeze.md b/.changeset/brown-worms-sneeze.md new file mode 100644 index 0000000000..51359dd384 --- /dev/null +++ b/.changeset/brown-worms-sneeze.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-react': patch +--- + +Fixed issue in useShadowRootElements which could lead to unlimited render loops diff --git a/.changeset/calm-brooms-fail.md b/.changeset/calm-brooms-fail.md new file mode 100644 index 0000000000..a87b5ad38c --- /dev/null +++ b/.changeset/calm-brooms-fail.md @@ -0,0 +1,38 @@ +--- +'@backstage/backend-dynamic-feature-service': patch +--- + +Deprecate the `dynamicPluginsServiceRef`, `dynamicPluginsServiceFactory` and `dynamicPluginsServiceFactoryWithOptions` in favor of using the `dynamicPluginsFeatureDiscoveryLoader` to discover dynamic features in a new backend system. + +See usage examples below: + +Example using the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + +```ts +import { createBackend } from '@backstage/backend-defaults'; +import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; +//... + +const backend = createBackend(); +backend.add(dynamicPluginsFeatureDiscoveryLoader); +//... +backend.start(); +``` + +Passing options to the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + +```ts +import { createBackend } from '@backstage/backend-defaults'; +import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; +import { myCustomModuleLoader } from './myCustomModuleLoader'; +//... + +const backend = createBackend(); +backend.add( + dynamicPluginsFeatureDiscoveryLoader({ + moduleLoader: myCustomModuleLoader, + }), +); +//... +backend.start(); +``` diff --git a/.changeset/cold-ways-protect.md b/.changeset/cold-ways-protect.md new file mode 100644 index 0000000000..26b693fcc6 --- /dev/null +++ b/.changeset/cold-ways-protect.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-dynamic-feature-service': patch +--- + +Relax type check for a plugin's default export to also accept a BackendFeature defined as a function instead of an object diff --git a/.changeset/cool-melons-check.md b/.changeset/cool-melons-check.md new file mode 100644 index 0000000000..462a023dca --- /dev/null +++ b/.changeset/cool-melons-check.md @@ -0,0 +1,21 @@ +--- +'@backstage/frontend-test-utils': patch +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-search-react': patch +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-devtools': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-search': patch +'@backstage/plugin-home': patch +'@backstage/plugin-org': patch +--- + +Updated exports to use the new type parameters for extensions and extension blueprints. diff --git a/.changeset/cool-poems-hammer.md b/.changeset/cool-poems-hammer.md new file mode 100644 index 0000000000..fd5897343e --- /dev/null +++ b/.changeset/cool-poems-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': patch +--- + +Slight type tweak to match newer React versions better diff --git a/.changeset/cuddly-rocks-invent.md b/.changeset/cuddly-rocks-invent.md new file mode 100644 index 0000000000..c4ce8ebdb9 --- /dev/null +++ b/.changeset/cuddly-rocks-invent.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-dynamic-feature-service': minor +--- + +**BREAKING**: `dynamicPluginsServiceFactory` is no longer callable as a function. If you need to provide options to make a custom factory, use `dynamicPluginsSchemasServiceFactoryWithOptions` instead. diff --git a/.changeset/curly-brooms-raise.md b/.changeset/curly-brooms-raise.md new file mode 100644 index 0000000000..84c8892f7c --- /dev/null +++ b/.changeset/curly-brooms-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend': patch +--- + +Deprecate create router as the legacy backend system will no longer be supported. diff --git a/.changeset/dry-beers-shake.md b/.changeset/dry-beers-shake.md new file mode 100644 index 0000000000..59ff5ead54 --- /dev/null +++ b/.changeset/dry-beers-shake.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-defaults': minor +--- + +Initial release of this package, which provides a default app setup through the `createApp` function. This replaces the existing `createApp` method from `@backstage/frontend-app-api`. diff --git a/.changeset/dry-monkeys-mate.md b/.changeset/dry-monkeys-mate.md new file mode 100644 index 0000000000..51883ddefa --- /dev/null +++ b/.changeset/dry-monkeys-mate.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +The `KubernetesBuilder` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. diff --git a/.changeset/eighty-tables-hope.md b/.changeset/eighty-tables-hope.md new file mode 100644 index 0000000000..203cc8bb65 --- /dev/null +++ b/.changeset/eighty-tables-hope.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +Internal update to add support for passing an `ApiRegistry` when creating the node tree diff --git a/.changeset/famous-badgers-drop.md b/.changeset/famous-badgers-drop.md new file mode 100644 index 0000000000..af519cf964 --- /dev/null +++ b/.changeset/famous-badgers-drop.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Added missing service mock for `mockServices.rootConfig.mock`, and fixed the definition of `mockServices.rootHttpRouter.factory` to not have a duplicate callback. diff --git a/.changeset/five-tigers-share.md b/.changeset/five-tigers-share.md new file mode 100644 index 0000000000..507614c8b8 --- /dev/null +++ b/.changeset/five-tigers-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Found the issue during testing the clean up of the workspace for the database implementation. diff --git a/.changeset/flat-kangaroos-push.md b/.changeset/flat-kangaroos-push.md new file mode 100644 index 0000000000..b046beeeb1 --- /dev/null +++ b/.changeset/flat-kangaroos-push.md @@ -0,0 +1,31 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +**BREAKING**: Updated the type parameters for `ExtensionDefinition` and `ExtensionBlueprint` to only have a single object parameter. The base type parameter is exported as `ExtensionDefinitionParameters` and `ExtensionBlueprintParameters` respectively. This is shipped as an immediate breaking change as we expect usage of these types to be rare, and it does not affect the runtime behavior of the API. + +This is a breaking change as it changes the type parameters. Existing usage can generally be updated as follows: + +- `ExtensionDefinition` -> `ExtensionDefinition` +- `ExtensionDefinition` -> `ExtensionDefinition` +- `ExtensionDefinition` -> `ExtensionDefinition<{ config: TConfig }>` +- `ExtensionDefinition` -> `ExtensionDefinition<{ config: TConfig, configInput: TConfigInput }>` + +If you need to infer the parameter you can use `ExtensionDefinitionParameters`, for example: + +```ts +import { + ExtensionDefinition, + ExtensionDefinitionParameters, +} from '@backstage/frontend-plugin-api'; + +function myUtility( + ext: ExtensionDefinition, +): T['config'] { + // ... +} +``` + +The same patterns apply to `ExtensionBlueprint`. + +This change is made to improve the readability of API references and ability to evolve the type parameters in the future. diff --git a/.changeset/forty-toes-float.md b/.changeset/forty-toes-float.md new file mode 100644 index 0000000000..b6ca5139ea --- /dev/null +++ b/.changeset/forty-toes-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': minor +--- + +**BREAKING**: The deprecated `.render()` method has been removed from the extension tester. diff --git a/.changeset/four-parents-buy.md b/.changeset/four-parents-buy.md new file mode 100644 index 0000000000..ac216c3c08 --- /dev/null +++ b/.changeset/four-parents-buy.md @@ -0,0 +1,7 @@ +--- +'@backstage/frontend-app-api': minor +--- + +**BREAKING**: The `createSpecializedApp` function now creates a bare-bones app without any of the default app structure or APIs. To re-introduce this functionality if you need to use `createSpecializedApp` you can install the `app` plugin from `@backstage/plugin-app`. + +In addition, the `createApp` and `CreateAppFeatureLoader` exports are now deprecated as they are being moved to `@backstage/frontend-defaults`, which should be used instead. diff --git a/.changeset/four-ties-raise.md b/.changeset/four-ties-raise.md new file mode 100644 index 0000000000..163443afff --- /dev/null +++ b/.changeset/four-ties-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': minor +--- + +Add `ui:backstage.review.name` option for custom item names on scaffolder review page, and also add support for rendering the `title` property instead of the key name. diff --git a/.changeset/fresh-apes-dress.md b/.changeset/fresh-apes-dress.md new file mode 100644 index 0000000000..6e20e53bcd --- /dev/null +++ b/.changeset/fresh-apes-dress.md @@ -0,0 +1,8 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-test-utils': patch +'@backstage/frontend-app-api': patch +'@backstage/plugin-app': minor +--- + +Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. diff --git a/.changeset/fresh-bears-prove.md b/.changeset/fresh-bears-prove.md new file mode 100644 index 0000000000..a4098a4b18 --- /dev/null +++ b/.changeset/fresh-bears-prove.md @@ -0,0 +1,6 @@ +--- +'@backstage/cli': patch +'@backstage/create-app': patch +--- + +Update templates to not refer to backend-common diff --git a/.changeset/fresh-pumas-clean.md b/.changeset/fresh-pumas-clean.md new file mode 100644 index 0000000000..2140f62651 --- /dev/null +++ b/.changeset/fresh-pumas-clean.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-app-backend': patch +'@backstage/plugin-app-node': patch +--- + +Fixing dependency metadata with the new `@backstage/plugin-app` package diff --git a/.changeset/friendly-days-march.md b/.changeset/friendly-days-march.md new file mode 100644 index 0000000000..a190c50a43 --- /dev/null +++ b/.changeset/friendly-days-march.md @@ -0,0 +1,7 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Added `createFrontendModule` as a replacement for `createExtensionOverrides`, which is now deprecated. + +Deprecated the `BackstagePlugin` and `FrontendFeature` type in favor of `FrontendPlugin` and `FrontendFeature` from `@backstage/frontend-app-api` respectively. diff --git a/.changeset/friendly-months-march.md b/.changeset/friendly-months-march.md new file mode 100644 index 0000000000..0bb9cd7cf0 --- /dev/null +++ b/.changeset/friendly-months-march.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +--- + +Added support for new `FrontendPlugin` and `FrontendModule` types. diff --git a/.changeset/funny-houses-rest.md b/.changeset/funny-houses-rest.md new file mode 100644 index 0000000000..21efce8c19 --- /dev/null +++ b/.changeset/funny-houses-rest.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-backend': patch +--- + +The `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. diff --git a/.changeset/fuzzy-mails-walk.md b/.changeset/fuzzy-mails-walk.md new file mode 100644 index 0000000000..b46fbd59c4 --- /dev/null +++ b/.changeset/fuzzy-mails-walk.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': patch +--- + +Deprecate the `featureDiscoveryServiceRef` in favor of using the new `discoveryFeatureLoader` instead. diff --git a/.changeset/fuzzy-spies-share.md b/.changeset/fuzzy-spies-share.md new file mode 100644 index 0000000000..99108b81a0 --- /dev/null +++ b/.changeset/fuzzy-spies-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-signals-backend': patch +--- + +The `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. diff --git a/.changeset/giant-jars-mix.md b/.changeset/giant-jars-mix.md new file mode 100644 index 0000000000..652e23a280 --- /dev/null +++ b/.changeset/giant-jars-mix.md @@ -0,0 +1,6 @@ +--- +'@backstage/create-app': patch +'@backstage/plugin-devtools': patch +--- + +Minor dockerfile syntax update diff --git a/.changeset/gorgeous-scissors-wave.md b/.changeset/gorgeous-scissors-wave.md new file mode 100644 index 0000000000..1db406b940 --- /dev/null +++ b/.changeset/gorgeous-scissors-wave.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Updated the error message for missing service dependencies to include the plugin and module IDs. diff --git a/.changeset/green-worms-rescue.md b/.changeset/green-worms-rescue.md new file mode 100644 index 0000000000..992012ad7d --- /dev/null +++ b/.changeset/green-worms-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add `checks: 'read'` for default GitHub app permissions diff --git a/.changeset/healthy-moons-drum.md b/.changeset/healthy-moons-drum.md new file mode 100644 index 0000000000..0f61d0b141 --- /dev/null +++ b/.changeset/healthy-moons-drum.md @@ -0,0 +1,9 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +Removed the following deprecated exports + +- `BackendPluginConfig` use `CreateBackendPluginOptions` +- `BackendModuleConfig` use `CreateBackendModuleOptions` +- `ExtensionPointConfig` use `CreateExtensionPointOptions` diff --git a/.changeset/heavy-suits-judge.md b/.changeset/heavy-suits-judge.md new file mode 100644 index 0000000000..d5c00184c7 --- /dev/null +++ b/.changeset/heavy-suits-judge.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings-backend': patch +--- + +In preparation to stop supporting to the legacy backend system, the `createRouter` function is now deprecated and we strongly recommend you [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) your backend to the new system. diff --git a/.changeset/kind-moose-learn.md b/.changeset/kind-moose-learn.md new file mode 100644 index 0000000000..810064b1a1 --- /dev/null +++ b/.changeset/kind-moose-learn.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': minor +--- + +**BREAKING**: The deprecated `identityServiceFactory` and `tokenManagerServiceFactory` have been removed. diff --git a/.changeset/kind-walls-speak.md b/.changeset/kind-walls-speak.md new file mode 100644 index 0000000000..441e247dea --- /dev/null +++ b/.changeset/kind-walls-speak.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': minor +--- + +**BREAKING**: The backwards compatibility with plugins using legacy auth through the token manager service has been removed. This means that instead of falling back to using the old token manager, requests towards plugins that don't support the new auth system will simply fail. Please make sure that all plugins in your deployment are hosted within a backend instance from the new backend system. diff --git a/.changeset/large-poets-check.md b/.changeset/large-poets-check.md new file mode 100644 index 0000000000..393d26ac9b --- /dev/null +++ b/.changeset/large-poets-check.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch +'@backstage/plugin-search-backend-module-techdocs': patch +'@backstage/plugin-search-backend-module-catalog': patch +'@backstage/plugin-search-backend-module-explore': patch +'@backstage/plugin-permission-node': patch +'@backstage/plugin-signals-backend': patch +'@backstage/plugin-auth-backend': patch +--- + +Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. diff --git a/.changeset/loud-brooms-pull.md b/.changeset/loud-brooms-pull.md new file mode 100644 index 0000000000..8ed3e22c5b --- /dev/null +++ b/.changeset/loud-brooms-pull.md @@ -0,0 +1,8 @@ +--- +'@backstage/catalog-client': patch +'@backstage/plugin-catalog-backend': patch +--- + +Moved `getEntities` ordering to utilize database instead of having it inside catalog client + +Please note that the latest version of `@backstage/catalog-client` will not order the entities in the same way as before. This is because the ordering is now done in the database query instead of in the client. If you rely on the ordering of the entities, you may need to update your backend plugin or code to handle this change. diff --git a/.changeset/lucky-sheep-cover.md b/.changeset/lucky-sheep-cover.md new file mode 100644 index 0000000000..9b9420a73a --- /dev/null +++ b/.changeset/lucky-sheep-cover.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +The `legacyPlugin` and `makeLegacyPlugin` helpers now provide their own shim implementation of the identity and token manager services, as these services are being removed from the new backend system. diff --git a/.changeset/metal-garlics-fetch.md b/.changeset/metal-garlics-fetch.md new file mode 100644 index 0000000000..076b5f34a5 --- /dev/null +++ b/.changeset/metal-garlics-fetch.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-app-api': patch +--- + +Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the `app` extension to the respective API extensions. For example, extensions created with `ThemeBlueprint` are now attached to the `themes` input of `api:app-theme` rather than the `app` extension. diff --git a/.changeset/metal-rivers-grin.md b/.changeset/metal-rivers-grin.md new file mode 100644 index 0000000000..b70750c98d --- /dev/null +++ b/.changeset/metal-rivers-grin.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +There is a new `mockErrorHandler` utility to help in mocking the error middleware in tests. diff --git a/.changeset/mighty-cheetahs-call.md b/.changeset/mighty-cheetahs-call.md new file mode 100644 index 0000000000..f5d17d8b20 --- /dev/null +++ b/.changeset/mighty-cheetahs-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Move down the discovery config to be in the root diff --git a/.changeset/nasty-tigers-knock.md b/.changeset/nasty-tigers-knock.md new file mode 100644 index 0000000000..c48d725779 --- /dev/null +++ b/.changeset/nasty-tigers-knock.md @@ -0,0 +1,37 @@ +--- +'@backstage/backend-common': patch +'@backstage/backend-dynamic-feature-service': patch +'@backstage/plugin-app-backend': patch +'@backstage/plugin-catalog-backend-module-aws': 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-gerrit': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend-module-gitlab': patch +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-backend-module-puppetdb': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-events-backend-module-aws-sqs': patch +'@backstage/plugin-events-backend-module-azure': patch +'@backstage/plugin-events-backend-module-bitbucket-cloud': patch +'@backstage/plugin-events-backend-module-gerrit': patch +'@backstage/plugin-events-backend-module-github': patch +'@backstage/plugin-events-backend-module-gitlab': patch +'@backstage/plugin-events-backend': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-permission-backend': patch +'@backstage/plugin-proxy-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-search-backend-module-catalog': patch +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-search-backend-module-explore': patch +'@backstage/plugin-search-backend-module-pg': patch +'@backstage/plugin-search-backend-module-techdocs': patch +'@backstage/plugin-search-backend': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-user-settings-backend': patch +--- + +Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. diff --git a/.changeset/new-poets-unite.md b/.changeset/new-poets-unite.md new file mode 100644 index 0000000000..83172ffd66 --- /dev/null +++ b/.changeset/new-poets-unite.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +Make k8s entity content appear on components & resources only by default in new FE system diff --git a/.changeset/ninety-mayflies-raise.md b/.changeset/ninety-mayflies-raise.md new file mode 100644 index 0000000000..62042c2615 --- /dev/null +++ b/.changeset/ninety-mayflies-raise.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-search-backend-module-pg': patch +--- + +Internal refactor to use `LoggerService` and `DatabaseService` instead of the legacy `Logger` and `PluginDatabaseManager` types. diff --git a/.changeset/olive-phones-sniff.md b/.changeset/olive-phones-sniff.md new file mode 100644 index 0000000000..d70953435c --- /dev/null +++ b/.changeset/olive-phones-sniff.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Add `pg-format` as a dependency diff --git a/.changeset/popular-cooks-camp.md b/.changeset/popular-cooks-camp.md new file mode 100644 index 0000000000..4049825efd --- /dev/null +++ b/.changeset/popular-cooks-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': minor +--- + +**BREAKING**: Removed service mocks for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`. diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..492877ea8a --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,271 @@ +{ + "mode": "pre", + "tag": "next", + "initialVersions": { + "example-app": "0.2.100", + "@backstage/app-defaults": "1.5.10", + "example-app-next": "0.0.14", + "app-next-example-plugin": "0.0.14", + "example-backend": "0.0.29", + "@backstage/backend-app-api": "0.9.0", + "@backstage/backend-common": "0.24.0", + "@backstage/backend-defaults": "0.4.2", + "@backstage/backend-dev-utils": "0.1.5", + "@backstage/backend-dynamic-feature-service": "0.3.0", + "example-backend-legacy": "0.2.101", + "@backstage/backend-openapi-utils": "0.1.16", + "@backstage/backend-plugin-api": "0.8.0", + "@backstage/backend-test-utils": "0.5.0", + "@backstage/catalog-client": "1.6.6", + "@backstage/catalog-model": "1.6.0", + "@backstage/cli": "0.27.0", + "@backstage/cli-common": "0.1.14", + "@backstage/cli-node": "0.2.7", + "@backstage/codemods": "0.1.49", + "@backstage/config": "1.2.0", + "@backstage/config-loader": "1.9.0", + "@backstage/core-app-api": "1.14.2", + "@backstage/core-compat-api": "0.2.8", + "@backstage/core-components": "0.14.10", + "@backstage/core-plugin-api": "1.9.3", + "@backstage/create-app": "0.5.18", + "@backstage/dev-utils": "1.0.37", + "e2e-test": "0.2.19", + "@backstage/e2e-test-utils": "0.1.1", + "@backstage/errors": "1.2.4", + "@backstage/eslint-plugin": "0.1.8", + "@backstage/frontend-app-api": "0.8.0", + "@backstage/frontend-plugin-api": "0.7.0", + "@backstage/frontend-test-utils": "0.1.12", + "@backstage/integration": "1.14.0", + "@backstage/integration-aws-node": "0.1.12", + "@backstage/integration-react": "1.1.30", + "@backstage/release-manifests": "0.0.11", + "@backstage/repo-tools": "0.9.5", + "@techdocs/cli": "1.8.17", + "techdocs-cli-embedded-app": "0.2.99", + "@backstage/test-utils": "1.5.10", + "@backstage/theme": "0.5.6", + "@backstage/types": "1.1.1", + "@backstage/version-bridge": "1.0.8", + "yarn-plugin-backstage": "0.0.1", + "@backstage/plugin-api-docs": "0.11.8", + "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.7", + "@backstage/plugin-app-backend": "0.3.72", + "@backstage/plugin-app-node": "0.1.23", + "@backstage/plugin-app-visualizer": "0.1.9", + "@backstage/plugin-auth-backend": "0.22.10", + "@backstage/plugin-auth-backend-module-atlassian-provider": "0.2.4", + "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.1.15", + "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.1.6", + "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.1.6", + "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.2.0", + "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.2.18", + "@backstage/plugin-auth-backend-module-github-provider": "0.1.20", + "@backstage/plugin-auth-backend-module-gitlab-provider": "0.1.20", + "@backstage/plugin-auth-backend-module-google-provider": "0.1.20", + "@backstage/plugin-auth-backend-module-guest-provider": "0.1.9", + "@backstage/plugin-auth-backend-module-microsoft-provider": "0.1.18", + "@backstage/plugin-auth-backend-module-oauth2-provider": "0.2.4", + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.1.16", + "@backstage/plugin-auth-backend-module-oidc-provider": "0.2.4", + "@backstage/plugin-auth-backend-module-okta-provider": "0.0.16", + "@backstage/plugin-auth-backend-module-onelogin-provider": "0.1.4", + "@backstage/plugin-auth-backend-module-pinniped-provider": "0.1.17", + "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.2.4", + "@backstage/plugin-auth-node": "0.5.0", + "@backstage/plugin-auth-react": "0.1.5", + "@backstage/plugin-bitbucket-cloud-common": "0.2.22", + "@backstage/plugin-catalog": "1.22.0", + "@backstage/plugin-catalog-backend": "1.25.0", + "@backstage/plugin-catalog-backend-module-aws": "0.4.0", + "@backstage/plugin-catalog-backend-module-azure": "0.2.0", + "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.3.0", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.3.0", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.2.0", + "@backstage/plugin-catalog-backend-module-gcp": "0.2.0", + "@backstage/plugin-catalog-backend-module-gerrit": "0.2.0", + "@backstage/plugin-catalog-backend-module-github": "0.7.0", + "@backstage/plugin-catalog-backend-module-github-org": "0.2.0", + "@backstage/plugin-catalog-backend-module-gitlab": "0.4.0", + "@backstage/plugin-catalog-backend-module-gitlab-org": "0.1.0", + "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.5.0", + "@backstage/plugin-catalog-backend-module-ldap": "0.8.0", + "@backstage/plugin-catalog-backend-module-logs": "0.0.2", + "@backstage/plugin-catalog-backend-module-msgraph": "0.6.0", + "@backstage/plugin-catalog-backend-module-openapi": "0.1.41", + "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.0", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.1.21", + "@backstage/plugin-catalog-backend-module-unprocessed": "0.4.10", + "@backstage/plugin-catalog-common": "1.0.26", + "@backstage/plugin-catalog-graph": "0.4.8", + "@backstage/plugin-catalog-import": "0.12.2", + "@backstage/plugin-catalog-node": "1.12.5", + "@backstage/plugin-catalog-react": "1.12.3", + "@backstage/plugin-catalog-unprocessed-entities": "0.2.7", + "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.4", + "@backstage/plugin-config-schema": "0.1.58", + "@backstage/plugin-devtools": "0.1.17", + "@backstage/plugin-devtools-backend": "0.3.9", + "@backstage/plugin-devtools-common": "0.1.12", + "@backstage/plugin-events-backend": "0.3.10", + "@backstage/plugin-events-backend-module-aws-sqs": "0.4.0", + "@backstage/plugin-events-backend-module-azure": "0.2.9", + "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.9", + "@backstage/plugin-events-backend-module-gerrit": "0.2.9", + "@backstage/plugin-events-backend-module-github": "0.2.9", + "@backstage/plugin-events-backend-module-gitlab": "0.2.9", + "@backstage/plugin-events-backend-test-utils": "0.1.33", + "@backstage/plugin-events-node": "0.3.9", + "@internal/plugin-todo-list": "1.0.30", + "@internal/plugin-todo-list-backend": "1.0.30", + "@internal/plugin-todo-list-common": "1.0.21", + "@backstage/plugin-home": "0.7.9", + "@backstage/plugin-home-react": "0.1.16", + "@backstage/plugin-kubernetes": "0.11.13", + "@backstage/plugin-kubernetes-backend": "0.18.4", + "@backstage/plugin-kubernetes-cluster": "0.0.14", + "@backstage/plugin-kubernetes-common": "0.8.2", + "@backstage/plugin-kubernetes-node": "0.1.17", + "@backstage/plugin-kubernetes-react": "0.4.2", + "@backstage/plugin-notifications": "0.3.0", + "@backstage/plugin-notifications-backend": "0.3.4", + "@backstage/plugin-notifications-backend-module-email": "0.2.0", + "@backstage/plugin-notifications-common": "0.0.5", + "@backstage/plugin-notifications-node": "0.2.4", + "@backstage/plugin-org": "0.6.28", + "@backstage/plugin-org-react": "0.1.27", + "@backstage/plugin-permission-backend": "0.5.47", + "@backstage/plugin-permission-backend-module-allow-all-policy": "0.1.20", + "@backstage/plugin-permission-common": "0.8.1", + "@backstage/plugin-permission-node": "0.8.1", + "@backstage/plugin-permission-react": "0.4.25", + "@backstage/plugin-proxy-backend": "0.5.4", + "@backstage/plugin-scaffolder": "1.24.0", + "@backstage/plugin-scaffolder-backend": "1.24.0", + "@backstage/plugin-scaffolder-backend-module-azure": "0.1.15", + "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.2.13", + "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.1.13", + "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.1.13", + "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.24", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.47", + "@backstage/plugin-scaffolder-backend-module-gcp": "0.1.1", + "@backstage/plugin-scaffolder-backend-module-gerrit": "0.1.15", + "@backstage/plugin-scaffolder-backend-module-gitea": "0.1.13", + "@backstage/plugin-scaffolder-backend-module-github": "0.4.1", + "@backstage/plugin-scaffolder-backend-module-gitlab": "0.4.5", + "@backstage/plugin-scaffolder-backend-module-notifications": "0.0.6", + "@backstage/plugin-scaffolder-backend-module-rails": "0.4.40", + "@backstage/plugin-scaffolder-backend-module-sentry": "0.1.31", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.3.7", + "@backstage/plugin-scaffolder-common": "1.5.5", + "@backstage/plugin-scaffolder-node": "0.4.9", + "@backstage/plugin-scaffolder-node-test-utils": "0.1.10", + "@backstage/plugin-scaffolder-react": "1.11.0", + "@backstage/plugin-search": "1.4.15", + "@backstage/plugin-search-backend": "1.5.15", + "@backstage/plugin-search-backend-module-catalog": "0.2.0", + "@backstage/plugin-search-backend-module-elasticsearch": "1.5.4", + "@backstage/plugin-search-backend-module-explore": "0.2.0", + "@backstage/plugin-search-backend-module-pg": "0.5.33", + "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.2.0", + "@backstage/plugin-search-backend-module-techdocs": "0.2.0", + "@backstage/plugin-search-backend-node": "1.3.0", + "@backstage/plugin-search-common": "1.2.14", + "@backstage/plugin-search-react": "1.7.14", + "@backstage/plugin-signals": "0.0.9", + "@backstage/plugin-signals-backend": "0.1.9", + "@backstage/plugin-signals-node": "0.1.9", + "@backstage/plugin-signals-react": "0.0.4", + "@backstage/plugin-techdocs": "1.10.8", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.37", + "@backstage/plugin-techdocs-backend": "1.10.10", + "@backstage/plugin-techdocs-common": "0.1.0", + "@backstage/plugin-techdocs-module-addons-contrib": "1.1.13", + "@backstage/plugin-techdocs-node": "1.12.9", + "@backstage/plugin-techdocs-react": "1.2.7", + "@backstage/plugin-user-settings": "0.8.11", + "@backstage/plugin-user-settings-backend": "0.2.22", + "@backstage/plugin-user-settings-common": "0.0.1", + "@backstage/plugin-app": "0.0.0" + }, + "changesets": [ + "big-spies-stare", + "blue-forks-cry", + "breezy-flowers-dance", + "brown-actors-clap", + "brown-boxes-arrive", + "brown-worms-sneeze", + "calm-brooms-fail", + "cold-ways-protect", + "cool-melons-check", + "cool-poems-hammer", + "cuddly-rocks-invent", + "curly-brooms-raise", + "dry-monkeys-mate", + "eighty-tables-hope", + "empty-coats-sparkle", + "five-tigers-share", + "flat-kangaroos-push", + "forty-toes-float", + "four-ties-raise", + "fresh-apes-dress", + "fresh-pumas-clean", + "funny-houses-rest", + "fuzzy-mails-walk", + "fuzzy-spies-share", + "giant-jars-mix", + "green-worms-rescue", + "healthy-moons-drum", + "heavy-suits-judge", + "hip-pandas-think", + "kind-moose-learn", + "kind-walls-speak", + "large-poets-check", + "lucky-sheep-cover", + "metal-garlics-fetch", + "metal-rivers-grin", + "mighty-days-kiss", + "nasty-tigers-knock", + "new-poets-unite", + "ninety-mayflies-raise", + "olive-phones-sniff", + "popular-cooks-camp", + "purple-cows-sing", + "purple-glasses-tease", + "rare-feet-melt", + "real-pants-rule", + "serious-cheetahs-help", + "sharp-fans-tan", + "sharp-items-study", + "sharp-mayflies-beg", + "shiny-zoos-film", + "short-moles-brush", + "silent-eyes-lie", + "six-goats-sort", + "six-humans-guess", + "sixty-rabbits-cheat", + "spicy-poems-hammer", + "spicy-vans-eat", + "sweet-cows-clean", + "swift-garlics-mix", + "swift-radios-enjoy", + "swift-seahorses-share", + "tame-hornets-shake", + "tasty-pigs-vanish", + "thick-walls-share", + "tiny-waves-provide", + "tough-peaches-kneel", + "tricky-apricots-film", + "twenty-clouds-melt", + "twenty-queens-grow", + "unlucky-cycles-clean", + "violet-jokes-tap", + "warm-boxes-grab-2", + "warm-boxes-grab", + "wild-buses-notice", + "wise-scissors-help", + "witty-years-cry", + "yellow-bees-hope" + ] +} diff --git a/.changeset/purple-cows-sing.md b/.changeset/purple-cows-sing.md new file mode 100644 index 0000000000..3391ced648 --- /dev/null +++ b/.changeset/purple-cows-sing.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +Removed deprecated `ServiceFactoryOrFunction` type. diff --git a/.changeset/purple-glasses-tease.md b/.changeset/purple-glasses-tease.md new file mode 100644 index 0000000000..5a107c1a2b --- /dev/null +++ b/.changeset/purple-glasses-tease.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-search-backend-module-techdocs': patch +'@backstage/plugin-search-backend-module-catalog': patch +'@backstage/plugin-search-backend-module-explore': patch +--- + +The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module: + +- `DefaultCatalogCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-catalog/README.md#installation; +- `ToolDocumentCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-explore/README.md#installation; +- `DefaultTechDocsCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-techdocs/README.md#installation. diff --git a/.changeset/rare-feet-melt.md b/.changeset/rare-feet-melt.md new file mode 100644 index 0000000000..b28b09a29d --- /dev/null +++ b/.changeset/rare-feet-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings-backend': patch +--- + +Replaced usage of the deprecated identity service with the new HTTP auth service for the new backend system. diff --git a/.changeset/real-pants-rule.md b/.changeset/real-pants-rule.md new file mode 100644 index 0000000000..8d6d3dc708 --- /dev/null +++ b/.changeset/real-pants-rule.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +--- + +Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. diff --git a/.changeset/renovate-53b7d25.md b/.changeset/renovate-53b7d25.md new file mode 100644 index 0000000000..0c4f7255d3 --- /dev/null +++ b/.changeset/renovate-53b7d25.md @@ -0,0 +1,6 @@ +--- +'@backstage/cli': patch +'@backstage/plugin-scaffolder-backend': patch +--- + +Updated dependency `esbuild` to `^0.23.0`. diff --git a/.changeset/renovate-93d032c.md b/.changeset/renovate-93d032c.md new file mode 100644 index 0000000000..b34e5a2e23 --- /dev/null +++ b/.changeset/renovate-93d032c.md @@ -0,0 +1,5 @@ +--- +'@backstage/codemods': patch +--- + +Updated dependency `jscodeshift` to `^0.16.0`. diff --git a/.changeset/rich-bees-tickle.md b/.changeset/rich-bees-tickle.md new file mode 100644 index 0000000000..a2af89c867 --- /dev/null +++ b/.changeset/rich-bees-tickle.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Wrap scheduled tasks from the scheduler core service now in OpenTelemetry spans diff --git a/.changeset/rotten-crabs-hear.md b/.changeset/rotten-crabs-hear.md new file mode 100644 index 0000000000..69148c59c1 --- /dev/null +++ b/.changeset/rotten-crabs-hear.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend': patch +--- + +Validate notification link when new notification is created diff --git a/.changeset/serious-spies-knock.md b/.changeset/serious-spies-knock.md new file mode 100644 index 0000000000..1484753019 --- /dev/null +++ b/.changeset/serious-spies-knock.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Skip start without proper config diff --git a/.changeset/sharp-fans-tan.md b/.changeset/sharp-fans-tan.md new file mode 100644 index 0000000000..add4a85758 --- /dev/null +++ b/.changeset/sharp-fans-tan.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +- Fix secret widget field not displaying as required. +- Fix secret widget not able to be required inside nested objects. +- Fix secret widget not able to be disabled. +- Support `minLength` and `maxLength` properties for secret widget. diff --git a/.changeset/sharp-items-study.md b/.changeset/sharp-items-study.md new file mode 100644 index 0000000000..61e8af54b6 --- /dev/null +++ b/.changeset/sharp-items-study.md @@ -0,0 +1,19 @@ +--- +'@backstage/backend-defaults': patch +--- + +Exports the `discoveryFeatureLoader` as a replacement for the deprecated `featureDiscoveryService`. +The `discoveryFeatureLoader` is a new backend system [feature loader](https://backstage.io/docs/backend-system/architecture/feature-loaders/) that discovers backend features from the current `package.json` and its dependencies. +Here is an example using the `discoveryFeatureLoader` loader in a new backend instance: + +```ts +import { createBackend } from '@backstage/backend-defaults'; +import { discoveryFeatureLoader } from '@backstage/backend-defaults'; +//... + +const backend = createBackend(); +//... +backend.add(discoveryFeatureLoader); +//... +backend.start(); +``` diff --git a/.changeset/shiny-zoos-film.md b/.changeset/shiny-zoos-film.md new file mode 100644 index 0000000000..f9fb8cdce5 --- /dev/null +++ b/.changeset/shiny-zoos-film.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-signals': patch +--- + +Added a `SignalsDisplay` extension to allows the signals plugin to be installed in an app as follows: + +```tsx +export default app.createRoot( + <> + + + + + + {routes} + + , +); +``` + +With this in place you can remove the explicit installation via the `plugins` option for `createApp`. diff --git a/.changeset/short-moles-brush.md b/.changeset/short-moles-brush.md new file mode 100644 index 0000000000..033805c2b5 --- /dev/null +++ b/.changeset/short-moles-brush.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +`createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. diff --git a/.changeset/silent-eyes-lie.md b/.changeset/silent-eyes-lie.md new file mode 100644 index 0000000000..e9da3d2685 --- /dev/null +++ b/.changeset/silent-eyes-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/test-utils': minor +--- + +Added the icons option to the renderInTestApp function's TestAppOptions to be forwarded to the icons option of `createApp`. diff --git a/.changeset/six-goats-sort.md b/.changeset/six-goats-sort.md new file mode 100644 index 0000000000..5df8924066 --- /dev/null +++ b/.changeset/six-goats-sort.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +Remove dependency on backend-common diff --git a/.changeset/six-humans-guess.md b/.changeset/six-humans-guess.md new file mode 100644 index 0000000000..60845257c8 --- /dev/null +++ b/.changeset/six-humans-guess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-proxy-backend': patch +--- + +Deprecated `createRouter` and its router options in favour of the new backend system. diff --git a/.changeset/sixty-rabbits-cheat.md b/.changeset/sixty-rabbits-cheat.md new file mode 100644 index 0000000000..0161e272ed --- /dev/null +++ b/.changeset/sixty-rabbits-cheat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': minor +--- + +Make use of the `useApp` hook to retrieve the specified search icon in the SearchBar diff --git a/.changeset/smart-owls-sell.md b/.changeset/smart-owls-sell.md new file mode 100644 index 0000000000..0a2b555a20 --- /dev/null +++ b/.changeset/smart-owls-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Properly indent the config schema diff --git a/.changeset/spicy-poems-hammer.md b/.changeset/spicy-poems-hammer.md new file mode 100644 index 0000000000..23e86e178e --- /dev/null +++ b/.changeset/spicy-poems-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +As the `@backstage/backend-common` package is deprecated, we have updated the `techdocs-node` package to stop depending on it. diff --git a/.changeset/spicy-vans-eat.md b/.changeset/spicy-vans-eat.md new file mode 100644 index 0000000000..037a5f18e0 --- /dev/null +++ b/.changeset/spicy-vans-eat.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Fix feature loaders in CJS double-default nested builds diff --git a/.changeset/sweet-cows-clean.md b/.changeset/sweet-cows-clean.md new file mode 100644 index 0000000000..fa2004186e --- /dev/null +++ b/.changeset/sweet-cows-clean.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +Added the ability to provide additional `extensions` and `features` to `renderInTestApp` diff --git a/.changeset/swift-garlics-mix.md b/.changeset/swift-garlics-mix.md new file mode 100644 index 0000000000..fc0993be3f --- /dev/null +++ b/.changeset/swift-garlics-mix.md @@ -0,0 +1,63 @@ +--- +'@backstage/backend-app-api': minor +'@backstage/backend-common': minor +'@backstage/backend-defaults': minor +'@backstage/backend-plugin-api': minor +'@backstage/backend-test-utils': minor +'@backstage/plugin-auth-backend-module-atlassian-provider': minor +'@backstage/plugin-auth-backend-module-aws-alb-provider': minor +'@backstage/plugin-auth-backend-module-azure-easyauth-provider': minor +'@backstage/plugin-auth-backend-module-bitbucket-provider': minor +'@backstage/plugin-auth-backend-module-cloudflare-access-provider': minor +'@backstage/plugin-auth-backend-module-gcp-iap-provider': minor +'@backstage/plugin-auth-backend-module-github-provider': minor +'@backstage/plugin-auth-backend-module-gitlab-provider': minor +'@backstage/plugin-auth-backend-module-google-provider': minor +'@backstage/plugin-auth-backend-module-guest-provider': minor +'@backstage/plugin-auth-backend-module-microsoft-provider': minor +'@backstage/plugin-auth-backend-module-oauth2-provider': minor +'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': minor +'@backstage/plugin-auth-backend-module-oidc-provider': minor +'@backstage/plugin-auth-backend-module-okta-provider': minor +'@backstage/plugin-auth-backend-module-onelogin-provider': minor +'@backstage/plugin-auth-backend-module-pinniped-provider': minor +'@backstage/plugin-auth-backend-module-vmware-cloud-provider': minor +'@backstage/plugin-auth-backend': minor +'@backstage/plugin-catalog-backend-module-backstage-openapi': minor +'@backstage/plugin-catalog-backend-module-gcp': minor +'@backstage/plugin-catalog-backend-module-github-org': minor +'@backstage/plugin-catalog-backend-module-gitlab-org': minor +'@backstage/plugin-catalog-backend-module-ldap': minor +'@backstage/plugin-catalog-backend-module-logs': minor +'@backstage/plugin-catalog-backend-module-openapi': minor +'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': minor +'@backstage/plugin-catalog-backend-module-unprocessed': minor +'@backstage/plugin-devtools-backend': minor +'@backstage/plugin-events-node': minor +'@backstage/plugin-notifications-backend-module-email': minor +'@backstage/plugin-notifications-backend': minor +'@backstage/plugin-permission-backend-module-allow-all-policy': minor +'@backstage/plugin-scaffolder-backend-module-azure': minor +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': minor +'@backstage/plugin-scaffolder-backend-module-bitbucket-server': minor +'@backstage/plugin-scaffolder-backend-module-bitbucket': minor +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': minor +'@backstage/plugin-scaffolder-backend-module-cookiecutter': minor +'@backstage/plugin-scaffolder-backend-module-gcp': minor +'@backstage/plugin-scaffolder-backend-module-gerrit': minor +'@backstage/plugin-scaffolder-backend-module-gitea': minor +'@backstage/plugin-scaffolder-backend-module-github': minor +'@backstage/plugin-scaffolder-backend-module-gitlab': minor +'@backstage/plugin-scaffolder-backend-module-notifications': minor +'@backstage/plugin-scaffolder-backend-module-rails': minor +'@backstage/plugin-scaffolder-backend-module-sentry': minor +'@backstage/plugin-scaffolder-backend-module-yeoman': minor +'@backstage/plugin-search-backend-module-stack-overflow-collator': minor +'@backstage/plugin-signals-backend': minor +--- + +**BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + +This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + +As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. diff --git a/.changeset/swift-radios-enjoy.md b/.changeset/swift-radios-enjoy.md new file mode 100644 index 0000000000..495117e3c6 --- /dev/null +++ b/.changeset/swift-radios-enjoy.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Internal refactor to re-declare the token manager service which was removed from `@backstage/backend-plugin-api`, but is still supported in this package for backwards compatibility. diff --git a/.changeset/swift-seahorses-share.md b/.changeset/swift-seahorses-share.md new file mode 100644 index 0000000000..deaf2ab042 --- /dev/null +++ b/.changeset/swift-seahorses-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +Updated the README to include documentation for the new backend support diff --git a/.changeset/tall-camels-march.md b/.changeset/tall-camels-march.md new file mode 100644 index 0000000000..892a8f19af --- /dev/null +++ b/.changeset/tall-camels-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-signals': patch +--- + +Put a name on the `SignalsDisplay` component extension diff --git a/.changeset/tame-hornets-shake.md b/.changeset/tame-hornets-shake.md new file mode 100644 index 0000000000..bf96193b55 --- /dev/null +++ b/.changeset/tame-hornets-shake.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fix an issue where keys with duplicate final key parts are not all displayed in the `ReviewState`. Change the way the keys are formatted to include the full schema path, separated by `>`. diff --git a/.changeset/tasty-pigs-vanish.md b/.changeset/tasty-pigs-vanish.md new file mode 100644 index 0000000000..73556c70d5 --- /dev/null +++ b/.changeset/tasty-pigs-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Entity presentation api now only fetches fields that are required to display entity title diff --git a/.changeset/thick-walls-share.md b/.changeset/thick-walls-share.md new file mode 100644 index 0000000000..798b1561b4 --- /dev/null +++ b/.changeset/thick-walls-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gerrit': patch +--- + +Added test cases for publish:gerrit action examples diff --git a/.changeset/tiny-icons-sit.md b/.changeset/tiny-icons-sit.md new file mode 100644 index 0000000000..7dc35a46fd --- /dev/null +++ b/.changeset/tiny-icons-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Small internal fix to better work with recent `lodash` versions diff --git a/.changeset/tiny-waves-provide.md b/.changeset/tiny-waves-provide.md new file mode 100644 index 0000000000..aabafcfd6b --- /dev/null +++ b/.changeset/tiny-waves-provide.md @@ -0,0 +1,7 @@ +--- +'@backstage/backend-plugin-api': patch +'@backstage/backend-defaults': patch +'@backstage/backend-common': patch +--- + +Allow the cache service to accept the human duration format for TTL diff --git a/.changeset/tough-peaches-kneel.md b/.changeset/tough-peaches-kneel.md new file mode 100644 index 0000000000..7674da8879 --- /dev/null +++ b/.changeset/tough-peaches-kneel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fixed an issue with the by-query call, where ordering by a field that does not exist on all entities led to not all results being returned diff --git a/.changeset/tricky-apricots-film.md b/.changeset/tricky-apricots-film.md new file mode 100644 index 0000000000..7b1c28fdb9 --- /dev/null +++ b/.changeset/tricky-apricots-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-signals-react': patch +--- + +Fix for `useSignal` returning the inverse value for `isSignalsAvailable`. diff --git a/.changeset/twenty-clouds-melt.md b/.changeset/twenty-clouds-melt.md new file mode 100644 index 0000000000..dee2b0f8ef --- /dev/null +++ b/.changeset/twenty-clouds-melt.md @@ -0,0 +1,64 @@ +--- +'@backstage/backend-defaults': minor +--- + +**BREAKING**: The default backend instance no longer provides implementations for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`. + +If you rely on plugins that still require these services, you can add them to your own backend by re-creating the service reference and factory. + +The following can be used to implement the identity service: + +```ts +import { + coreServices, + createServiceFactory, + createServiceRef, +} from '@backstage/backend-plugin-api'; +import { + DefaultIdentityClient, + IdentityApi, +} from '@backstage/plugin-auth-node'; + +backend.add( + createServiceFactory({ + service: createServiceRef({ id: 'core.identity' }), + deps: { + discovery: coreServices.discovery, + }, + async factory({ discovery }) { + return DefaultIdentityClient.create({ discovery }); + }, + }), +); +``` + +The following can be used to implement the token manager service: + +```ts +import { ServerTokenManager, TokenManager } from '@backstage/backend-common'; +import { createBackend } from '@backstage/backend-defaults'; +import { + coreServices, + createServiceFactory, + createServiceRef, +} from '@backstage/backend-plugin-api'; + +backend.add( + createServiceFactory({ + service: createServiceRef({ id: 'core.tokenManager' }), + deps: { + config: coreServices.rootConfig, + logger: coreServices.rootLogger, + }, + createRootContext({ config, logger }) { + return ServerTokenManager.fromConfig(config, { + logger, + allowDisabledTokenManager: true, + }); + }, + async factory(_deps, tokenManager) { + return tokenManager; + }, + }), +); +``` diff --git a/.changeset/twenty-queens-grow.md b/.changeset/twenty-queens-grow.md new file mode 100644 index 0000000000..1236488dbe --- /dev/null +++ b/.changeset/twenty-queens-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': minor +--- + +Added EntityOwnerPicker component to the TemplateListPage to allow filtering on owner diff --git a/.changeset/unlucky-cycles-clean.md b/.changeset/unlucky-cycles-clean.md new file mode 100644 index 0000000000..5b582dec59 --- /dev/null +++ b/.changeset/unlucky-cycles-clean.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Scaffolder review page shows static amount of asterisks for secret fields. diff --git a/.changeset/violet-apricots-smoke.md b/.changeset/violet-apricots-smoke.md new file mode 100644 index 0000000000..80caf466eb --- /dev/null +++ b/.changeset/violet-apricots-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Remove references to the `@backstage/backend-tasks` in versions of the `create-app` package, as it has been deprecated. diff --git a/.changeset/violet-jokes-tap.md b/.changeset/violet-jokes-tap.md new file mode 100644 index 0000000000..5b79ced6cd --- /dev/null +++ b/.changeset/violet-jokes-tap.md @@ -0,0 +1,21 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-app-api': patch +--- + +Added support for defining `replaces` in `createExtensionInput` which will allow extensions to redirect missing `attachTo` points to an input of the created extension. + +```ts +export const AppThemeApi = ApiBlueprint.makeWithOverrides({ + name: 'app-theme', + inputs: { + themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { + // attachTo: { id: 'app', input: 'themes'} will be redirected to this input instead + replaces: [{ id: 'app', input: 'themes' }], + }), + }, + factory: () { + ... + } +}); +``` diff --git a/.changeset/warm-boxes-grab-2.md b/.changeset/warm-boxes-grab-2.md new file mode 100644 index 0000000000..f1928c8be7 --- /dev/null +++ b/.changeset/warm-boxes-grab-2.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +A new `apis` parameter has been added to `factory` for extensions. This is a way to access utility APIs without being coupled to the React context. diff --git a/.changeset/warm-boxes-grab.md b/.changeset/warm-boxes-grab.md new file mode 100644 index 0000000000..e10dbe086c --- /dev/null +++ b/.changeset/warm-boxes-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Added the `root` extension the replace the `app` extension as the root of the app. diff --git a/.changeset/wild-buses-notice.md b/.changeset/wild-buses-notice.md new file mode 100644 index 0000000000..3620786aa8 --- /dev/null +++ b/.changeset/wild-buses-notice.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Added the option to skip database migrations by setting `skipMigrations: true` in config. This can be done globally in the database config or by plugin id. diff --git a/.changeset/wise-scissors-help.md b/.changeset/wise-scissors-help.md new file mode 100644 index 0000000000..c8fd2d4c44 --- /dev/null +++ b/.changeset/wise-scissors-help.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-defaults': patch +'@backstage/backend-app-api': patch +'@backstage/backend-common': patch +'@backstage/backend-plugin-api': patch +--- + +Updates to the config schema to match reality diff --git a/.changeset/witty-years-cry.md b/.changeset/witty-years-cry.md new file mode 100644 index 0000000000..48c566730b --- /dev/null +++ b/.changeset/witty-years-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-backend': patch +--- + +Deprecated `createRouter` and its router options in favour of the new backend system. diff --git a/.changeset/yellow-bees-hope.md b/.changeset/yellow-bees-hope.md new file mode 100644 index 0000000000..1ea8690c20 --- /dev/null +++ b/.changeset/yellow-bees-hope.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': minor +--- + +Removed deprecated `icons` property passing to `createApp` and `createSpecializedApp`. Use `IconBundleBlueprint.make` to create extensions instead and include them in the app. diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5a70a25512..37f54997c6 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,22 +1,17 @@ { $schema: 'https://docs.renovatebot.com/renovate-schema.json', - labels: ['dependencies'], - extends: ['config:best-practices', ':gitSignOff'], // do not pin dev dependencies, which are part of the best-practices preset ignorePresets: [':pinDevDependencies', ':pinDigest', 'docker:pinDigests'], - constraints: { // TODO(freben): Remove this later; it addresses a temporary issue in corepack // https://github.com/nodejs/corepack/issues/379 // https://github.com/renovatebot/renovate/discussions/27465 corepack: '0.24.1', }, - - // the default limit are 10 PRs + // the default limit is 10 PRs prConcurrentLimit: 20, - postUpdateOptions: ['yarnDedupeHighest'], rangeStrategy: 'update-lockfile', ignoreDeps: [ @@ -26,19 +21,19 @@ ], packageRules: [ { - matchSourceUrlPrefixes: ['https://github.com/spotify/web-scripts'], groupName: 'Spotify web-scripts monorepo packages', rangeStrategy: 'replace', + matchSourceUrls: ['https://github.com/spotify/web-scripts{/,}**'], }, { - matchSourceUrlPrefixes: ['https://github.com/microsoft/rushstack'], groupName: 'API Extractor / Rush Stack monorepo packages', rangeStrategy: 'replace', + matchSourceUrls: ['https://github.com/microsoft/rushstack{/,}**'], }, { - matchSourceUrlPrefixes: ['https://github.com/gregberge/svgr'], groupName: 'SVGR monorepo packages', rangeStrategy: 'replace', + matchSourceUrls: ['https://github.com/gregberge/svgr{/,}**'], }, // We update yarn packages manually as it's gzip'd and we don't want to pollute the repository too much. { diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml index c3886e05b9..abb98c03e1 100644 --- a/.github/workflows/api-breaking-changes-comment.yml +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -22,7 +22,7 @@ jobs: action: ${{ steps.event.outputs.ACTION }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index c39fdf7f7c..72f7ee9fb4 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -14,7 +14,7 @@ jobs: if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -33,7 +33,7 @@ jobs: registry-url: https://registry.npmjs.org/ - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: linux-v18 @@ -46,7 +46,7 @@ jobs: cat ${{ github.event_path }} > event.json - name: Upload Artifacts - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4 with: name: preview-spec path: | diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml index bde8f15606..fcff8bc11a 100644 --- a/.github/workflows/automate_area-labels.yml +++ b/.github/workflows/automate_area-labels.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 94b4b51848..1d8e29fab0 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -34,7 +34,7 @@ jobs: ref: 'refs/pull/${{ github.event.pull_request.number }}/merge' - name: fetch base run: git fetch --depth 1 origin ${{ github.base_ref }} - - uses: backstage/actions/changeset-feedback@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + - uses: backstage/actions/changeset-feedback@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 name: Generate feedback with: diff-ref: 'origin/master' diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index 2aac0a17f6..2bc7940971 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index 6cb16937c6..1ffeac1cc0 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/ci-noop.yml b/.github/workflows/ci-noop.yml index adf0feb926..9b54e6fe6f 100644 --- a/.github/workflows/ci-noop.yml +++ b/.github/workflows/ci-noop.yml @@ -40,7 +40,7 @@ jobs: name: Test ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 168d6205e4..5cbe2e15a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: name: Install ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -41,7 +41,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -64,7 +64,7 @@ jobs: name: Verify ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -77,7 +77,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -222,7 +222,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index bdbe22aae8..2ac5434f72 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -10,11 +10,11 @@ jobs: timeout-minutes: 10 steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - - uses: backstage/actions/cron@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + - uses: backstage/actions/cron@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 9f305f9a20..86db3795bc 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -37,7 +37,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -51,14 +51,14 @@ jobs: working-directory: ./example-app - name: Login to GitHub Container Registry - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Build and push uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index eafa4a8fcd..ac02794268 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml deleted file mode 100644 index 5488b9a189..0000000000 --- a/.github/workflows/deploy_nightly.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Deploy Nightly Release -on: - schedule: - - cron: '0 2 * * *' # run at 2 AM UTC - -jobs: - build: - if: github.repository == 'backstage/backstage' # prevent running on forks - - runs-on: ubuntu-latest - - env: - CI: true - NODE_OPTIONS: --max-old-space-size=8192 - - steps: - - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 - with: - egress-policy: audit - - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: use node.js 18.x - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: 18.x - registry-url: https://registry.npmjs.org/ # Needed for auth - - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 - with: - cache-prefix: ${{ runner.os }}-v18.x - - # No verification done here, only build & publish. If the master branch - # is broken we will see that from those builds, but we still want to push nightly - # builds since upgrading to them is a manual process anyway. - - - name: tsc - run: yarn tsc - - - name: build - run: yarn backstage-cli repo build - - - name: build embedded techdocs app - working-directory: packages/techdocs-cli-embedded-app - run: yarn build - - # Prepares a nightly release version of any package with pending changesets - # Pre-mode is exited if case we're in it, otherwise it has no effect - - name: prepare nightly release - run: | - yarn changeset pre exit || true - yarn changeset version --snapshot nightly - - # Publishes the nightly release to npm, by using tag we make sure the release is - # 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 config set -H 'npmAuthToken' "${{secrets.NPM_TOKEN}}" - yarn workspaces foreach -p -j 10 -v --no-private npm publish --access public --tolerate-republish --tag nightly - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Discord notification - if: ${{ failure() }} - uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # 0.3.2 - env: - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} - with: - args: 'Nightly build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}' diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 7ae5637986..f86c1db4fa 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -1,11 +1,5 @@ name: Deploy Packages on: - workflow_dispatch: - inputs: - force_release: - description: Unconditionally trigger the release job - required: false - type: boolean push: branches: [master, patch/*] @@ -82,7 +76,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -140,7 +134,7 @@ jobs: release: needs: build - if: needs.build.outputs.needs_release == 'true' || inputs.force_release == true + if: needs.build.outputs.needs_release == 'true' runs-on: ubuntu-latest @@ -148,75 +142,16 @@ jobs: matrix: node-version: [18.x] - env: - CI: 'true' - NODE_OPTIONS: --max-old-space-size=8192 - steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: ${{ matrix.node-version }} - registry-url: https://registry.npmjs.org/ # Needed for auth - - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 - with: - cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - - - name: build type declarations - run: yarn tsc:full - - - name: build packages - run: yarn backstage-cli repo build - - - name: build embedded techdocs app - working-directory: packages/techdocs-cli-embedded-app - run: yarn build - - # 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 workspaces foreach -v --no-private npm publish --access public --tolerate-republish --tag next - else - yarn workspaces foreach -v --no-private npm publish --access public --tolerate-republish - fi - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - # Grabs the version in the root package.json and creates a tag on GitHub - - name: Create a release tag - id: create_tag - run: node scripts/create-release-tag.js - env: - GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - - # Convert the newly created tag into a release with changelog information - - name: Create release on GitHub - run: node scripts/create-github-release.js ${{ steps.create_tag.outputs.tag_name }} 1 - env: - GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - - - name: Dispatch repository event - uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 - with: - token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - event-type: release-published - client-payload: '{"version": "${{ steps.create_tag.outputs.version }}"}' - - # Notify everyone about this great new release :D + # Notify maintainers that a new release is ready to be published - name: Discord notification uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # 0.3.2 env: - DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }} - TAG_NAME: ${{ steps.create_tag.outputs.tag_name }} + DISCORD_WEBHOOK: ${{ secrets.DISCORD_MAINTAINERS_WEBHOOK }} with: - args: 'A new release has been published! https://github.com/backstage/backstage/releases/tag/{{TAG_NAME}}' + args: 'A new release is ready to be [published](https://github.com/backstage/publishing/actions/workflows/publish-main.yml) from {{GITHUB_SHA}}' diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 26e394fd87..c8793d3ab0 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -3,14 +3,20 @@ on: issues: types: [opened] +permissions: + contents: read + jobs: sync: + permissions: + contents: read # for github/issue-labeler to get repo contents + issues: write # for github/issue-labeler to create or remove labels runs-on: ubuntu-latest if: github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml index d6f36da686..27a5a78052 100644 --- a/.github/workflows/pr-review-comment-trigger.yaml +++ b/.github/workflows/pr-review-comment-trigger.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -30,7 +30,7 @@ jobs: run: | mkdir -p ./pr echo $PR_NUMBER > ./pr/pr_number - - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: pr_number-${{ github.event.pull_request.number }} path: pr/ diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml index 1b2b9ee5be..6133fdef8a 100644 --- a/.github/workflows/pr-review-comment.yaml +++ b/.github/workflows/pr-review-comment.yaml @@ -17,7 +17,7 @@ jobs: steps: # Inspired by https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -40,7 +40,7 @@ jobs: const prNumber = artifact.name.slice('pr_number-'.length) core.setOutput('pr-number', prNumber); - - uses: backstage/actions/re-review@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + - uses: backstage/actions/re-review@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index bf7c5cfa8d..510d5242f4 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,12 +18,12 @@ jobs: if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request ) steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - name: PR sync - uses: backstage/actions/pr-sync@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/pr-sync@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 4ef8cc7181..0d22deeca1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -58,7 +58,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: 'Upload artifact' - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: SARIF file path: results.sarif @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index da6bd2592d..859f077095 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -25,7 +25,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index ce79efb148..357b7f5cd9 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -11,7 +11,7 @@ jobs: if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 62dd7e0c13..898aaa74c7 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -25,7 +25,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v18.x diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index 4ba792d4e0..19072a443b 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -11,7 +11,7 @@ jobs: if: github.actor == 'renovate[bot]' && github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index af2e4da251..0369f89648 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -24,12 +24,12 @@ jobs: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v18.x - name: Create Snyk report - uses: snyk/actions/node@6312a53377a551c0258438bf25fb8f378afbc977 # master + uses: snyk/actions/node@9213221444c2dc9e8b2502c1e857c26d851e84a7 # master continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found with: args: > diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 1bdf0d30cf..99fb8e16bd 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -25,13 +25,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Monitor and Synchronize Snyk Policies - uses: snyk/actions/node@6312a53377a551c0258438bf25fb8f378afbc977 # master + uses: snyk/actions/node@9213221444c2dc9e8b2502c1e857c26d851e84a7 # master with: command: monitor args: > @@ -46,7 +46,7 @@ jobs: # Above we run the `monitor` command, this runs the `test` command which is # the one that generates the SARIF report that we can upload to GitHub. - name: Create Snyk report - uses: snyk/actions/node@6312a53377a551c0258438bf25fb8f378afbc977 # master + uses: snyk/actions/node@9213221444c2dc9e8b2502c1e857c26d851e84a7 # master continue-on-error: true # To make sure that SARIF upload gets called with: args: > @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: sarif_file: snyk.sarif diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 3a10083b7e..3a29ba3c79 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_accessibility-noop.yml b/.github/workflows/verify_accessibility-noop.yml index 6b51713aa1..8d8cb30789 100644 --- a/.github/workflows/verify_accessibility-noop.yml +++ b/.github/workflows/verify_accessibility-noop.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 386306dcb1..fd86075ea0 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -30,7 +30,7 @@ jobs: with: node-version: 18.x - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v18.x - name: run Lighthouse CI diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index b2fb2dc6ec..ea31ea6431 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/autobuild@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5cf07d8b700b67e235fbb65cbc84f69c0cf10464 # v3.25.14 + uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 2a0c4449a9..8e1a91d850 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-kubernetes-noop.yml b/.github/workflows/verify_e2e-kubernetes-noop.yml index b51c7fd201..544c4e6186 100644 --- a/.github/workflows/verify_e2e-kubernetes-noop.yml +++ b/.github/workflows/verify_e2e-kubernetes-noop.yml @@ -23,7 +23,7 @@ jobs: name: Kubernetes ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index 4647505cbf..18eee7e3f2 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -22,7 +22,7 @@ jobs: name: Kubernetes ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -35,7 +35,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-linux-noop.yml b/.github/workflows/verify_e2e-linux-noop.yml index c042bdeac7..024f288255 100644 --- a/.github/workflows/verify_e2e-linux-noop.yml +++ b/.github/workflows/verify_e2e-linux-noop.yml @@ -29,7 +29,7 @@ jobs: name: E2E Linux ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index fa303ce1c6..1c1ed7a3eb 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -41,7 +41,7 @@ jobs: name: E2E Linux ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -58,7 +58,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 41f76a6410..b59c8c0dd1 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -30,12 +30,12 @@ jobs: name: Techdocs steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows-noop.yml b/.github/workflows/verify_e2e-windows-noop.yml index 299489fcbe..5244a78919 100644 --- a/.github/workflows/verify_e2e-windows-noop.yml +++ b/.github/workflows/verify_e2e-windows-noop.yml @@ -25,7 +25,7 @@ jobs: name: E2E Windows ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index bc782c4bd3..ec58ba3dae 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -31,7 +31,7 @@ jobs: name: E2E Windows ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -56,7 +56,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: setup python - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: '3.10' @@ -78,7 +78,7 @@ jobs: uses: browser-actions/setup-chrome@803ef6dfb4fdf22089c9563225d95e4a515820a0 # latest - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index ad94f9ece5..3a5899d3cf 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite-noop.yml b/.github/workflows/verify_microsite-noop.yml index d8ccaf73e5..daec683da6 100644 --- a/.github/workflows/verify_microsite-noop.yml +++ b/.github/workflows/verify_microsite-noop.yml @@ -21,7 +21,7 @@ jobs: name: Microsite steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 08dcddde8e..a695eb1d51 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -24,7 +24,7 @@ jobs: name: Microsite steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -34,7 +34,7 @@ jobs: uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 18.x - - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5 + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5 with: python-version: '3.9' diff --git a/.github/workflows/verify_microsite_accessibility-noop.yml b/.github/workflows/verify_microsite_accessibility-noop.yml index 9f0ecc65be..a3e8e7fdec 100644 --- a/.github/workflows/verify_microsite_accessibility-noop.yml +++ b/.github/workflows/verify_microsite_accessibility-noop.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index 220e2633c3..b2c98c1e58 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_storybook-noop.yml b/.github/workflows/verify_storybook-noop.yml index a1a1c1965d..1f9ded9587 100644 --- a/.github/workflows/verify_storybook-noop.yml +++ b/.github/workflows/verify_storybook-noop.yml @@ -28,7 +28,7 @@ jobs: name: Storybook steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index e9da9f0398..9bea2f3dc9 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -28,7 +28,7 @@ jobs: name: Storybook steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit @@ -42,7 +42,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@772cef06641090d0095188e15c85647acdf0c250 # v0.6.11 + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: storybook yarn install @@ -51,7 +51,7 @@ jobs: - run: yarn build-storybook - - uses: chromaui/action@fdbe7756d4dbf493e2fbb822df73be7accd07e1c # v11 + - uses: chromaui/action@b984808b772126a9f44b2b7737b131b68a2ede32 # v11 with: token: ${{ secrets.GITHUB_TOKEN }} # projectToken intentionally shared to allow collaborators to run Chromatic on forks diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 109e2ee96e..feb209dc2b 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit diff --git a/SECURITY.md b/SECURITY.md index 8de1307356..5c6c8106d5 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -59,14 +59,14 @@ function writeTemporaryFile(tmpDir: string, name: string, content: string) { If the `name` of the file is controlled by the user, they can for example enter `../../../../etc/hosts` as the name of the file. This can lead to a file being written outside the intended directory, which in turn can be used to inject malicious code or other form of attacks. -The recommended solution to this is to use `resolveSafeChildPath` from `@backstage/backend-common` to resolve the file path instead. It makes sure that the resolved path does not fall outside the provided directory. If you simply want to validate whether a file path is safe, you can use `isChildPath` instead. +The recommended solution to this is to use `resolveSafeChildPath` from `@backstage/backend-plugin-api` to resolve the file path instead. It makes sure that the resolved path does not fall outside the provided directory. If you simply want to validate whether a file path is safe, you can use `isChildPath` instead. The insecure example above should instead be written like this: ```ts // THIS IS GOOD, DO THIS -import { resolveSafeChildPath } from '@backstage/backend-common'; +import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; import fs from 'fs-extra'; function writeTemporaryFile(tmpDir: string, name: string, content: string) { diff --git a/beps/0002-dynamic-frontend-plugins/README.md b/beps/0002-dynamic-frontend-plugins/README.md index 34c6e2d435..db223c959d 100644 --- a/beps/0002-dynamic-frontend-plugins/README.md +++ b/beps/0002-dynamic-frontend-plugins/README.md @@ -301,7 +301,7 @@ An example of [integration with scalprum](https://github.com/backstage/backstage **Dynamic Feature configuration** -The dynamic remote loading can be added directly into the [`createApp`](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/wiring/createApp.tsx#L234) function. +The dynamic remote loading can be added directly into the [`createApp`](https://backstage.io/docs/reference/frontend-defaults.createapp) function. The current `feature` type can be expanded with a `DynamicFrontendFeature` type: @@ -351,7 +351,7 @@ const scalprum = initialize({ }); ``` -Because the [`appLoader`](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/wiring/createApp.tsx#L193) is already async, it is a perfect place to load the plugin registry and init the dynamic plugins. +Because the [`appLoader`](https://backstage.io/docs/reference/frontend-defaults.createapp) is already async, it is a perfect place to load the plugin registry and init the dynamic plugins. Initializing the dynamic feature is just a case of mapping the `DynamicFrontendFeature` to `FrontendFeature` via Scalprum: diff --git a/beps/0008-docs-personas-framework-portal/README.md b/beps/0008-docs-personas-framework-portal/README.md index 07468b7c4a..1ef073a053 100644 --- a/beps/0008-docs-personas-framework-portal/README.md +++ b/beps/0008-docs-personas-framework-portal/README.md @@ -3,10 +3,11 @@ title: Improved Backstage Documentation with Personas status: provisional authors: - '@waldirmontoya25' - - '@aramissennyeyd' + - '@aramissennyeydd' owners: + - '@backstage/documentation-maintainers' project-areas: - - core + - documentation creation-date: 2024-03-18 --- @@ -23,22 +24,22 @@ creation-date: 2024-03-18 - [Design Details](#design-details) - [Personas](#personas) - [User](#user) - - [Documentation Style](#documentation-style) - - [Administrator](#administrator) - - [Documentation Style](#documentation-style-1) - - [Integrator](#integrator) - - [Documentation Style](#documentation-style-2) + - [Documentation Content](#documentation-content) + - [Operator](#operator) + - [Documentation Content](#documentation-content-1) + - [Builder](#builder) + - [Documentation Content](#documentation-content-2) - [Contributor](#contributor) - - [Documentation Style](#documentation-style-3) + - [Documentation Content](#documentation-content-3) - [Business Stakeholder](#business-stakeholder) - - [Documentation Style](#documentation-style-4) + - [Documentation Content](#documentation-content-4) - [Release Plan](#release-plan) - [Dependencies](#dependencies) - [Example Table of Contents](#example-table-of-contents) ## Summary -This BEP proposes restructuring the Backstage documentation to emphasize the dual nature of Backstage as both a framework for building developer portals and a fully functional developer portal out of the box, as demonstrated by the demo site. The documentation will be divided into two main sections: One focusing on the developer portal that users get with the core plugins, and another on the framework that allows integrators and builders to create their own developer portal. The goal is to improve clarity, navigation, and adoption of Backstage by positioning it as both a ready-to-use developer portal and a framework for building custom developer portals. +This BEP proposes restructuring the Backstage documentation to emphasize the dual nature of Backstage as both a framework for building developer portals and a fully functional developer portal out of the box, as demonstrated by the demo site. The documentation will be divided into two main sections: One focusing on the developer portal that users get with the core plugins, and another on the framework that allows operators and builders to create their own developer portal. The goal is to improve clarity, navigation, and adoption of Backstage by positioning it as both a ready-to-use developer portal and a framework for building custom developer portals. ## Motivation @@ -48,8 +49,8 @@ The current Backstage documentation has been reported to be difficult to navigat - Divide the documentation into two section: Framework and Developer Portal - Define the personas Backstage is targeting -- Structure the documentation to cater the different personas -- Move the existing content to the appropriate section +- Provide complete, clear and easy-to-find instructions for tasks required of personas +- Move the existing content to the appropriate section. ### Non-Goals @@ -63,14 +64,14 @@ The proposed restructuring of the Backstage documentation revolves around two co 1. Positioning Backstage as both a framework to build developer portals and a developer portal itself, and splitting the documentation into two main sections: - Developer Portal: Focusing on the features, configuration, and usage of the developer portal that users get out of the box with the core plugins. - - Framework: Covering the aspects of Backstage as a framework, including guides for integrators and builders who want to create their own developer portal using Backstage. + - Framework: Covering the aspects of Backstage as a framework, including guides for operators and builders who want to create their own developer portal using Backstage. 2. Defining the personas participating in Backstage adoption journeys to improve documentation navigation. The identified personas are: - **End User**: A person who uses Backstage to find information, use plugins, and consume the developer portal. - - **Administrator/Operator**: A person who configures, secures, and deploys the developer portal, manages plugins, and oversees the general administration of the developer portal. - - **Integrator/Builder**: A person who builds plugins, customizes the code and design, and creates custom-built developer portals based on the Backstage framework. This includes developers and designers and anyone adding new functionality to their own Backstage instance. - - **Product Manager/Business stakeholders**: A person who defines the strategy for adopting Backstage, identifies use cases, communicates the value proposition for adopting Backstage and connects the developer portal to the business strategy. + - **Operator**: A person who configures, secures, and deploys the developer portal, manages plugins, and oversees the general administration of the developer portal. + - **Builder**: A person who builds plugins, customizes the code and design, and creates custom-built developer portals based on the Backstage framework. This includes developers and designers and anyone adding new functionality to their own Backstage instance. + - **Business Stakeholder**: A person who defines the strategy for adopting Backstage, identifies use cases, communicates the value proposition for adopting Backstage and connects the developer portal to the business strategy. - **Contributor**: A person who contributes to the Backstage upstream ecosystem. The adoption strategy would be as follows: @@ -90,6 +91,7 @@ The benefits of restructuring the documentation according to these ideas include - The Docs section of the microsite will be divided into two top-level sections: Framework and Developer Portal. - The structure of the Table of Contents will align with the outline proposed in https://github.com/backstage/backstage/issues/21946. +- The developer portal documentation will be the primary entry point. Users should hit this site when navigating to backstage.io/docs. The framework documentation should be hosted similarly, but will be introduced organically in the developer portal documentation when referencing customization and other builder facing tasks. ### Personas @@ -97,7 +99,7 @@ The benefits of restructuring the documentation according to these ideas include Users navigate the developer portal to access tools, information, and plugins essential for their daily tasks. They rely on Backstage to effortlessly find resources, utilize integrations, and connect with other tools and services within their ecosystem. Their interaction is predominantly with the frontend of the portal, where ease of use, accessibility, and relevant content discovery are critical. -##### Documentation Style +##### Documentation Content Documentation for this persona should be about usability of the portal once it is running. For example: @@ -112,13 +114,13 @@ Documentation for this persona should be about usability of the portal once it i - Using available plugins - Step by step tutorials -#### Administrator +#### Operator -Administrators are responsible for the behind-the-scenes technical setup and maintenance of the Backstage portal. This includes deploying the portal, configuring plugins, managing user access, and ensuring the security and performance of the system. They interact with both the frontend and backend, often using command-line tools, administrative dashboards, and configuration files to perform their tasks. +Operators are responsible for the behind-the-scenes technical setup and maintenance of the Backstage portal. This includes deploying the portal, configuring plugins, managing user access, and ensuring the security and performance of the system. They interact with both the frontend and backend, often using command-line tools, administrative dashboards, and configuration files to perform their tasks. -##### Documentation Style +##### Documentation Content -Documentation written for this persona should be DevOps technical, assuming a strong DevOps background. The goal with administrator documentation is to give administrators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices. For example: +Documentation written for this persona should be DevOps technical, assuming a strong DevOps background. The goal is to give operators a strong understanding of how to deploy and manage a Backstage Developer Portal, best practices. For example: - Installing and upgrading - Configuring @@ -126,17 +128,17 @@ Documentation written for this persona should be DevOps technical, assuming a st - Plugins - Ingesting data (users/groups/components, etc) - Installing plugins -- Implement Git Flows for the Developer portal +- Implement Git Flows for the Developer Portal - Creating Pipelines for Docs generation - Troubleshooting -#### Integrator +#### Builder -Integrators actively work on extending and customizing Backstage. This includes developing new plugins, customizing the UI/UX, and integrating external services or data sources. Their work is deeply technical, involving coding, and engaging with the Backstage community for support and collaboration. They need a deep understanding of the Backstage architecture and APIs, working closely with both the framework's backend and frontend aspects. +Builders actively work on extending and customizing Backstage. This includes developing new plugins, customizing the UI/UX, and integrating external services or data sources. Their work is deeply technical, involving coding, and engaging with the Backstage community for support and collaboration. They need a deep understanding of the Backstage architecture and APIs, working closely with both the framework's backend and frontend aspects. -##### Documentation Style +##### Documentation Content -Documentation written for this persona should be software technical, assuming a strong software background. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for integrators is to give them a strong understanding of how to use the Backstage framework to build/evolve a company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. For example: +Documentation written for this persona should be software technical, assuming a strong software background. While we can assume an overall technical knowledge, where possible we should link out to useful guides for the technologies we use, ex: Node.js, express.js, React, etc. The goal with documentation written for this persona is to give them a strong understanding of how to use the Backstage framework to build/evolve a company's Backstage Developer Portal, orient them to get support from the open source community, and prepare them for continuing to deliver value for their Backstage Developer Portal. For example: - API references - Frontend and Backend systems @@ -151,7 +153,9 @@ Documentation written for this persona should be software technical, assuming a Contributors are involved in the development of the Backstage framework itself. They contribute to the core codebase, develop new features, fix bugs, create documentation and maintain the overall health of the project. They are deeply involved in the open-source community, collaborating with maintainers and other contributors to improve the framework and its ecosystem. -##### Documentation Style +Documentation for the contributor role should _not_ exist on the docs site. It should exist solely in the Github repositories (backstage/backstage, backstage/community-plugins). It can be linked to from the site, but should not have dedicated guides outside of the Github repositories. + +##### Documentation Content The goal with documentation written for contributors is to give them a strong understanding of how to contribute to the Backstage framework, orient them to get support from the open source community, and prepare them for continuing to deliver value for the Backstage framework. For example: @@ -164,7 +168,7 @@ The goal with documentation written for contributors is to give them a strong un Business stakeholders use Backstage to align technical capabilities with business goals, monitoring how features and plugins support operational efficiency, developer satisfaction, and strategic objectives. They are involved in defining the strategy and measuring the impact of the developer portal on the organization. They need to navigate through dashboards, reports, and analytics within Backstage to gather insights and make informed decisions. -##### Documentation Style +##### Documentation Content Documentation written for this persona should be strategic, assuming a strong background in business development and strategy. The goal for business documentation is to give a strong understanding of what Backstage Developer Portal can do for their company, how to deliver value quickly and continuously and guides for pitching or driving Backstage adoption. For example: @@ -175,9 +179,10 @@ Documentation written for this persona should be strategic, assuming a strong ba ## Release Plan -- Release the BEP by 03/24/2024. -- Discuss the changes with the community and gather feedback by 04/24/2024. -- Implement the changes by 04/30/2024. +- [x] Release the BEP (04/21/2024). +- [ ] Discuss the changes with the community and gather feedback. +- [ ] Implement the table of contents changes. +- [ ] Create issues to track reviewing and rewriting existing documentation into these new personas. ## Dependencies @@ -185,20 +190,87 @@ None ## Example Table of Contents -- Overview - - "The overview should introduce users to the concept of Backstage, what an IDP is, how to deliver value, why you should care about DevEx, etc." - - What is Backstage? - - Roadmap - - Vision - - Release and Versioning Policy - - Backstage Threat Model - - Logo assets - - Support and community +- Developer Portal + - Overview + - What is Backstage? + - Roadmap + - Vision + - User personas and use cases + - How this documentation is organized + - Framework + - Developer portal + - Release and Versioning Policy + - Backstage Threat Model + - Logo assets + - Support and community + - Architecture Overview + - Getting Started + - Operator Guides + - Developer Portal + - "How do I deploy, monitor, configure and verify my Backstage Developer Portal?" + - Installing and Configuring + - Database + - Authentication + - Installing plugins + - Customize the design + - Securing + - Deploying in Production + - Integrating with other systems + - Managing + - Monitoring + - Troubleshooting + - Upgrading + - Keeping backstage up to date + - Customizing + - Core Plugins + - "How do I install and configure Backstage Developer Portal with plugins." + - Home Page + - Installing and Configuring + - Software Catalog + - Overview + - The life of an Entity + - Catalog Configuration + - System Model + - YAML file format + - Entity Reference + - Well Known annotations + - Well known relations + - Well known statuses + - Creating the catalog graph + - Software Templates + - Overview + - Configuring + - Adding a new template + - Writing a template + - Built in actions + - TechDocs + - Overview + - Getting Started + - Architecture + - Installing and configuring + - Using Cloud Storage for TechDocs generated files + - Configuring CI/CD to generate and publish TechDocs sites + - TechDocs CLI + - Troubleshooting + - Kubernetes + - Installing and Configuring + - Authentication + - Troubleshooting + - Search + - Business Stakeholder Guides + - "How do I present Backstage to leadership, what are the benefits, why should I care, etc." + - Strategies for adopting + - Use cases + - User Guides + - "How do I use the default OSS Backstage" + - Logging in + - Registering a component + - Creating a new component + - Reference - Framework - Architecture Overview - - "The arch overview should explain how the framework is structured, where plugins and instances fit in and how to understand the current design of Backstage." - Getting Started - - Integrator/Builder Guides + - Builder Guides - Local Development - "Prepare users for how to develop locally, debug problems, run tests, etc." - CLI @@ -280,68 +352,3 @@ None - "How to get started contributing to OSS." - Contributing to Backstage - Reference -- Developer Portal - - Architecture Overview - - Getting Started - - Administrator Guides - - Developer Portal - - "How do I deploy, monitor, configure and verify my Backstage Developer Portal?" - - Installing and Configuring - - Database - - Authentication - - Installing plugins - - Customize the design - - Securing - - Deploying in Production - - Integrating with other systems - - Managing - - Monitoring - - Troubleshooting - - Upgrading - - Keeping backstage up to date - - Customizing - - Core Plugins - - "How do I install and configure Backstage Developer Portal with plugins." - - Home Page - - Installing and Configuring - - Software Catalog - - Overview - - The life of an Entity - - Catalog Configuration - - System Model - - YAML file format - - Entity Reference - - Well Known annotations - - Well known relations - - Well known statuses - - Creating the catalog graph - - Software Templates - - Overview - - Configuring - - Adding a new template - - Writing a template - - Built in actions - - TechDocs - - Overview - - Getting Started - - Architecture - - Installing and configuring - - Using Cloud Storage for TechDocs generated files - - Configuring CI/CD to generate and publish TechDocs sites - - TechDocs CLI - - Troubleshooting - - Kubernetes - - Installing and Configuring - - Authentication - - Troubleshooting - - Search - - Product Manager Guides - - "How do I present Backstage to leadership, what are the benefits, why should I care, etc." - - Strategies for adopting - - Use cases - - User Guides - - "How do I use the default OSS Backstage" - - Logging in - - Registering a component - - Creating a new component - - Reference diff --git a/beps/00010-event-auditor/README.md b/beps/0011-event-auditor/README.md similarity index 100% rename from beps/00010-event-auditor/README.md rename to beps/0011-event-auditor/README.md diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild index 053b4fb492..fa8e42482b 100644 --- a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild +++ b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild @@ -55,4 +55,4 @@ COPY docker/default.conf.template /etc/nginx/templates/default.conf.template COPY docker/inject-config.sh /docker-entrypoint.d/40-inject-config.sh -ENV PORT 80 +ENV PORT=80 diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild b/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild index f0fa4a034b..b07d02c0c8 100644 --- a/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild +++ b/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild @@ -36,4 +36,4 @@ COPY docker/default.conf.template /etc/nginx/templates/default.conf.template COPY docker/inject-config.sh /docker-entrypoint.d/40-inject-config.sh -ENV PORT 80 +ENV PORT=80 diff --git a/contrib/docker/minimal-hardened-image/Dockerfile b/contrib/docker/minimal-hardened-image/Dockerfile index a247e0dff1..b9435aa8af 100644 --- a/contrib/docker/minimal-hardened-image/Dockerfile +++ b/contrib/docker/minimal-hardened-image/Dockerfile @@ -23,8 +23,8 @@ RUN --mount=type=cache,target=/home/nonroot/.cache/pip,uid=65532,gid=65532 \ # Build Node environment in a separate builder stage FROM cgr.dev/chainguard/wolfi-base:latest as node-builder -ENV NODE_VERSION 20=~20.11 -ENV NODE_ENV production +ENV NODE_VERSION="20=~20.11" +ENV NODE_ENV=production RUN --mount=type=cache,target=/var/cache/apk,sharing=locked,uid=65532,gid=65532 \ --mount=type=cache,target=/var/lib/apk,sharing=locked,uid=65532,gid=65532 \ @@ -52,20 +52,20 @@ RUN --mount=type=cache,target=/home/nonroot/.yarn/berry/cache,sharing=locked,uid # Final stage to build the application image FROM cgr.dev/chainguard/wolfi-base:latest -ENV PYTHON_VERSION 3.12=~3.12 -ENV NODE_VERSION 20=~20.14 -ENV NODE_ENV production +ENV PYTHON_VERSION="3.12=~3.12" +ENV NODE_VERSION="20=~20.14" +ENV NODE_ENV=production RUN --mount=type=cache,target=/var/cache/apk,sharing=locked,uid=65532,gid=65532 \ --mount=type=cache,target=/var/lib/apk,sharing=locked,uid=65532,gid=65532 \ apk update && \ apk add \ # add node for backstage - nodejs-$NODE_VERSION \ + nodejs-$NODE_VERSION \ # add python for backstage techdocs - python-$PYTHON_VERSION \ + python-$PYTHON_VERSION \ # add tini for init process - tini + tini WORKDIR /app diff --git a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md index deef356d84..3aab9261c9 100644 --- a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md +++ b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md @@ -111,3 +111,7 @@ The `proxy-agent` package can be used as an alternative to `global-agent` (do no ``` 4. Start the backend with `yarn start` + +## Backstage CLI + +The Backstage CLI [versions:bump](https://backstage.io/docs/tooling/cli/commands#versionsbump) command also supports proxies via `global-agent` environment variable configuration. See the [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated/#proxy) docs for more information. diff --git a/docs/backend-system/building-plugins-and-modules/01-index.md b/docs/backend-system/building-plugins-and-modules/01-index.md index 4dfca00d4e..db78faaced 100644 --- a/docs/backend-system/building-plugins-and-modules/01-index.md +++ b/docs/backend-system/building-plugins-and-modules/01-index.md @@ -159,8 +159,7 @@ The same applies for modules that perform their own migrations and interact with the database. They will run on the same logical database instance as the target plugin, so care must be taken to choose table names that do not risk colliding with those of the plugin. A recommended naming pattern is `__`, for example the `@backstage/backend-tasks` package creates -tables named `backstage_backend_tasks__
`. If you use the default [`Knex` migration facilities](https://knexjs.org/guide/migrations.html), you will also +name>__
`, for example the scheduler core service creates tables named `backstage_backend_tasks__
`, because it used to be the case that the service lived in a package named `@backstage/backend-tasks`. Things have since moved around a bit, but the effects of the rule are still visible. If you use the default [`Knex` migration facilities](https://knexjs.org/guide/migrations.html), you will also want to make sure that it uses similarly prefixed migration state tables for its internal bookkeeping needs, so they do not collide with the main ones used by the plugin itself. You can do this as follows: diff --git a/docs/backend-system/building-plugins-and-modules/02-testing.md b/docs/backend-system/building-plugins-and-modules/02-testing.md index addcc72c84..38107f0242 100644 --- a/docs/backend-system/building-plugins-and-modules/02-testing.md +++ b/docs/backend-system/building-plugins-and-modules/02-testing.md @@ -31,17 +31,20 @@ import { myPlugin } from './plugin.ts'; describe('myPlugin', () => { it('can serve values from config', async () => { const fakeConfig = { myPlugin: { value: 7 } }; + const mockLogger = mockServices.logger.mock(); const { server } = await startTestBackend({ features: [ myPlugin(), mockServices.rootConfig.factory({ data: fakeConfig }), + mockLogger, ], }); const response = await request(server).get('/api/example/get-value'); expect(response.status).toBe(200); expect(response.body).toEqual({ value: 7 }); + expect(mockLogger.info).toHaveBeenCalledWith('Starting myPlugin'); }); }); ``` @@ -53,6 +56,76 @@ The returned server also has a `port()` method which returns the dynamically bound listening port. You can use this to perform lower level network interactions with the running test service. +### mock services + +The [`mockServices`](https://backstage.io/docs/reference/backend-test-utils.mockservices) object from `@backstage/backend-test-utils` provides service factory functions, and mocks for all core services that you can use to verify interactions between plugin and services. + +All mock services provide a factory function that is sufficient for most tests. Here's an example: + +```ts +const fakeConfig = { myPlugin: { value: 7 } }; +const { server } = await startTestBackend({ + features: [ + // Will provide access to the default urlReaders automatically. + mockServices.urlReader.factory(), + // Some factories accept options, in this example we provide some fake config. + mockServices.rootConfig.factory({ data: fakeConfig }), + ], +}); +``` + +There might be situations where you want to mock a service implementation to verify interactions, in those cases you can use the `mock` function to get a mock object that you can interact with. Here's an example: + +```ts +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { myPlugin } from './plugin.ts'; + +describe('myPlugin', () => { + it('should call use UrlReader', async () => { + const mockReader = mockServices.urlReader.mock(); + + await startTestBackend({ + features: [myPlugin(), mockReader], + }); + + expect(mockReader.readUrl).toHaveBeenCalledWith('https://backstage.io'); + }); + + it('should call use UrlReader again', async () => { + const partialImpl = jest.fn(); + await startTestBackend({ + features: [ + myPlugin(), + // You could also supply partial implementations to the mock function. + mockServices.urlReader.mock({ readUrl: partialImpl }), + ], + }); + expect(partialImpl).toHaveBeenCalledWith('https://backstage.io'); + }); +}); +``` + +Available services: + +- [`auth`](https://backstage.io/docs/reference/backend-test-utils.mockservices.auth/) +- [`cache`](https://backstage.io/docs/reference/backend-test-utils.mockservices.cache/) +- [`database`](https://backstage.io/docs/reference/backend-test-utils.mockservices.database/) +- [`discovery`](https://backstage.io/docs/reference/backend-test-utils.mockservices.discovery/) +- [`events`](https://backstage.io/docs/reference/backend-test-utils.mockservices.events/) +- [`httpAuth`](https://backstage.io/docs/reference/backend-test-utils.mockservices.httpAuth/) +- [`httpRouter`](https://backstage.io/docs/reference/backend-test-utils.mockservices.httpRouter/) +- [`lifecycle`](https://backstage.io/docs/reference/backend-test-utils.mockservices.lifecycle/) +- [`logger`](https://backstage.io/docs/reference/backend-test-utils.mockservices.logger/) +- [`permissions`](https://backstage.io/docs/reference/backend-test-utils.mockservices.permissions/) +- [`rootConfig`](https://backstage.io/docs/reference/backend-test-utils.mockservices.rootConfig/) +- [`rootHealth`](https://backstage.io/docs/reference/backend-test-utils.mockservices.rootHealth/) +- [`rootHttpRouter`](https://backstage.io/docs/reference/backend-test-utils.mockservices.rootHttpRouter/) +- [`rootLifecycle`](https://backstage.io/docs/reference/backend-test-utils.mockservices.rootLifecycle/) +- [`rootLogger`](https://backstage.io/docs/reference/backend-test-utils.mockservices.rootLogger/) +- [`scheduler`](https://backstage.io/docs/reference/backend-test-utils.mockservices.scheduler/) +- [`urlReader`](https://backstage.io/docs/reference/backend-test-utils.mockservices.urlReader/) +- [`userInfo`](https://backstage.io/docs/reference/backend-test-utils.mockservices.userInfo/) + ## Testing Remote Service Interactions If your backend plugin or service interacts with external services using HTTP diff --git a/docs/backend-system/building-plugins-and-modules/08-migrating.md b/docs/backend-system/building-plugins-and-modules/08-migrating.md index 699852c494..8b2ea17de8 100644 --- a/docs/backend-system/building-plugins-and-modules/08-migrating.md +++ b/docs/backend-system/building-plugins-and-modules/08-migrating.md @@ -255,3 +255,67 @@ backend.add( Checkout the [custom service implementations](https://backstage.io/docs/backend-system/building-backends/index#custom-service-implementations) documentation and also the [core service configurations](https://backstage.io/docs/backend-system/core-services/index) page in case you'd like to create your own custom mock factory for one or more services. 3. Now you can finally start your plugin locally by running `yarn start` from the root folder of your plugin. + +## Remove support for the old backend system + +Given that you have followed the guide above to export your new backend plugin the steps to deprecate and remove the old backend plugin are the following: + +### Deprecate public exports other than the default export + +First of all make sure that `createRouter` and `routerOptions` are marked as deprecated to give users time and an indication to migrate to the new system (we recommend deprecating in one release and remove the deprecates in the following one). +This is done by adding a `@deprecated` annotation to the legacy exports. It's worth noting that the plugin can continue using `createRouter` internally but it should not be exported as part of public api. If you are reusing the create router and relative imports in migrated plugins, ensure that you refactor the internal code to remove deprecated imports once the `createRouter` export gets deleted. It is recommended that you avoid the use of `@backstage/backend-common` and `@backstage/backend-tasks` in migrated plugins as they will be deleted together with the ending of support for the legacy system. There are instructions in most of the deprecated imports about how to stop using them once you have migrated to the new backend system. + +```ts title="@backstage/plugin-kubernetes-backend/src/service/router.ts" +import { KubernetesBuilder } from './KubernetesBuilder'; + +/** +* @public +// highlight-add-next-line +* @deprecated Please migrate to the new backend system. +*/ +export interface RouterOptions { + logger: Logger; + config: Config; + catalogApi: CatalogApi; + clusterSupplier?: KubernetesClustersSupplier; + discovery: PluginEndpointDiscovery; +} + +/** +* @public +// highlight-add-next-line +* @deprecated Please migrate to the new backend system. +*/ +export async function createRouter( + options: RouterOptions, +): Promise { + const { router } = await KubernetesBuilder.createBuilder(options) + .setClusterSupplier(options.clusterSupplier) + .build(); + return router; +} +``` + +If your plugin contains an `api-report.md` file make sure to run `yarn build:api-reports` afterwards. +It's recommended to inspect the api report and look for other exports other than the new backend plugin, they should most likely also be deprecated as plugins in the new backend system are extended using extension points and not directly by passing options. Any type of builder or helper methods that are used together with the backend plugin should be moved to a library package specifically for that plugin (e.g. a `plugin-kubernetes-backend-node` package, see the [package roles](https://backstage.io/docs/tooling/cli/build-system/#package-roles) documentation for more details). + +After removals of deprecations all your `index.ts` should contain is just the default export: + +```ts title="@backstage/plugin-kubernetes-backend/src/index.ts" +export { kubernetesPlugin as default } from './plugin'; +``` + +### Deprecate the `/alpha` subpath if it exists + +In cases where you previously supported the new backend system using an `alpha` export, please deprecate the alpha exports and re-export them from `index.ts`. + +```ts title="@backstage/-backend/src/alpha.ts" +/** +* @alpha +// highlight-add-next-line +* @deprecated Please import from the root path instead. +*/ +export default createPlugin({ + //... +}); +``` diff --git a/docs/backend-system/core-services/database.md b/docs/backend-system/core-services/database.md index e083f0b44d..2a172cbe46 100644 --- a/docs/backend-system/core-services/database.md +++ b/docs/backend-system/core-services/database.md @@ -20,7 +20,7 @@ import { coreServices, createBackendPlugin, } from '@backstage/backend-plugin-api'; -import { resolvePackagePath } from '@backstage/backend-common'; +import { resolvePackagePath } from '@backstage/backend-plugin-api'; createBackendPlugin({ pluginId: 'example', diff --git a/docs/backend-system/core-services/url-reader.md b/docs/backend-system/core-services/url-reader.md index b9616a029e..242ffd2307 100644 --- a/docs/backend-system/core-services/url-reader.md +++ b/docs/backend-system/core-services/url-reader.md @@ -45,3 +45,114 @@ createBackendPlugin({ }, }); ``` + +## Providing custom URL readers + +You can also create an internal or bespoke reader and provide it to the backend using a service factory. The following example shows how to create a custom URL reader and provide it to the backend. + +```ts title="packages/backend/src/index.ts" +import { createBackend } from '@backstage/backend-defaults'; +import { + ReaderFactory, + urlReaderFactoriesServiceRef, +} from '@backstage/backend-defaults/urlReader'; +import { + createServiceFactory, + UrlReaderService, +} from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; + +class CustomUrlReader implements UrlReaderService { + static factory: ReaderFactory = ({ config, treeResponseFactory }) => { + const reader = new CustomUrlReader(config); + const predicate = (url: URL) => url.host === 'myCustomDomain'; + return [{ reader, predicate }]; + }; + + constructor(private readonly config: Config) {} + // implementations of read, readTree and search methods skipped for this example +} + +const customReader = createServiceFactory({ + service: urlReaderFactoriesServiceRef, + deps: {}, + async factory() { + return CustomUrlReader.factory; + }, +}); + +const backend = createBackend(); +// backend.add() of other plugins and modules excluded +backend.add(customReader); +``` + +## Writing URL Readers + +We want to make sure all URL Readers behave in the same way. Hence if possible, +all the methods of the `UrlReaderService` interface should be implemented. However it +is okay to start by implementing just one of them and creating issues for the +remaining ones. + +You can choose to make new URL Readers open source if the use case is beneficial to other users. Either as its own package or by updating the +[`default` factory](https://github.com/backstage/backstage/blob/ce2ca68f07ad3334401d3277b989bf145b728a64/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts#L82-L102) +method of URL Readers. It's recommended to create an issue in the Backstage repository to discuss the use case and get feedback before starting the implementation of a new core URL Reader. + +Here are some general guidelines for writing URL Readers + +#### `readUrl` + +`readUrl` method expects a user-friendly URL, something which can be copied from +the browser naturally when a person is browsing the provider in their browser. + +- ✅ Valid URL : + `https://github.com/backstage/backstage/blob/master/ADOPTERS.md` +- ❌ Not a valid URL : + `https://raw.githubusercontent.com/backstage/backstage/master/ADOPTERS.md` +- ❌ Not a valid URL : `https://github.com/backstage/backstage/ADOPTERS.md` + +Upon receiving the URL, `readUrl` converts the user-friendly URL into an API URL +which can be used to request the provider's API. + +`readUrl` then makes an authenticated request to the provider API and returns the response containing the file's contents and `ETag` (if the provider supports it). + +#### `readTree` + +`readTree` method also expects user-friendly URLs similar to `read` but the URL +should point to a tree (could be the root of a repository or even a +sub-directory). + +- ✅ Valid URL : `https://github.com/backstage/backstage` +- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master` +- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/docs` + +Using the provider's API documentation, find out an API endpoint which can be +used to download either a zip or a tarball. You can download the entire tree +(e.g. a repository) and filter out in case the user is expecting only a +sub-tree. But some APIs are smart enough to accept a path and return only a +sub-tree in the downloaded archive. + +#### `search` + +`search` method expects a glob pattern of a URL and returns a list of files +matching the query. + +- ✅ Valid URL : + `https://github.com/backstage/backstage/blob/master/**/catalog-info.yaml` +- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/**/*.md` +- ✅ Valid URL : + `https://github.com/backstage/backstage/blob/master/*/package.json` +- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/README` + +The core logic of `readTree` can be used here to extract all the files inside +the tree and return the files matching the pattern in the `url`. + +### Caching + +All of the methods above support ETag based caching. If the method is called +without an ETag, the response contains the ETag of the resource (should ideally +forward the ETag returned by the provider). If the method is called with an +ETag, it first compares the ETag and returns a `NotModifiedError` in case the +resource has not been modified. This approach is very similar to the actual +[`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) and +[`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) +HTTP headers. diff --git a/docs/contribute/project-structure.md b/docs/contribute/project-structure.md index 16f20a66ca..f06fba0523 100644 --- a/docs/contribute/project-structure.md +++ b/docs/contribute/project-structure.md @@ -82,9 +82,11 @@ are separated out into their own folder, see further down. package. The `backend` uses plugins to construct a working backend that the frontend (`app`) can use. -- [`backend-common/`](https://github.com/backstage/backstage/tree/master/packages/backend-common) - - There are no "core" packages in the backend. Instead we have `backend-common` - which contains helper middleware and other utils. +- [`backend-app-api/`](https://github.com/backstage/backstage/tree/master/packages/backend-app-api) - + This package contains the central wiring for how to make Backstage backends. + +- [`backend-plugin-api/`](https://github.com/backstage/backstage/tree/master/packages/backend-plugin-api) - + This package contains the core APIs that are used to make Backstage backend features such as plugins and modules. - [`catalog-client`](https://github.com/backstage/backstage/tree/master/packages/catalog-client) - An isomorphic client to interact with the Software Catalog. Backend plugins diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index 897498d28f..7b71c2eb61 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -81,7 +81,7 @@ USER node WORKDIR /app # This switches many Node.js dependencies to production mode. -ENV NODE_ENV production +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, @@ -263,7 +263,7 @@ COPY --chown=node:node app-config*.yaml ./ COPY --chown=node:node examples ./examples # This switches many Node.js dependencies to production mode. -ENV NODE_ENV production +ENV NODE_ENV=production CMD ["node", "packages/backend", "--config", "app-config.yaml", "--config", "app-config.production.yaml"] ``` diff --git a/docs/deployment/flightcontrol.md b/docs/deployment/flightcontrol.md index 5d59371b47..14e0b308a6 100644 --- a/docs/deployment/flightcontrol.md +++ b/docs/deployment/flightcontrol.md @@ -68,7 +68,7 @@ USER node WORKDIR /app # This switches many Node.js dependencies to production mode. -ENV NODE_ENV production +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, diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index d53b8bdf2f..feb0f68048 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -95,7 +95,7 @@ import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-rea import { ValidateKebabCase, validateKebabCaseValidation, -} from './ValidateKebabCase/ValidateKebabCaseExtension'; +} from './ValidateKebabCaseExtension'; export const ValidateKebabCaseFieldExtension = scaffolderPlugin.provide( createScaffolderFieldExtension({ diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 59198e919b..e47af8d1d7 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -580,6 +580,10 @@ Note: To refer external diagram files, we need to include the diagrams directory ## How to add Mermaid support in TechDocs +There are two options for adding Mermaid support in TechDocs: using [Kroki](https://kroki.io) or by using [markdown-inline-mermaid](https://github.com/johanneswuerbach/markdown-inline-mermaid). We currently use `markdown-inline-mermaid` for the [Mermaid example on the Demo site](https://demo.backstage.io/docs/default/component/backstage-demo/examples/mermaid/). + +### Using Kroki + To add `Mermaid` support in TechDocs, you can use [`kroki`](https://kroki.io) that creates diagrams from Textual descriptions. It is a single rendering gateway for all popular diagrams-as-a-code tools. It supports an enormous number @@ -678,6 +682,30 @@ Done! Now you have a support of the following diagrams along with mermaid: - `Vega-Lite` - `WaveDrom` +### Using `markdown-inline-mermaid` + +To use `markdown-inline-mermaid` to generate your Mermaid diagrams in TechDocs you'll need to do the following: + +1. In your Dockerfile you will need to make sure you install `markdown-inline-mermaid` like this: `RUN pip3 install mkdocs-techdocs-core markdown-inline-mermaid` +2. You will also need to install the `@mermaid-js/mermaid-cli`, to do that add this: `RUN yarn global add @mermaid-js/mermaid-cli` +3. Now in your `mkdocs.yml` file you will need to add the following section (this is at the root level like `plugins` which you should already have): + + ```yaml title="mkdocs.yml" + markdown_extensions: + - markdown_inline_mermaid + ``` + +4. With this in place you can now add Mermaid diagrams in your Markdown files like this: + + ````md + ```mermaid + sequenceDiagram + Alice->>John: Hello John, how are you? + John-->>Alice: Great! + Alice-)John: See you later! + ``` + ```` + ## How to implement a hybrid build strategy One limitation of the [Recommended deployment](./architecture.md#recommended-deployment) is that diff --git a/docs/frontend-system/architecture/10-app.md b/docs/frontend-system/architecture/10-app.md index 532971d124..d1c3820f09 100644 --- a/docs/frontend-system/architecture/10-app.md +++ b/docs/frontend-system/architecture/10-app.md @@ -16,7 +16,7 @@ Below is a simple example of how to create and render an app instance: ```ts import ReactDOM from 'react-dom/client'; -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; // Create your app instance const app = createApp({ diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index d47e2697a9..785843f6e0 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -295,7 +295,7 @@ export default createExtensionOverrides({ Assuming the above code resides in the `@internal/search-page` package, you can install it in your app like this: ```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; import searchPageOverride from '@internal/search-page'; const app = createApp({ diff --git a/docs/frontend-system/architecture/36-routes.md b/docs/frontend-system/architecture/36-routes.md index e684932414..2a9b372b1c 100644 --- a/docs/frontend-system/architecture/36-routes.md +++ b/docs/frontend-system/architecture/36-routes.md @@ -256,7 +256,7 @@ app: We also have the ability to express this in code as an option to `createApp`, but you of course only need to use one of these two methods: ```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; import catalog from '@backstage/plugin-catalog'; import scaffolder from '@backstage/plugin-scaffolder'; diff --git a/docs/frontend-system/building-apps/01-index.md b/docs/frontend-system/building-apps/01-index.md index 16aacc1185..5b5298a2c5 100644 --- a/docs/frontend-system/building-apps/01-index.md +++ b/docs/frontend-system/building-apps/01-index.md @@ -30,13 +30,13 @@ The created-app is currently templated for legacy frontend system applications, ## The app instance -The starting point of a frontend app is the `createApp` function, which accepts a single options object as its only parameter. It is imported from `@backstage/frontend-app-api`, which is where you will find most of the common APIs for building apps. +The starting point of a frontend app is the `createApp` function, which accepts a single options object as its only parameter. It is imported from `@backstage/frontend-defaults`, which is where you will find most of the common APIs for building apps. This is how to create a minimal app: ```tsx title="in src/index.ts" import ReactDOM from 'react-dom/client'; -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; import catalogPlugin from '@backstage/plugin-catalog/alpha'; // Create your app instance @@ -87,7 +87,7 @@ Previously you would customize the application routes, components, apis, sidebar A manual installation is required if your packages are not discovered automatically, either because you are not using `@backstage/cli` to build your application or because the features are defined in local modules in the app package. In order to manually install a feature, you must import it and pass it to the `createApp` function: ```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; // This plugin was create as a local module in the app import { somePlugin } from './plugins'; @@ -104,10 +104,10 @@ You can also pass overrides to the features array, for more details, please read ### Using an async features loader -In case you need to perform asynchronous operations before passing features to the `createApp` function, define a [feature loader](https://backstage.io/docs/reference/frontend-app-api.createappfeatureloader/) object and pass it to the `features` option: +In case you need to perform asynchronous operations before passing features to the `createApp` function, define a [feature loader](https://backstage.io/docs/reference/frontend-defaults.createappfeatureloader/) object and pass it to the `features` option: ```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; const app = createApp({ features: { @@ -129,7 +129,7 @@ export default app.createRoot(); In some cases we want to load our configuration from a backend server and to do so, you can pass an callback to the `configLoader` option when calling the `createApp` function, the callback should return a promise of an object with the config object: ```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; import { getConfigFromServer } from './utils'; // Example lazy loading the app configuration diff --git a/docs/frontend-system/building-apps/06-plugin-conversion.md b/docs/frontend-system/building-apps/06-plugin-conversion.md new file mode 100644 index 0000000000..5e7d431e73 --- /dev/null +++ b/docs/frontend-system/building-apps/06-plugin-conversion.md @@ -0,0 +1,87 @@ +--- +id: plugin-conversion +title: Converting 3rd-party plugins from the old system +sidebar_label: Converting 3rd-party Plugins +# prettier-ignore +description: Documentation for how to convert 3rd-party plugins to support the new frontend system. +--- + +If you are using or want to use a 3rd-party plugin that does not yet support the new frontend system in your app, you can often use conversion utilities from `@backstage/core-compat-api` in order wrap the plugin to make it possible to install in your app. + +> [!CAUTION] +> The purpose of these utilities is to wrap 3rd-party plugins. Do not use them for your own plugins where you can add support for the new frontend system directly. + +## Converting a legacy plugin + +The `@backstage/core-compat-api` package exports the `convertLegacyPlugin` function that can be used to convert a legacy plugin to the new frontend system. Simply pass in the old plugin instance and the new plugin will be returned. + +```ts +import { techdocsPlugin } from '@backstage/plugin-techdocs'; + +// TechDocs is used as an example here, it already supports +// the new frontend system so don't use this code directly. +const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, { + extensions: [], +}); +``` + +Note that when using `convertLegacyPlugin` we also have to pass an array of extensions. This is because the `convertLegacyPlugin` function does not have access to or know about the different types of extensions in the old system. You instead need to manually convert each of the old extensions that you want to include in the plugin instance. + +## Converting legacy extensions + +As mentioned above, you need to manually convert each of the old extensions that you want to include in the plugin instance. The `@backstage/core-compat-api` package exports `convertLegacyPageExtension` for this purpose, which can be used to convert a top-level page extension to the new system. Simply pass in the old extension and the new extension will be returned. + +```ts +const convertedIndexPage = convertLegacyPageExtension(TechDocsIndexPage); +``` + +### Overriding inferred parameters + +The conversion functions such as `convertLegacyPageExtension` will attempt to infer parameters from the old extension, in particular it uses the name of the old extension to determine things like the name and route paths of the new extension. In some cases you may need to supply your own parameters to the conversion function if the inferred values are not correct. + +```ts +const convertedIndexPage = convertLegacyPageExtension(TechDocsIndexPage, { + name: 'index', + defaultPath: '/docs', +}); +``` + +### Other types of extensions + +The `@backstage/core-compat-api` only provides conversion functions for the built-in extension types, i.e. only page extensions. There are many plugins that define their own extension types, such as the entity content and card extensions for the catalog. For those cases it's up to the plugin libraries to provide their own conversion functions for these types of extensions. For example, `@backstage/plugin-catalog-react/alpha` provides both `convertLegacyEntityContentExtension` and `convertLegacyEntityCardExtension`. + +The following are the known conversion functions provided by various libraries: + +- `convertLegacyPageExtension` - `@backstage/core-compat-api` +- `convertLegacyEntityContentExtension` - `@backstage/plugin-catalog-react/alpha` +- `convertLegacyEntityCardExtension` - `@backstage/plugin-catalog-react/alpha` + +## Putting it all together + +Using the plugin converter along with extension converters from various libraries, we can not more fully convert our 3rd-party plugin to be able to install it in an app built with the new frontend system: + +```ts +import { + techdocsPlugin, + TechDocsIndexPage, + TechDocsReaderPage, + EntityTechdocsContent, +} from '@backstage/plugin-techdocs'; + +const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, { + extensions: [ + convertLegacyPageExtension(TechDocsIndexPage, { + name: 'index', + defaultPath: '/docs', + }), + convertLegacyPageExtension(TechDocsReaderPage, { + defaultPath: '/docs/:namespace/:kind/:name/*', + }), + convertLegacyEntityContentExtension(EntityTechdocsContent), + ], +}); + +const app = createApp({ + features: [convertedTechdocsPlugin], +}); +``` diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md index ceb3f34c35..96e7dd2bd5 100644 --- a/docs/frontend-system/building-apps/08-migrating.md +++ b/docs/frontend-system/building-apps/08-migrating.md @@ -18,7 +18,7 @@ The first step in migrating an app is to switch out the `createApp` function for // highlight-remove-next-line import { createApp } from '@backstage/app-defaults'; // highlight-add-next-line -import { createApp } from '@backstage/frontend-app-api'; +import { createApp } from '@backstage/frontend-defaults'; ``` This immediate switch will lead to a lot of breakages that we need to fix. @@ -495,7 +495,7 @@ The entity pages are typically defined in `packages/app/src/components/catalog` New apps feature a built-in sidebar extension (`app/nav`) that will render all nav item extensions provided by plugins. This is a placeholder implementation and not intended as a long-term solution. In the future we will aim to provide a more flexible sidebar extension that allows for more customization out of the box. -Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/packages/frontend-app-api/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/25-extension-overrides.md): +Because the built-in sidebar is quite limited you may want to override the sidebar with your own custom implementation. To do so, use `createExtension` directly and refer to the [original sidebar implementation](https://github.com/backstage/backstage/blob/master/plugins/app/src/extensions/AppNav.tsx). The following is an example of how to take your existing sidebar from the `Root` component that you typically find in `packages/app/src/components/Root.tsx`, and use it in an [extension override](../architecture/25-extension-overrides.md): ```tsx const nav = createExtension({ diff --git a/docs/frontend-system/building-plugins/05-migrating.md b/docs/frontend-system/building-plugins/05-migrating.md index f0abc97c81..cf4d82002a 100644 --- a/docs/frontend-system/building-plugins/05-migrating.md +++ b/docs/frontend-system/building-plugins/05-migrating.md @@ -15,9 +15,9 @@ The main concept is that routes, components, apis are now extensions. You can us In the legacy frontend system a plugin was defined in its own `plugin.ts` file as following: ```ts title="my-plugin/src/plugin.ts" - import { createFrontendPlugin } from '@backstage/core-plugin-api'; + import { createPlugin } from '@backstage/core-plugin-api'; - export const myPlugin = createFrontendPlugin({ + export const myPlugin = createPlugin({ id: 'my-plugin', apis: [], routes: { diff --git a/docs/getting-started/keeping-backstage-updated.md b/docs/getting-started/keeping-backstage-updated.md index 88e4a02ef2..19062904e2 100644 --- a/docs/getting-started/keeping-backstage-updated.md +++ b/docs/getting-started/keeping-backstage-updated.md @@ -76,3 +76,15 @@ While package duplication might be acceptable in many cases, you might want to deduplicate packages for the purpose of optimizing bundle size and installation speed. We recommend using deduplication utilities such as `yarn dedupe` to trim down the number of duplicate packages. + +## Proxy + +The Backstage CLI uses [global-agent](https://www.npmjs.com/package/global-agent) to configure HTTP/HTTPS proxy settings using environment variables. This allows you to route the CLI’s network traffic through a proxy server, which can be useful in environments with restricted internet access. + +### Example Configuration + +```bash +export GLOBAL_AGENT_HTTP_PROXY=http://proxy.company.com:8080 +export GLOBAL_AGENT_HTTPS_PROXY=https://secure-proxy.company.com:8080 +export GLOBAL_AGENT_NO_PROXY=localhost,internal.company.com +``` diff --git a/docs/integrations/bitbucketServer/discovery--old.md b/docs/integrations/bitbucketServer/discovery--old.md new file mode 100644 index 0000000000..d8ef11533b --- /dev/null +++ b/docs/integrations/bitbucketServer/discovery--old.md @@ -0,0 +1,123 @@ +--- +id: discovery +title: Bitbucket Server Discovery +sidebar_label: Discovery +# prettier-ignore +description: Automatically discovering catalog entities from repositories in Bitbucket Server +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./discovery.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +The Bitbucket Server integration has a special entity provider for discovering +catalog files located in Bitbucket Server. +The provider will search your Bitbucket Server account and register catalog files matching the configured path +as Location entity and via following processing steps add all contained catalog entities. +This can be useful as an alternative to static locations or manually adding things to the catalog. + +## Installation + +You will have to add the entity provider in the catalog initialization code of your +backend. The provider is not installed by default, therefore you have to add a +dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend +package. + +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server +``` + +And then add the entity provider to your catalog builder: + +```ts title="packages/backend/src/plugins/catalog.ts" +/* highlight-add-next-line */ +import { BitbucketServerEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-server'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await CatalogBuilder.create(env); + /* highlight-add-start */ + builder.addEntityProvider( + BitbucketServerEntityProvider.fromConfig(env.config, { + logger: env.logger, + scheduler: env.scheduler, + }), + ); + /* highlight-add-end */ + + // .. +} +``` + +## Configuration + +To use the entity provider, you'll need a [Bitbucket Server integration set up](locations.md). + +Additionally, you need to configure your entity provider instance(s): + +```yaml title="app-config.yaml" +catalog: + providers: + bitbucketServer: + yourProviderId: # identifies your ingested dataset + host: 'bitbucket.mycompany.com' + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.*$' # optional; RegExp + repoSlug: '^service-.*$' # optional; RegExp + skipArchivedRepos: true # optional; boolean + schedule: # same options as in TaskScheduleDefinition + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } +``` + +- **`host`**: + The host of the Bitbucket Server instance, **note**: the host needs to registered as an integration as well, see [location](locations.md). +- **`catalogPath`** _(optional)_: + Default: `/catalog-info.yaml`. + Path where to look for `catalog-info.yaml` files. + When started with `/`, it is an absolute path from the repo root. +- **`filters`** _(optional)_: + - **`projectKey`** _(optional)_: + Regular expression used to filter results based on the project key. + - **`repoSlug`** _(optional)_: + Regular expression used to filter results based on the repo slug. + - **`skipArchivedRepos`** _(optional)_: + Boolean flag to filter out archived repositories. +- **`schedule`**: + - **`frequency`**: + How often you want the task to run. The system does its best to avoid overlapping invocations. + - **`timeout`**: + The maximum amount of time that a single task invocation can take. + - **`initialDelay`** _(optional)_: + The amount of time that should pass before the first invocation happens. + - **`scope`** _(optional)_: + `'global'` or `'local'`. Sets the scope of concurrency control. + +## Custom location processing + +The Bitbucket Server Entity Provider will by default emit a location for each +matching repository. However, it is possible to override this functionality and take full control of how each +matching repository is processed. + +`BitbucketServerEntityProvider.fromConfig` takes an optional parameter +`options.parser` where you can set your own parser to be used for each matched +repository. + +```typescript +const provider = BitbucketServerEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler, + parser: async function* customLocationParser(options: { + location: LocationSpec; + client: BitbucketServerClient; + }) { + // Custom logic for interpreting the matching repository + // See defaultBitbucketServerLocationParser for an example + }, +}); +``` diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md index 133978a8fb..3bc37e4cb5 100644 --- a/docs/integrations/bitbucketServer/discovery.md +++ b/docs/integrations/bitbucketServer/discovery.md @@ -6,6 +6,10 @@ sidebar_label: Discovery description: Automatically discovering catalog entities from repositories in Bitbucket Server --- +:::info +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./discovery--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + The Bitbucket Server integration has a special entity provider for discovering catalog files located in Bitbucket Server. The provider will search your Bitbucket Server account and register catalog files matching the configured path @@ -16,34 +20,21 @@ This can be useful as an alternative to static locations or manually adding thin You will have to add the entity provider in the catalog initialization code of your backend. The provider is not installed by default, therefore you have to add a -dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend -package. +dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend package. ```bash title="From your Backstage root directory" yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server ``` -And then add the entity provider to your catalog builder: +And update your backend by adding the following line: -```ts title="packages/backend/src/plugins/catalog.ts" -/* highlight-add-next-line */ -import { BitbucketServerEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-server'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const builder = await CatalogBuilder.create(env); - /* highlight-add-start */ - builder.addEntityProvider( - BitbucketServerEntityProvider.fromConfig(env.config, { - logger: env.logger, - scheduler: env.scheduler, - }), - ); - /* highlight-add-end */ - - // .. -} +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-catalog-backend/alpha')); +/* highlight-add-start */ +backend.add( + import('@backstage/plugin-catalog-backend-module-bitbucket-server/alpha'), +); +/* highlight-add-end */ ``` ## Configuration @@ -92,27 +83,3 @@ catalog: The amount of time that should pass before the first invocation happens. - **`scope`** _(optional)_: `'global'` or `'local'`. Sets the scope of concurrency control. - -## Custom location processing - -The Bitbucket Server Entity Provider will by default emit a location for each -matching repository. However, it is possible to override this functionality and take full control of how each -matching repository is processed. - -`BitbucketServerEntityProvider.fromConfig` takes an optional parameter -`options.parser` where you can set your own parser to be used for each matched -repository. - -```typescript -const provider = BitbucketServerEntityProvider.fromConfig(env.config, { - logger: env.logger, - schedule: env.scheduler, - parser: async function* customLocationParser(options: { - location: LocationSpec; - client: BitbucketServerClient; - }) { - // Custom logic for interpreting the matching repository - // See defaultBitbucketServerLocationParser for an example - }, -}); -``` diff --git a/docs/notifications/index.md b/docs/notifications/index.md index 95d77a9b7f..86a67ce211 100644 --- a/docs/notifications/index.md +++ b/docs/notifications/index.md @@ -54,7 +54,7 @@ backend.add(import('@backstage/plugin-notifications-backend')); ### Add Notifications Frontend ```bash -yarn workspace app add @backstage/notifications +yarn workspace app add @backstage/plugin-notifications ``` To add the notifications main menu, add following to your `packages/app/src/components/Root/Root.tsx`: @@ -109,22 +109,23 @@ Start with: yarn workspace app add @backstage/plugin-signals ``` -To install the plugin, you have to add the following to your `packages/app/src/plugins.ts`: +To install the plugin, add the `SignalsDisplay` to your app root in `packages/app/src/App.tsx`: -```ts -export { signalsPlugin } from '@backstage/plugin-signals'; -``` +```tsx +import { SignalsDisplay } from '@backstage/plugin-signals'; -And make sure that your `packages/app/src/App.tsx` contains: - -```ts -import * as plugins from './plugins'; - -const app = createApp({ - // ... - plugins: Object.values(plugins), - // ... -}); +export default app.createRoot( + <> + + + {/* highlight-add-next-line */} + + + + {routes} + + , +); ``` If the signals plugin is properly configured, it will be automatically discovered by the notifications plugin and used. @@ -218,6 +219,59 @@ React.useEffect(() => { }, [lastSignal, notificationsApi]); ``` +#### Using signals in your own plugin + +It's possible to use signals in your own plugin to deliver data from the backend to the frontend in near real-time. + +To use signals in your own frontend plugin, you need to add the `useSignal` hook from `@backstage/plugin-signals-react` from `@backstage/plugin-notifications-common` with optional generic type of the signal. + +```ts +// To use the same type of signal in the backend, this should be placed in a shared common package +export type MySignalType = { + user: string; + data: string; + // .... +}; + +const { lastSignal } = useSignal('my-plugin'); + +useEffect(() => { + if (lastSignal) { + // Do something with the signal + } +}, [lastSignal]); +``` + +To send signals from the backend plugin, you must add the `signalsServiceRef` to your plugin or module as a dependency. + +```ts +import { signalsServiceRef } from '@backstage/plugin-signals-node'; +export const myPlugin = createBackendPlugin({ + pluginId: 'my', + register(env) { + env.registerInit({ + deps: { + httpRouter: coreServices.httpRouter, + signals: signalsServiceRef, + }, + async init({ httpRouter, signals }) { + httpRouter.use( + await createRouter({ + signals, + }), + ); + }, + }); + }, +}); +``` + +To send the signal using the service, you can use the `publish` method. + +```ts +signals.publish({ user: 'user', data: 'test' }); +``` + ### Consuming Notifications In a front-end plugin, the simplest way to query a notification is by its ID: @@ -249,14 +303,18 @@ import { Notification } from '@backstage/plugin-notifications-common'; import { NotificationProcessor } from '@backstage/plugin-notifications-node'; class MyNotificationProcessor implements NotificationProcessor { - async decorate(notification: Notification): Promise { + // preProcess is called before the notification is saved to database. + // This is a good place to modify the notification before it is saved and sent to the user. + async preProcess(notification: Notification): Promise { if (notification.origin === 'plugin-my-plugin') { notification.payload.icon = 'my-icon'; } return notification; } - async send(notification: Notification): Promise { + // postProcess is called after the notification is saved to database and the signal is emitted. + // This is a good place to send the notification to external services. + async postProcess(notification: Notification): Promise { nodemailer.sendEmail({ from: 'backstage', to: 'user', diff --git a/docs/permissions/custom-rules--old.md b/docs/permissions/custom-rules--old.md new file mode 100644 index 0000000000..7661e23327 --- /dev/null +++ b/docs/permissions/custom-rules--old.md @@ -0,0 +1,165 @@ +--- +id: custom-rules--old +title: Defining custom permission rules +description: How to define custom permission rules for existing resources +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../backend-system/index.md), being the default since Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./custom-rules.md) instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + +For some use cases, you may want to define custom [rules](../references/glossary.md#rule-permission-plugin) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of. + +## Define a custom rule + +Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`. + +We use Zod in our example below. To install, run: + +```bash +yarn workspace backend add zod +``` + +```typescript title="packages/backend/src/plugins/permission.ts" +... + +import type { Entity } from '@backstage/catalog-model'; +import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; +import { createConditionFactory } from '@backstage/plugin-permission-node'; +import { z } from 'zod'; + +export const isInSystemRule = createCatalogPermissionRule({ + name: 'IS_IN_SYSTEM', + description: 'Checks if an entity is part of the system provided', + resourceType: 'catalog-entity', + paramsSchema: z.object({ + systemRef: z + .string() + .describe('SystemRef to check the resource is part of'), + }), + apply: (resource: Entity, { systemRef }) => { + if (!resource.relations) { + return false; + } + + return resource.relations + .filter(relation => relation.type === 'partOf') + .some(relation => relation.targetRef === systemRef); + }, + toQuery: ({ systemRef }) => ({ + key: 'relations.partOf', + values: [systemRef], + }), +}); + +const isInSystem = createConditionFactory(isInSystemRule); + +... +``` + +For a more detailed explanation on defining rules, refer to the [documentation for plugin authors](./plugin-authors/03-adding-a-resource-permission-check.md#adding-support-for-conditional-decisions). + +Still in the `packages/backend/src/plugins/permission.ts` file, let's use the condition we just created in our `TestPermissionPolicy`. + +```ts title="packages/backend/src/plugins/permission.ts" +... +/* highlight-remove-next-line */ +import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; +/* highlight-add-next-line */ +import { catalogConditions, createCatalogConditionalDecision, createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; +/* highlight-remove-next-line */ +import { createConditionFactory } from '@backstage/plugin-permission-node'; +/* highlight-add-next-line */ +import { PermissionPolicy, PolicyQuery, PolicyQueryUser, createConditionFactory } from '@backstage/plugin-permission-node'; +/* highlight-add-start */ +import { AuthorizeResult, PolicyDecision, isResourcePermission } from '@backstage/plugin-permission-common'; +/* highlight-add-end */ +... + +export const isInSystemRule = createCatalogPermissionRule({ + name: 'IS_IN_SYSTEM', + description: 'Checks if an entity is part of the system provided', + resourceType: 'catalog-entity', + paramsSchema: z.object({ + systemRef: z + .string() + .describe('SystemRef to check the resource is part of'), + }), + apply: (resource: Entity, { systemRef }) => { + if (!resource.relations) { + return false; + } + + return resource.relations + .filter(relation => relation.type === 'partOf') + .some(relation => relation.targetRef === systemRef); + }, + toQuery: ({ systemRef }) => ({ + key: 'relations.partOf', + values: [systemRef], + }), +}); + +const isInSystem = createConditionFactory(isInSystemRule); + +class TestPermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + user?: PolicyQueryUser, + ): Promise { + if (isResourcePermission(request.permission, 'catalog-entity')) { + return createCatalogConditionalDecision( + request.permission, + /* highlight-remove-start */ + catalogConditions.isEntityOwner({ + claims: user?.info.ownershipEntityRefs ?? [], + }), + /* highlight-remove-end */ + /* highlight-add-start */ + { + anyOf: [ + catalogConditions.isEntityOwner({ + claims: user?.info.ownershipEntityRefs ?? [], + }), + isInSystem({ systemRef: 'interviewing' }), + ], + }, + /* highlight-add-end */ + ); + } + + return { result: AuthorizeResult.ALLOW }; + } +} + +... +``` + +## Provide the rule during plugin setup + +Now that we have a custom rule defined and added to our policy, we need provide it to the catalog plugin. This step is important because the catalog plugin will use the rule's `toQuery` and `apply` methods while evaluating conditional authorize results. There's no guarantee that the catalog and permission backends are running on the same server, so we must explicitly link the rule to ensure that it's available at runtime. + +The api for providing custom rules may differ between plugins, but there should typically be some integration point during the creation of the backend router. For the catalog, this integration point is exposed via `CatalogBuilder.addPermissionRules`. + +```typescript title="packages/backend/src/plugins/catalog.ts" +import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; +/* highlight-add-next-line */ +import { isInSystemRule } from './permission'; + +... + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await CatalogBuilder.create(env); + /* highlight-add-next-line */ + builder.addPermissionRules(isInSystemRule); + ... + return router; +} +``` + +The updated policy will allow catalog entity resource permissions if any of the following are true: + +- User owns the target entity +- Target entity is part of the 'interviewing' system diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 57565755fb..9642f80215 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -4,19 +4,23 @@ title: Defining custom permission rules description: How to define custom permission rules for existing resources --- +:::info +This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage [version 1.24](../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./custom-rules--old.md) instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + For some use cases, you may want to define custom [rules](../references/glossary.md#rule-permission-plugin) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of. ## Define a custom rule -Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/plugins/permission.ts`. +Plugins should export a rule factory that provides type-safety that ensures compatibility with the plugin's backend. The catalog plugin exports `createCatalogPermissionRule` from `@backstage/plugin-catalog-backend/alpha` for this purpose. Note: the `/alpha` path segment is temporary until this API is marked as stable. For this example, we'll define the rule and create a condition in `packages/backend/src/extensions/permissionsPolicyExtension.ts`. -We use Zod in our example below. To install, run: +We use `zod` and `@backstage/catalog-model` in our example below. To install them run: -```bash -yarn workspace backend add zod +```bash title="from your Backstage root directory" +yarn --cwd packages/backend add zod @backstage/catalog-model ``` -```typescript title="packages/backend/src/plugins/permission.ts" +```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts" ... import type { Entity } from '@backstage/catalog-model'; @@ -55,9 +59,9 @@ const isInSystem = createConditionFactory(isInSystemRule); For a more detailed explanation on defining rules, refer to the [documentation for plugin authors](./plugin-authors/03-adding-a-resource-permission-check.md#adding-support-for-conditional-decisions). -Still in the `packages/backend/src/plugins/permission.ts` file, let's use the condition we just created in our `TestPermissionPolicy`. +Still in the `packages/backend/src/extensions/permissionsPolicyExtension.ts` file, let's use the condition we just created in our `CustomPermissionPolicy`. -```ts title="packages/backend/src/plugins/permission.ts" +```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts" ... /* highlight-remove-next-line */ import { createCatalogPermissionRule } from '@backstage/plugin-catalog-backend/alpha'; @@ -98,7 +102,7 @@ export const isInSystemRule = createCatalogPermissionRule({ const isInSystem = createConditionFactory(isInSystemRule); -class TestPermissionPolicy implements PermissionPolicy { +class CustomPermissionPolicy implements PermissionPolicy { async handle( request: PolicyQuery, user?: PolicyQueryUser, @@ -135,25 +139,49 @@ class TestPermissionPolicy implements PermissionPolicy { Now that we have a custom rule defined and added to our policy, we need provide it to the catalog plugin. This step is important because the catalog plugin will use the rule's `toQuery` and `apply` methods while evaluating conditional authorize results. There's no guarantee that the catalog and permission backends are running on the same server, so we must explicitly link the rule to ensure that it's available at runtime. -The api for providing custom rules may differ between plugins, but there should typically be some integration point during the creation of the backend router. For the catalog, this integration point is exposed via `CatalogBuilder.addPermissionRules`. +The api for providing custom rules may differ between plugins, but there should typically be an [extension point](../backend-system/architecture/05-extension-points.md) that you can use in your created module to add your rule. For the catalog, this extension point is exposed via `catalogPermissionExtensionPoint`. Here's the steps you'll need to take to add the `isInSystemRule` we created above to the catalog: -```typescript title="packages/backend/src/plugins/catalog.ts" -import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; -/* highlight-add-next-line */ -import { isInSystemRule } from './permission'; +1. We will be using the `@backstage/plugin-catalog-node` package as it contains the extension point we need. Run this to add it: -... + ```bash title="from your Backstage root directory" + yarn --cwd packages/backend add @backstage/plugin-catalog-node + ``` -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const builder = await CatalogBuilder.create(env); - /* highlight-add-next-line */ - builder.addPermissionRules(isInSystemRule); - ... - return router; -} -``` +2. Next create a `catalogPermissionRules.ts` file in the `packages/backend/src/extensions` folder. +3. Then add this as the contents of the new `catalogPermissionRules.ts` file: + + ```typescript title="packages/backend/src/extensions/catalogPermissionRules.ts" + import { createBackendModule } from '@backstage/backend-plugin-api'; + import { catalogPermissionExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; + import { isInSystemRule } from './permissionPolicyExtension'; + + export default createBackendModule({ + pluginId: 'catalog', + moduleId: 'permission-rules', + register(reg) { + reg.registerInit({ + deps: { catalog: catalogPermissionExtensionPoint }, + async init({ catalog }) { + catalog.addPermissionRules(isInSystemRule); + }, + }); + }, + }); + ``` + +4. Next we need to add this to the backend by adding the following line: + + ```ts title="packages/backend/src/index.ts" + // catalog plugin + backend.add(import('@backstage/plugin-catalog-backend/alpha')); + backend.add( + import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'), + ); + /* highlight-add-next-line */ + backend.add(import('./extensions/catalogPermissionRules')); + ``` + +5. Now when you run you Backstage instance - `yarn dev` - the rule will be added to the catalog plugin. The updated policy will allow catalog entity resource permissions if any of the following are true: diff --git a/docs/permissions/getting-started--new.md b/docs/permissions/getting-started--new.md deleted file mode 100644 index b6198a222f..0000000000 --- a/docs/permissions/getting-started--new.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: getting-started--new -title: Getting Started -description: How to get started with the permission framework as an integrator ---- - -Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others. - -## Prerequisites - -The permissions framework depends on a few other Backstage systems, which must be set up before we can dive into writing a policy. - -### Upgrade to the latest version of Backstage - -To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! - -### Supply an identity resolver to populate group membership on sign in - -**Note**: If you are working off of an existing Backstage instance, you likely already have some form of an identity resolver set up. - -Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity. - -[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. - -## Enable and test the permissions system - -All you need to do now is enable the permissions system in your Backstage instance! - -1. Set the property `permission.enabled` to `true` in `app-config.yaml`. - -```yaml title="app-config.yaml" -permission: - enabled: true -``` - -Congratulations! Now that the framework is configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! diff --git a/docs/permissions/getting-started--old.md b/docs/permissions/getting-started--old.md new file mode 100644 index 0000000000..96dbfec92f --- /dev/null +++ b/docs/permissions/getting-started--old.md @@ -0,0 +1,169 @@ +--- +id: getting-started--old +title: Getting Started +description: How to get started with the permission framework as an integrator +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../backend-system/index.md), being the default since Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./getting-started.md) instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + +If you prefer to watch a video instead, you can start with this video introduction: + + + +:::note Note + +This video was recorded in the January 2022 Contributors Session using `@backstage/create-app@0.4.14`. Some aspects of the demo may have changed in later releases. + +::: + +Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others. + +## Prerequisites + +The permissions framework depends on a few other Backstage systems, which must be set up before we can dive into writing a policy. + +### Upgrade to the latest version of Backstage + +The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! + +### Enable service-to-service authentication + +Service-to-service authentication allows Backstage backend code to verify that a given request originates from elsewhere in the Backstage backend. This is useful for tasks like collation of catalog entities in the search index. This type of request shouldn’t be permissioned, so it’s important to configure this feature before trying to use the permissions framework. + +To set up service-to-service authentication, follow the [service-to-service authentication docs](../auth/service-to-service-auth.md). + +### Supply an identity resolver to populate group membership on sign in + +**Note**: If you are working off of an existing Backstage instance, you likely already have some form of an identity resolver set up. + +Like many other parts of Backstage, the permissions framework relies on information about group membership. This simplifies authoring policies through the use of groups, rather than requiring each user to be listed in the configuration. Group membership is also often useful for conditional permissions, for example allowing permissions to act on an entity to be granted when a user is a member of a group that owns that entity. + +[The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. + +## Optionally add cookie-based authentication + +Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior. + +## Integrating the permission framework with your Backstage instance + +### 1. Set up the permission backend + +The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it: + +1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend: + + ```bash title="From your Backstage root directory" + yarn --cwd packages/backend add @backstage/plugin-permission-backend + ``` + +2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. + + ```typescript title="packages/backend/src/plugins/permission.ts" + import { createRouter } from '@backstage/plugin-permission-backend'; + import { + AuthorizeResult, + PolicyDecision, + } from '@backstage/plugin-permission-common'; + import { PermissionPolicy } from '@backstage/plugin-permission-node'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + class TestPermissionPolicy implements PermissionPolicy { + async handle(): Promise { + return { result: AuthorizeResult.ALLOW }; + } + } + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + config: env.config, + logger: env.logger, + discovery: env.discovery, + policy: new TestPermissionPolicy(), + identity: env.identity, + }); + } + ``` + +3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. You’ll need to import the module from the previous step, create a plugin environment, and add the router to the express app: + + ```ts title="packages/backend/src/index.ts" + import proxy from './plugins/proxy'; + import techdocs from './plugins/techdocs'; + import search from './plugins/search'; + /* highlight-add-next-line */ + import permission from './plugins/permission'; + + async function main() { + const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); + const searchEnv = useHotMemoize(module, () => createEnv('search')); + const appEnv = useHotMemoize(module, () => createEnv('app')); + /* highlight-add-next-line */ + const permissionEnv = useHotMemoize(module, () => createEnv('permission')); + // .. + + apiRouter.use('/techdocs', await techdocs(techdocsEnv)); + apiRouter.use('/proxy', await proxy(proxyEnv)); + apiRouter.use('/search', await search(searchEnv)); + /* highlight-add-next-line */ + apiRouter.use('/permission', await permission(permissionEnv)); + // .. + } + ``` + +### 2. Enable and test the permissions system + +Now that the permission backend is running, it’s time to enable the permissions framework and make sure it’s working properly. + +1. Set the property `permission.enabled` to `true` in `app-config.yaml`. + + ```yaml title="app-config.yaml" + permission: + enabled: true + ``` + +2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission that’s easy for us to test. This policy rejects any attempt to delete a catalog entity: + + ```ts title="packages/backend/src/plugins/permission.ts" + import { createRouter } from '@backstage/plugin-permission-backend'; + import { + AuthorizeResult, + PolicyDecision, + } from '@backstage/plugin-permission-common'; + /* highlight-remove-next-line */ + import { PermissionPolicy } from '@backstage/plugin-permission-node'; + /* highlight-add-start */ + import { + PermissionPolicy, + PolicyQuery, + } from '@backstage/plugin-permission-node'; + /* highlight-add-end */ + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + class TestPermissionPolicy implements PermissionPolicy { + /* highlight-remove-next-line */ + async handle(): Promise { + /* highlight-add-start */ + async handle(request: PolicyQuery): Promise { + if (request.permission.name === 'catalog.entity.delete') { + return { + result: AuthorizeResult.DENY, + }; + } + /* highlight-add-end */ + + return { result: AuthorizeResult.ALLOW }; + } + } + ``` + +3. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. + +![Entity detail page showing disabled unregister entity context menu entry](../assets/permissions/disabled-unregister-entity.png) + +Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index e8132e571b..d2c487e6ea 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -4,14 +4,8 @@ title: Getting Started description: How to get started with the permission framework as an integrator --- -If you prefer to watch a video instead, you can start with this video introduction: - - - -:::note Note - -This video was recorded in the January 2022 Contributors Session using `@backstage/create-app@0.4.14`. Some aspects of the demo may have changed in later releases. - +:::info +This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage [version 1.24](../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./getting-started--old.md) instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)! ::: Backstage integrators control permissions by writing a policy. In general terms, a policy is simply an async function which receives a request to authorize a specific action for a user and (optional) resource, and returns a decision on whether to authorize that permission. Integrators can implement their own policies from scratch, or adopt reusable policies written by others. @@ -22,13 +16,7 @@ The permissions framework depends on a few other Backstage systems, which must b ### Upgrade to the latest version of Backstage -The permissions framework itself is new to Backstage and still evolving quickly. To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! - -### Enable service-to-service authentication - -Service-to-service authentication allows Backstage backend code to verify that a given request originates from elsewhere in the Backstage backend. This is useful for tasks like collation of catalog entities in the search index. This type of request shouldn’t be permissioned, so it’s important to configure this feature before trying to use the permissions framework. - -To set up service-to-service authentication, follow the [service-to-service authentication docs](../auth/service-to-service-auth.md). +To ensure your version of Backstage has all the latest permission-related functionality, it’s important to upgrade to the latest version. The [Backstage upgrade helper](https://backstage.github.io/upgrade-helper/) is a great tool to help ensure that you’ve made all the necessary changes during the upgrade! ### Supply an identity resolver to populate group membership on sign in @@ -38,33 +26,40 @@ Like many other parts of Backstage, the permissions framework relies on informat [The IdentityResolver docs](../auth/identity-resolver.md) describe the process for resolving group membership on sign in. -## Optionally add cookie-based authentication +## Test Permission Policy -Asset requests initiated by the browser will not include a token in the `Authorization` header. If these requests check authorization through the permission framework, as done in plugins like TechDocs, then you'll need to set up cookie-based authentication. Refer to the ["Authenticate API requests"](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial for a demonstration on how to implement this behavior. +To help validate the permission framework is setup we'll create a Test Permission Policy: -## Integrating the permission framework with your Backstage instance +1. Backstage ships with a default Allow All Policy, we want to remove that as it would override our Test Permission Policy. To do this remove the following line: -### 1. Set up the permission backend - -The permissions framework uses a new `permission-backend` plugin to accept authorization requests from other plugins across your Backstage instance. The Backstage backend does not include this permission backend by default, so you will need to add it: - -1. Add `@backstage/plugin-permission-backend` as a dependency of your Backstage backend: - - ```bash title="From your Backstage root directory" - yarn --cwd packages/backend add @backstage/plugin-permission-backend + ```ts title="packages/backend/src/index.ts" + // permission plugin + backend.add(import('@backstage/plugin-permission-backend/alpha')); + /* highlight-remove-start */ + backend.add( + import('@backstage/plugin-permission-backend-module-allow-all-policy'), + ); + /* highlight-remove-end */ ``` -2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. +2. Now we need to add the `@backstage/backend-plugin-api` package: - ```typescript title="packages/backend/src/plugins/permission.ts" - import { createRouter } from '@backstage/plugin-permission-backend'; + ```bash title="from your Backstage root directory" + yarn --cwd packages/backend add @backstage/backend-plugin-api + ``` + +3. Next we will create an `extensions` folder under `packages/backend/src` +4. In this new `extensions` folder we will add a new file called: `permissionsPolicyExtension.ts` +5. Copy the following into the new `permissionsPolicyExtension.ts` file: + + ```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts" + import { createBackendModule } from '@backstage/backend-plugin-api'; import { - AuthorizeResult, PolicyDecision, + AuthorizeResult, } from '@backstage/plugin-permission-common'; import { PermissionPolicy } from '@backstage/plugin-permission-node'; - import { Router } from 'express'; - import { PluginEnvironment } from '../types'; + import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha'; class TestPermissionPolicy implements PermissionPolicy { async handle(): Promise { @@ -72,48 +67,34 @@ The permissions framework uses a new `permission-backend` plugin to accept autho } } - export default async function createPlugin( - env: PluginEnvironment, - ): Promise { - return await createRouter({ - config: env.config, - logger: env.logger, - discovery: env.discovery, - policy: new TestPermissionPolicy(), - identity: env.identity, - }); - } + export default createBackendModule({ + pluginId: 'permission', + moduleId: 'permission-policy', + register(reg) { + reg.registerInit({ + deps: { policy: policyExtensionPoint }, + async init({ policy }) { + policy.setPolicy(new TestPermissionPolicy()); + }, + }); + }, + }); ``` -3. Wire up the permission policy in `packages/backend/src/index.ts`. [The index in the example backend](https://github.com/backstage/backstage/blob/master/packages/backend/src/index.ts) shows how to do this. You’ll need to import the module from the previous step, create a plugin environment, and add the router to the express app: +6. We now need to register this in the backend. We will do this by adding the follow line: ```ts title="packages/backend/src/index.ts" - import proxy from './plugins/proxy'; - import techdocs from './plugins/techdocs'; - import search from './plugins/search'; + // permission plugin + backend.add(import('@backstage/plugin-permission-backend/alpha')); /* highlight-add-next-line */ - import permission from './plugins/permission'; - - async function main() { - const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); - const searchEnv = useHotMemoize(module, () => createEnv('search')); - const appEnv = useHotMemoize(module, () => createEnv('app')); - /* highlight-add-next-line */ - const permissionEnv = useHotMemoize(module, () => createEnv('permission')); - // .. - - apiRouter.use('/techdocs', await techdocs(techdocsEnv)); - apiRouter.use('/proxy', await proxy(proxyEnv)); - apiRouter.use('/search', await search(searchEnv)); - /* highlight-add-next-line */ - apiRouter.use('/permission', await permission(permissionEnv)); - // .. - } + backend.add(import('./extensions/permissionPolicyExtension')); ``` -### 2. Enable and test the permissions system +You now have a Test Permission Policy in place, this will help us test that the permission framework is working in the next section. -Now that the permission backend is running, it’s time to enable the permissions framework and make sure it’s working properly. +## Enable and test the permissions system + +Now lets test end to end that the permissions framework is setup and configured properly we will use the Test Permission Policy we create above as is, then modify it do deny access which will confirm everything is working as expected. Here's how to do that: 1. Set the property `permission.enabled` to `true` in `app-config.yaml`. @@ -122,44 +103,11 @@ Now that the permission backend is running, it’s time to enable the permission enabled: true ``` -2. Update the PermissionPolicy in `packages/backend/src/plugins/permission.ts` to disable a permission that’s easy for us to test. This policy rejects any attempt to delete a catalog entity: +2. Now run `yarn dev`, Backstage should load up in your browser +3. You should see that you have entities in your Catalog, pretty simple +4. Let's change this line in our Test Permission Policy `return { result: AuthorizeResult.ALLOW };` to be `return { result: AuthorizeResult.DENY };` +5. Run `yarn dev` once again, Backstage should load up in your browser +6. This time you should not see any entities in your Catalog, if you do then something went wrong along the way and you'll need to review the steps above +7. Revert the change we made in step 4 so that the line looks like this: `return { result: AuthorizeResult.ALLOW };` - ```ts title="packages/backend/src/plugins/permission.ts" - import { createRouter } from '@backstage/plugin-permission-backend'; - import { - AuthorizeResult, - PolicyDecision, - } from '@backstage/plugin-permission-common'; - /* highlight-remove-next-line */ - import { PermissionPolicy } from '@backstage/plugin-permission-node'; - /* highlight-add-start */ - import { - PermissionPolicy, - PolicyQuery, - } from '@backstage/plugin-permission-node'; - /* highlight-add-end */ - import { Router } from 'express'; - import { PluginEnvironment } from '../types'; - - class TestPermissionPolicy implements PermissionPolicy { - /* highlight-remove-next-line */ - async handle(): Promise { - /* highlight-add-start */ - async handle(request: PolicyQuery): Promise { - if (request.permission.name === 'catalog.entity.delete') { - return { - result: AuthorizeResult.DENY, - }; - } - /* highlight-add-end */ - - return { result: AuthorizeResult.ALLOW }; - } - } - ``` - -3. Now that you’ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. - -![Entity detail page showing disabled unregister entity context menu entry](../assets/permissions/disabled-unregister-entity.png) - -Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! +Congratulations! Now that the framework is fully configured, you can craft a permission policy that works best for your organization by [writing your own policy](./writing-a-policy.md)! diff --git a/docs/permissions/writing-a-policy--old.md b/docs/permissions/writing-a-policy--old.md new file mode 100644 index 0000000000..d95fa7ffb3 --- /dev/null +++ b/docs/permissions/writing-a-policy--old.md @@ -0,0 +1,148 @@ +--- +id: writing-a-policy--old +title: Writing a permission policy +description: How to write your own permission policy as a Backstage integrator +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../backend-system/index.md), being the default since Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./writing-a-policy.md) instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + +In the [previous section](./getting-started.md), we were able to set up the permission framework and make a simple change to our `TestPermissionPolicy` to confirm that policy is indeed wired up correctly. + +That policy looked like this: + +```typescript title="packages/backend/src/plugins/permission.ts" +class TestPermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + _user?: PolicyQueryUser, + ): Promise { + if (request.permission.name === 'catalog.entity.delete') { + return { + result: AuthorizeResult.DENY, + }; + } + + return { result: AuthorizeResult.ALLOW }; + } +} +``` + +## What's in a policy? + +Let's break this down a bit further. The request object of type [PolicyQuery](https://backstage.io/docs/reference/plugin-permission-node.policyquery) is a simple wrapper around [the Permission object](https://backstage.io/docs/reference/plugin-permission-common.permission). This permission object encapsulates information about the action that the user is attempting to perform (See [the Concepts page](./concepts.md) for more details). + +In the policy above, we are checking to see if the provided action is a catalog entity delete action, which is the permission that the catalog plugin authors have created to represent the action of unregistering a catalog entity. If this is the case, we return a [Definitive Policy Decision](https://backstage.io/docs/reference/plugin-permission-common.definitivepolicydecision) of DENY. In all other cases, we return ALLOW (resulting in an allow-by-default behavior). + +As we confirmed in the previous section, we know that this now prevents us from unregistering catalog components. Hooray! But you may notice that this prevents _anyone_ from unregistering a component, which is not a very realistic policy. Let's improve this policy by disabling the unregister action _unless you are the owner of this component_. + +## Conditional decisions + +Let's change the policy to the following: + +```ts +import { + AuthorizeResult, + PolicyDecision, + /* highlight-add-next-line */ + isPermission, +} from '@backstage/plugin-permission-common'; +/* highlight-add-start */ +import { + catalogConditions, + createCatalogConditionalDecision, +} from '@backstage/plugin-catalog-backend/alpha'; +import { + catalogEntityDeletePermission, +} from '@backstage/plugin-catalog-common/alpha'; +/* highlight-add-end */ + +class TestPermissionPolicy implements PermissionPolicy { + /* highlight-remove-next-line */ + async handle(request: PolicyQuery): Promise { + /* highlight-add-start */ + async handle( + request: PolicyQuery, + user?: PolicyQueryUser, + ): Promise { + /* highlight-add-end */ + /* highlight-remove-next-line */ + if (request.permission.name === 'catalog.entity.delete') { + /* highlight-add-next-line */ + if (isPermission(request.permission, catalogEntityDeletePermission)) { + /* highlight-remove-start */ + return { + result: AuthorizeResult.DENY, + }; + /* highlight-remove-end */ + /* highlight-add-start */ + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.info.ownershipEntityRefs ?? [], + }), + ); + /* highlight-add-end */ + } + return { result: AuthorizeResult.ALLOW }; + } +} +``` + +Let's walk through the new code that we just added. + +Instead of returning an Definitive Policy Decision, we use factory methods to construct a [Conditional Policy Decision](https://backstage.io/docs/reference/plugin-permission-common.conditionalpolicydecision) (See the [Concepts page](./concepts.md) for more details). Since the policy doesn't have enough information to determine if `user` is the entity owner, this criteria is encapsulated within the conditional decision. However, `createCatalogConditionalDecision` will not compile unless `request.permission` is a catalog entity [`ResourcePermission`](https://backstage.io/docs/reference/plugin-permission-common.resourcepermission). This type constraint ensures that policies return conditional decisions that are compatible with the requested permission. To address this, we use [`isPermission`](https://backstage.io/docs/reference/plugin-permission-common.ispermission) to ["narrow"](https://www.typescriptlang.org/docs/handbook/2/narrowing.html) the type of `request.permission` to `ResourcePermission<'catalog-entity'>`. This matches the runtime behavior that was in place before, but you'll notice that the type of `request.permission` has changed within the scope of that `if` statement. + +The `catalogConditions` object contains all of the rules defined by the catalog plugin. These rules can be combined to form a [`PermissionCriteria`](https://backstage.io/docs/reference/plugin-permission-common.permissioncriteria) object, but for this case we only need to use the `isEntityOwner` rule. This rule accepts a list of entity refs that represent User identity and Group membership used to determine ownership. The second argument to `PermissionPolicy#handle` provides us with a `PolicyQueryUser` object, from which we can grab the user's `ownershipEntityRefs`. We provide an empty array as a fallback since the user may be anonymous. + +You should now be able to see in your Backstage app that the unregister entity button is enabled for entities that you own, but disabled for all other entities! + +## Resource types + +Now let's say we want to prevent all actions on catalog entities unless performed by the owner. One way to achieve this may be to simply update the `if` statement and check for each permission. If you choose to write your policy this way, it will certainly work! However, it may be difficult to maintain as the policy grows, and it may not be obvious if certain permissions are left out. We can author this same policy in a more scalable way by checking the resource type of the requested permission. + +```ts +import { + AuthorizeResult, + PolicyDecision, + /* highlight-remove-next-line */ + isPermission, + isResourcePermission, + /* highlight-add-next-line */ +} from '@backstage/plugin-permission-common'; +import { + catalogConditions, + createCatalogConditionalDecision, +} from '@backstage/plugin-catalog-backend/alpha'; +/* highlight-remove-start */ +import { + catalogEntityDeletePermission, +} from '@backstage/plugin-catalog-common/alpha'; +/* highlight-remove-end */ + +class TestPermissionPolicy implements PermissionPolicy { + async handle( + request: PolicyQuery, + user?: PolicyQueryUser, + ): Promise { + /* highlight-remove-next-line */ + if (isPermission(request.permission, catalogEntityDeletePermission)) { + /* highlight-add-next-line */ + if (isResourcePermission(request.permission, 'catalog-entity')) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.info.ownershipEntityRefs ?? [], + }), + ); + } + + return { result: AuthorizeResult.ALLOW }; + } +} +``` + +In this example, we use [`isResourcePermission`](https://backstage.io/docs/reference/plugin-permission-common.isresourcepermission) to match all permissions with a resource type of `catalog-entity`. Just like `isPermission`, this helper will "narrow" the type of `request.permission` and enable the use of `createCatalogConditionalDecision`. In addition to the behavior you observed before, you should also see that catalog entities are no longer visible unless you are the owner - success! + +_Note:_ Some catalog permissions do not have the `'catalog-entity'` resource type, such as [`catalogEntityCreatePermission`](https://github.com/backstage/backstage/blob/1e5e9fb9de9856a49e60fc70c38a4e4e94c69570/plugins/catalog-common/src/permissions.ts#L49). In those cases, a definitive decision is required because conditions can't be applied to an entity that does not exist yet. diff --git a/docs/permissions/writing-a-policy.md b/docs/permissions/writing-a-policy.md index 0762f6b0c6..ce662b6e1a 100644 --- a/docs/permissions/writing-a-policy.md +++ b/docs/permissions/writing-a-policy.md @@ -4,16 +4,40 @@ title: Writing a permission policy description: How to write your own permission policy as a Backstage integrator --- +:::info +This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage [version 1.24](../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./writing-a-policy--old.md) instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + In the [previous section](./getting-started.md), we were able to set up the permission framework and make a simple change to our `TestPermissionPolicy` to confirm that policy is indeed wired up correctly. That policy looked like this: -```typescript title="packages/backend/src/plugins/permission.ts" +```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts" class TestPermissionPolicy implements PermissionPolicy { - async handle( - request: PolicyQuery, - _user?: PolicyQueryUser, - ): Promise { + async handle(): Promise { + return { result: AuthorizeResult.ALLOW }; + } +} +``` + +That is a very simple example and it's not really doing anything helpful, let's expand this a little more. + +First, let's rename this from `TestPermissionPolicy` to `CustomPermissionPolicy` as you'll build on adding to it as your permissions needs require. Then we'll add a check for a permission. Here's what the full `permissionsPolicyExtension.ts` will look like: + +```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts" +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + PolicyDecision, + AuthorizeResult, +} from '@backstage/plugin-permission-common'; +import { + PermissionPolicy, + PolicyQuery, +} from '@backstage/plugin-permission-node'; +import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha'; + +class CustomPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery): Promise { if (request.permission.name === 'catalog.entity.delete') { return { result: AuthorizeResult.DENY, @@ -23,8 +47,23 @@ class TestPermissionPolicy implements PermissionPolicy { return { result: AuthorizeResult.ALLOW }; } } + +export default createBackendModule({ + pluginId: 'permission', + moduleId: 'permission-policy', + register(reg) { + reg.registerInit({ + deps: { policy: policyExtensionPoint }, + async init({ policy }) { + policy.setPolicy(new CustomPermissionPolicy()); + }, + }); + }, +}); ``` +Now with this policy in place the ability to delete entities in the Catalog is not allowed for anyone. The following sections will expand on the concepts used here. + ## What's in a policy? Let's break this down a bit further. The request object of type [PolicyQuery](https://backstage.io/docs/reference/plugin-permission-node.policyquery) is a simple wrapper around [the Permission object](https://backstage.io/docs/reference/plugin-permission-common.permission). This permission object encapsulates information about the action that the user is attempting to perform (See [the Concepts page](./concepts.md) for more details). @@ -54,7 +93,7 @@ import { } from '@backstage/plugin-catalog-common/alpha'; /* highlight-add-end */ -class TestPermissionPolicy implements PermissionPolicy { +class CustomPermissionPolicy implements PermissionPolicy { /* highlight-remove-next-line */ async handle(request: PolicyQuery): Promise { /* highlight-add-start */ @@ -117,7 +156,7 @@ import { } from '@backstage/plugin-catalog-common/alpha'; /* highlight-remove-end */ -class TestPermissionPolicy implements PermissionPolicy { +class CustomPermissionPolicy implements PermissionPolicy { async handle( request: PolicyQuery, user?: PolicyQueryUser, diff --git a/docs/plugins/internationalization.md b/docs/plugins/internationalization.md index 0f48e35a25..ba3f694d0c 100644 --- a/docs/plugins/internationalization.md +++ b/docs/plugins/internationalization.md @@ -151,32 +151,52 @@ export const myPluginTranslationRef = createTranslationRef({ Step 1: Create translation resources +You should separate different translations to their own files and import them in the main file: + ```ts // packages/app/src/translations/userSettings.ts import { createTranslationResource } from '@backstage/core-plugin-api/alpha'; import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha'; -export const userSettingsMessages = createTranslationResource({ +export const userSettingsTranslations = createTranslationResource({ ref: userSettingsTranslationRef, translations: { - zh: () => - Promise.resolve({ - default: { - 'languageToggle.title': '语言', - 'languageToggle.select': '选择{{language}}', - 'languageToggle.description': '切换语言', - 'themeToggle.title': '主题', - 'themeToggle.description': '切换主题', - 'themeToggle.select': '选择{{theme}}', - 'themeToggle.selectAuto': '选择自动主题', - 'themeToggle.names.auto': '自动', - 'themeToggle.names.dark': '暗黑', - 'themeToggle.names.light': '明亮', - }, - }), + zh: () => import('./userSettings-zh'), }, }); + +// packages/app/src/translations/userSettings-zh.ts +import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha'; + +const zh = createTranslationMessages({ + ref: userSettingsTranslationRef, + full: false, // False means that this is a partial translation + messages: { + 'languageToggle.title': '语言', + 'languageToggle.select': '选择{{language}}', + }, +}); + +export default zh; +``` + +It's also possible to export the list of messages directly: + +```ts +// packages/app/src/translations/userSettings-zh.ts +export default { + 'languageToggle.title': '语言', + 'languageToggle.select': '选择{{language}}', + 'languageToggle.description': '切换语言', + 'themeToggle.title': '主题', + 'themeToggle.description': '切换主题', + 'themeToggle.select': '选择{{theme}}', + 'themeToggle.selectAuto': '选择自动主题', + 'themeToggle.names.auto': '自动', + 'themeToggle.names.dark': '暗黑', + 'themeToggle.names.light': '明亮', +}; ``` You should change `zh` under the translations object to your local language. @@ -186,12 +206,12 @@ Step 2: Config translations in `packages/app/src/App.tsx` In an app you can both override the default messages, as well as register translations for additional languages: ```diff -+ import { userSettingsMessages } from './translations/userSettings'; ++ import { userSettingsTranslations } from './translations/userSettings'; const app = createApp({ + __experimentalTranslations: { + availableLanguages: ['en', 'zh'], -+ resources: [userSettingsMessages], ++ resources: [userSettingsTranslations], + }, }) ``` diff --git a/docs/plugins/url-reader.md b/docs/plugins/url-reader.md deleted file mode 100644 index 9fab0482bb..0000000000 --- a/docs/plugins/url-reader.md +++ /dev/null @@ -1,265 +0,0 @@ ---- -id: url-reader -title: URL Reader -sidebar_label: URL Reader -# prettier-ignore -description: URL Reader is a backend core API responsible for reading files from external locations. ---- - -## Concept - -Some of the core plugins of Backstage have to read files from an external -location. [Software Catalog](../features/software-catalog/index.md) has to read -the [`catalog-info.yaml`](../features/software-catalog/descriptor-format.md) -entity descriptor files to register and track an entity. -[Software Templates](../features/software-templates/index.md) have to download -the template skeleton files before creating a new component. -[TechDocs](../features/techdocs/README.md) has to download the markdown source -files before generating a documentation site. - -Since, the requirement for reading files is so essential for Backstage plugins, -the -[`coreServices.urlReader`](../reference/backend-plugin-api.coreservices.urlreader.md) -package provides a dedicated API for reading from such URL based remote -locations like GitHub, GitLab, Bitbucket, Google Cloud Storage, etc. This is -commonly referred to as "URL Reader". It takes care of making authenticated -requests to the remote host so that private files can be read securely. If users -have [GitHub App based authentication](../integrations/github/github-apps.md) set up, URL Reader even -refreshes the token, to avoid reaching the GitHub API rate limit. - -As a result, plugin authors do not have to worry about any of these problems -when trying to read files. - -## Interface - -When the Backstage backend starts, a new instance of URL Reader is created. You -can see this in the index file of your Backstage backend -i.e.`packages/backend/src/index.ts`. -[Example](https://github.com/backstage/backstage/blob/ebbe91dbe79038a61d35cf6ed2d96e0e0d5a15f3/packages/backend/src/index.ts#L57) - -```ts -// File: packages/backend/src/index.ts - -import { UrlReaders } from '@backstage/backend-common'; - -function makeCreateEnv(config: Config) { - // .... - const reader = UrlReaders.default({ logger: root, config }); - // -} -``` - -This instance contains all the default URL Reader providers -in the backend-defaults package including GitHub, GitLab, Bitbucket, Azure, Google -GCS. As the need arises, more URL Readers are being written to support different -providers. - -The generic interface of a URL Reader instance looks like this. - -```ts -export type UrlReader = { - /* Used to read a single file and return its content. */ - readUrl(url: string, options?: ReadUrlOptions): Promise; - /* Used to read a file tree and download as a directory. */ - readTree(url: string, options?: ReadTreeOptions): Promise; - /* Used to search a file in a tree. */ - search(url: string, options?: SearchOptions): Promise; -}; -``` - -## Using a URL Reader inside a plugin - -The `reader` instance is available in the backend plugin environment and passed -on to all the backend plugins. You can see an -[example](https://github.com/backstage/backstage/blob/b0be185369ebaad22255b7cdf18535d1d4ffd0e7/packages/backend/src/plugins/techdocs.ts#L31). -When any of the methods on this instance is called with a URL, URL Reader -extracts the host for that URL (e.g. `github.com`, `ghe.mycompany.com`, etc.). -Using the -[`@backstage/integration`](https://github.com/backstage/backstage/tree/master/packages/integration) -package, it looks inside the -[`integrations:`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/app-config.yaml#L134-L158) -config of the `app-config.yaml` to find out how to work with the host based on -the configs provided like authentication token, API base URL, etc. - -Make sure your plugin-specific backend file at -`packages/backend/src/plugins/.ts` is forwarding the `reader` instance -passed on as the `PluginEnvironment` to the actual plugin's `createRouter` -function. See how this is done in -[Catalog](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend/src/plugins/catalog.ts#L25-L27) -and -[TechDocs](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend/src/plugins/techdocs.ts#L31-L36) -backend plugins. - -Once the reader instance is available inside the plugin, one of its methods can -directly be used with a URL. Some example usages - - -- [`readUrl`](https://github.com/backstage/backstage/blob/a7607b5/plugins/catalog-backend/src/modules/codeowners/lib/read.ts#L24-L33) - - Catalog using the `readUrl` method to read the CODEOWNERS file in a repository. -- [`readTree`](https://github.com/backstage/backstage/blob/84a8788/plugins/techdocs-node/src/helpers.ts#L146-L167) - - TechDocs using the `readTree` method to download markdown files in order to - generate the documentation site. -- [`readTree`](https://github.com/backstage/backstage/blob/cb4f0e4/plugins/techdocs-node/src/stages/prepare/url.ts#L51-L73) - - TechDocs using `NotModifiedError` to maintain cache and speed up and limit the - number of requests. -- [`search`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts#L88-L108) - - Catalog using the `search` method to find files for a location URL containing - a glob pattern. - -Note that URL Readers which target git-based version control systems may, under -the hood, leverage the ability to create tar archives based on a specific git -commit-ish. A consequence of this is that files and directories configured with -[the `export-ignore` attribute](https://git-scm.com/docs/gitattributes#_creating_an_archive) -via `.gitattributes` will not be visible to the URL reader when using the -`readTree` or `search` methods. - -Be aware of this limitation and ensure that end-users of your plugin are also -aware via, for example, documentation. - -## Writing a new URL Reader - -If the available URL Readers are not sufficient for your use case and you want -to add a new URL Reader for any other provider, you are most welcome to -contribute one! - -Feel free to use the -[GitHub URL Reader](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/GithubUrlReader.ts) -as a source of inspiration. - -### 1. Add an integration - -The provider for your new URL Reader can also be called an "integration" in -Backstage. The `integrations:` section of your Backstage `app-config.yaml` -config file is supposed to be the place where a Backstage integrator defines the -host URL for the integration, authentication details and other integration -related configurations. - -The `@backstage/integration` package is where most of the integration specific -code lives, so that it is shareable across Backstage. Functions like "read the -integrations config and process it", "construct headers for authenticated -requests to the host" or "convert a plain file URL into its API URL for -downloading the file" would live in this package. - -### 2. Create the URL Reader - -Create a new class which implements the -[`UrlReader` type](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/types.ts#L21-L28) -inside `@backstage/backend-common`. Create and export a static `factory` method -which reads the integration config and returns a map of host URLs the new reader -should be used for. See the -[GitHub URL Reader](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/GithubUrlReader.ts#L50-L63) -for example. - -### 3. Implement the methods - -We want to make sure all URL Readers behave in the same way. Hence if possible, -all the methods of the `UrlReader` interface should be implemented. However it -is okay to start by implementing just one of them and create issues for the -remaining. - -#### `readUrl` - -`readUrl` method expects a user-friendly URL, something which can be copied from -the browser naturally when a person is browsing the provider in their browser. - -- ✅ Valid URL : - `https://github.com/backstage/backstage/blob/master/ADOPTERS.md` -- ❌ Not a valid URL : - `https://raw.githubusercontent.com/backstage/backstage/master/ADOPTERS.md` -- ❌ Not a valid URL : `https://github.com/backstage/backstage/ADOPTERS.md` - -Upon receiving the URL, `readUrl` converts the user-friendly URL into an API URL -which can be used to request the provider's API. - -`readUrl` then makes an authenticated request to the provider API and returns the response containing the file's contents and ETag(if the provider supports it). - -#### `readTree` - -`readTree` method also expects user-friendly URLs similar to `read` but the URL -should point to a tree (could be the root of a repository or even a -sub-directory). - -- ✅ Valid URL : `https://github.com/backstage/backstage` -- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master` -- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/docs` - -Using the provider's API documentation, find out an API endpoint which can be -used to download either a zip or a tarball. You can download the entire tree -(e.g. a repository) and filter out in case the user is expecting only a -sub-tree. But some APIs are smart enough to accept a path and return only a -sub-tree in the downloaded archive. - -#### search - -`search` method expects a glob pattern of a URL and returns a list of files -matching the query. - -- ✅ Valid URL : - `https://github.com/backstage/backstage/blob/master/**/catalog-info.yaml` -- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/**/*.md` -- ✅ Valid URL : - `https://github.com/backstage/backstage/blob/master/*/package.json` -- ✅ Valid URL : `https://github.com/backstage/backstage/blob/master/READM` - -The core logic of `readTree` can be used here to extract all the files inside -the tree and return the files matching the pattern in the `url`. - -### 4. Add to available URL Readers - -There are two ways to make your new URL Reader available for use. - -You can choose to make it open source, by updating the -[`default` factory](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/backend-common/src/reading/UrlReaders.ts#L62-L81) -method of URL Readers. - -But for something internal which you don't want to make open source, you can -update your `packages/backend/src/index.ts` file and update how the `reader` -instance is created. - -```ts -// File: packages/backend/src/index.ts -const reader = UrlReaders.default({ - logger: root, - config, - // This is where your internal URL Readers would go. - factories: [myCustomReader.factory], -}); -``` - -### 5. Caching - -All of the methods above support an ETag based caching. If the method is called -without an `etag`, the response contains an ETag of the resource (should ideally -forward the ETag returned by the provider). If the method is called with an -`etag`, it first compares the ETag and returns a `NotModifiedError` in case the -resource has not been modified. This approach is very similar to the actual -[`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) and -[`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match) -HTTP headers. - -### 6. Debugging - -When debugging one of the URL Readers, you can straightforward use the -[`reader` instance created](https://github.com/backstage/backstage/blob/ebbe91dbe79038a61d35cf6ed2d96e0e0d5a15f3/packages/backend/src/index.ts#L57) -when the backend starts and call one of the methods with your debugging URL. - -```ts -// File: packages/backend/src/index.ts - -async function main() { - // ... - const createEnv = makeCreateEnv(config); - - const testReader = createEnv('test-url-reader').reader; - const response = await testReader.readUrl( - 'https://github.com/backstage/backstage/blob/master/catalog-info.yaml', - ); - console.log((await response.buffer()).toString()); - // ... -} -``` - -This will be run every time you restart the backend. Note that after any change -in the URL Reader code, you need to stop the backend and restart, since the -`reader` instance is memoized and does not update on hot module reloading. Also, -there are a lot of unit tests written for the URL Readers, which you can make -use of. diff --git a/docs/releases/v1.30.0-changelog.md b/docs/releases/v1.30.0-changelog.md index 608d94e711..0af531f483 100644 --- a/docs/releases/v1.30.0-changelog.md +++ b/docs/releases/v1.30.0-changelog.md @@ -283,8 +283,8 @@ Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.30.0](https:// - 861f162: **BREAKING**: Removed these deprecated helpers: - - `setupRequestMockHandlers` Use `CreateMockDirectoryOptions` instead. - - `CreateMockDirectoryOptions` Use `registerMswTestHooks` instead. + - `setupRequestMockHandlers` is removed; use `registerMswTestHooks` instead. + - `MockDirectoryOptions` is removed; use `CreateMockDirectoryOptions` instead. Stopped exporting the deprecated and internal `isDockerDisabledForTests` helper. diff --git a/docs/releases/v1.30.0.md b/docs/releases/v1.30.0.md index 230dc54813..0d3ad1dd5b 100644 --- a/docs/releases/v1.30.0.md +++ b/docs/releases/v1.30.0.md @@ -67,8 +67,8 @@ The `versions:check` command has also been removed as its only purpose was to ve ### BREAKING: `@backstage/backend-test-utils` -- `setupRequestMockHandlers` is removed; use `CreateMockDirectoryOptions` instead. -- `CreateMockDirectoryOptions` is removed; use `registerMswTestHooks` instead. +- `setupRequestMockHandlers` is removed; use `registerMswTestHooks` instead. +- `MockDirectoryOptions` is removed; use `CreateMockDirectoryOptions` instead. - Stopped exporting the deprecated and internal `isDockerDisabledForTests` helper. - Removed `get` method from `ServiceFactoryTester` which is replaced by `getSubject` diff --git a/docs/releases/v1.31.0-next.0-changelog.md b/docs/releases/v1.31.0-next.0-changelog.md new file mode 100644 index 0000000000..7e8b118fff --- /dev/null +++ b/docs/releases/v1.31.0-next.0-changelog.md @@ -0,0 +1,2787 @@ +# Release v1.31.0-next.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.31.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.31.0-next.0) + +## @backstage/backend-app-api@0.10.0-next.0 + +### Minor Changes + +- 19ff127: **BREAKING**: The deprecated `identityServiceFactory` and `tokenManagerServiceFactory` have been removed. +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- cd38da8: Deprecate the `featureDiscoveryServiceFactory` in favor of using `@backstage/backend-defaults#discoveryFeatureLoader` instead. +- 7f779c7: `auth.externalAccess` should be optional in the config schema +- 51a69b5: Fix feature loaders in CJS double-default nested builds +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/backend-common@0.25.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 8ba77ed: The `legacyPlugin` and `makeLegacyPlugin` helpers now provide their own shim implementation of the identity and token manager services, as these services are being removed from the new backend system. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 2e9ec14: Add `pg-format` as a dependency +- 19ff127: Internal refactor to re-declare the token manager service which was removed from `@backstage/backend-plugin-api`, but is still supported in this package for backwards compatibility. +- 66dbf0a: Allow the cache service to accept the human duration format for TTL +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## @backstage/backend-defaults@0.5.0-next.0 + +### Minor Changes + +- 359fcd7: **BREAKING**: The backwards compatibility with plugins using legacy auth through the token manager service has been removed. This means that instead of falling back to using the old token manager, requests towards plugins that don't support the new auth system will simply fail. Please make sure that all plugins in your deployment are hosted within a backend instance from the new backend system. + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +- 19ff127: **BREAKING**: The default backend instance no longer provides implementations for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`. + + If you rely on plugins that still require these services, you can add them to your own backend by re-creating the service reference and factory. + + The following can be used to implement the identity service: + + ```ts + import { + coreServices, + createServiceFactory, + createServiceRef, + } from '@backstage/backend-plugin-api'; + import { + DefaultIdentityClient, + IdentityApi, + } from '@backstage/plugin-auth-node'; + + backend.add( + createServiceFactory({ + service: createServiceRef({ id: 'core.identity' }), + deps: { + discovery: coreServices.discovery, + }, + async factory({ discovery }) { + return DefaultIdentityClient.create({ discovery }); + }, + }), + ); + ``` + + The following can be used to implement the token manager service: + + ```ts + import { ServerTokenManager, TokenManager } from '@backstage/backend-common'; + import { createBackend } from '@backstage/backend-defaults'; + import { + coreServices, + createServiceFactory, + createServiceRef, + } from '@backstage/backend-plugin-api'; + + backend.add( + createServiceFactory({ + service: createServiceRef({ id: 'core.tokenManager' }), + deps: { + config: coreServices.rootConfig, + logger: coreServices.rootLogger, + }, + createRootContext({ config, logger }) { + return ServerTokenManager.fromConfig(config, { + logger, + allowDisabledTokenManager: true, + }); + }, + async factory(_deps, tokenManager) { + return tokenManager; + }, + }), + ); + ``` + +### Patch Changes + +- 7f779c7: `auth.externalAccess` should be optional in the config schema + +- 7a72ec8: Exports the `discoveryFeatureLoader` as a replacement for the deprecated `featureDiscoveryService`. + The `discoveryFeatureLoader` is a new backend system [feature loader](https://backstage.io/docs/backend-system/architecture/feature-loaders/) that discovers backend features from the current `package.json` and its dependencies. + Here is an example using the `discoveryFeatureLoader` loader in a new backend instance: + + ```ts + import { createBackend } from '@backstage/backend-defaults'; + import { discoveryFeatureLoader } from '@backstage/backend-defaults'; + //... + + const backend = createBackend(); + //... + backend.add(discoveryFeatureLoader); + //... + backend.start(); + ``` + +- 66dbf0a: Allow the cache service to accept the human duration format for TTL + +- 5a8fcb4: Added the option to skip database migrations by setting `skipMigrations: true` in config. This can be done globally in the database config or by plugin id. + +- 0b2a402: Updates to the config schema to match reality + +- Updated dependencies + - @backstage/backend-app-api@0.10.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## @backstage/backend-dynamic-feature-service@0.4.0-next.0 + +### Minor Changes + +- 9080f57: **BREAKING**: `dynamicPluginsServiceFactory` is no longer callable as a function. If you need to provide options to make a custom factory, use `dynamicPluginsSchemasServiceFactoryWithOptions` instead. + +### Patch Changes + +- cd38da8: Deprecate the `dynamicPluginsServiceRef`, `dynamicPluginsServiceFactory` and `dynamicPluginsServiceFactoryWithOptions` in favor of using the `dynamicPluginsFeatureDiscoveryLoader` to discover dynamic features in a new backend system. + + See usage examples below: + + Example using the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + + ```ts + import { createBackend } from '@backstage/backend-defaults'; + import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; + //... + + const backend = createBackend(); + backend.add(dynamicPluginsFeatureDiscoveryLoader); + //... + backend.start(); + ``` + + Passing options to the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + + ```ts + import { createBackend } from '@backstage/backend-defaults'; + import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; + import { myCustomModuleLoader } from './myCustomModuleLoader'; + //... + + const backend = createBackend(); + backend.add( + dynamicPluginsFeatureDiscoveryLoader({ + moduleLoader: myCustomModuleLoader, + }), + ); + //... + backend.start(); + ``` + +- e27f889: Relax type check for a plugin's default export to also accept a BackendFeature defined as a function instead of an object + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. + +- Updated dependencies + - @backstage/backend-app-api@0.10.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-app-node@0.1.25-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-backend@0.3.12-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/backend-plugin-api@0.9.0-next.0 + +### Minor Changes + +- 19ff127: **BREAKING**: The deprecated identity and token manager services have been removed. This means that `coreServices.identity` and `coreServices.tokenManager` are gone, along with related types and utilities in other packages. + +- f687050: Removed the following deprecated exports + + - `BackendPluginConfig` use `CreateBackendPluginOptions` + - `BackendModuleConfig` use `CreateBackendModuleOptions` + - `ExtensionPointConfig` use `CreateExtensionPointOptions` + +- 4d82481: Removed deprecated `ServiceFactoryOrFunction` type. + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- cd38da8: Deprecate the `featureDiscoveryServiceRef` in favor of using the new `discoveryFeatureLoader` instead. +- 66dbf0a: Allow the cache service to accept the human duration format for TTL +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/backend-test-utils@0.6.0-next.0 + +### Minor Changes + +- 19ff127: **BREAKING**: Removed service mocks for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`. +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 0363bf1: There is a new `mockErrorHandler` utility to help in mocking the error middleware in tests. +- Updated dependencies + - @backstage/backend-app-api@0.10.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/frontend-app-api@0.9.0-next.0 + +### Minor Changes + +- 62cce6c: Removed deprecated `icons` property passing to `createApp` and `createSpecializedApp`. Use `IconBundleBlueprint.make` to create extensions instead and include them in the app. + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. + +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. + +- f3a2b91: Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the `app` extension to the respective API extensions. For example, extensions created with `ThemeBlueprint` are now attached to the `themes` input of `api:app-theme` rather than the `app` extension. + +- 5446061: Internal refactor following removal of v1 extension support. The app implementation itself still supports v1 extensions at runtime. + +- 98850de: Added support for defining `replaces` in `createExtensionInput` which will allow extensions to redirect missing `attachTo` points to an input of the created extension. + + ```ts + export const AppThemeApi = ApiBlueprint.makeWithOverrides({ + name: 'app-theme', + inputs: { + themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { + // attachTo: { id: 'app', input: 'themes'} will be redirected to this input instead + replaces: [{ id: 'app', input: 'themes' }], + }), + }, + factory: () { + ... + } + }); + ``` + +- 4a66456: Added the `root` extension the replace the `app` extension as the root of the app. + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/plugin-app@0.1.0-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-plugin-api@0.8.0-next.0 + +### Minor Changes + +- 5446061: **BREAKING**: Removed support for "v1" extensions. This means that it is no longer possible to declare inputs and outputs as objects when using `createExtension`. In addition, all extension creators except for `createComponentExtension` have been removed, use the equivalent blueprint instead. See the [1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations/#130) for more information on this change. +- fec8b57: **BREAKING**: Updated the type parameters for `ExtensionDefinition` and `ExtensionBlueprint` to only have a single object parameter. The base type parameter is exported as `ExtensionDefinitionParameters` and `ExtensionBlueprintParameters` respectively. This is shipped as an immediate breaking change as we expect usage of these types to be rare, and it does not affect the runtime behavior of the API. + + This is a breaking change as it changes the type parameters. Existing usage can generally be updated as follows: + + - `ExtensionDefinition` -> `ExtensionDefinition` + - `ExtensionDefinition` -> `ExtensionDefinition` + - `ExtensionDefinition` -> `ExtensionDefinition<{ config: TConfig }>` + - `ExtensionDefinition` -> `ExtensionDefinition<{ config: TConfig, configInput: TConfigInput }>` + + If you need to infer the parameter you can use `ExtensionDefinitionParameters`, for example: + + ```ts + import { + ExtensionDefinition, + ExtensionDefinitionParameters, + } from '@backstage/frontend-plugin-api'; + + function myUtility( + ext: ExtensionDefinition, + ): T['config'] { + // ... + } + ``` + + The same patterns apply to `ExtensionBlueprint`. + + This change is made to improve the readability of API references and ability to evolve the type parameters in the future. + +### Patch Changes + +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. + +- f3a2b91: Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the `app` extension to the respective API extensions. For example, extensions created with `ThemeBlueprint` are now attached to the `themes` input of `api:app-theme` rather than the `app` extension. + +- 98850de: Added support for defining `replaces` in `createExtensionInput` which will allow extensions to redirect missing `attachTo` points to an input of the created extension. + + ```ts + export const AppThemeApi = ApiBlueprint.makeWithOverrides({ + name: 'app-theme', + inputs: { + themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { + // attachTo: { id: 'app', input: 'themes'} will be redirected to this input instead + replaces: [{ id: 'app', input: 'themes' }], + }), + }, + factory: () { + ... + } + }); + ``` + +- 4a66456: A new `apis` parameter has been added to `factory` for extensions. This is a way to access utility APIs without being coupled to the React context. + +- Updated dependencies + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-test-utils@0.2.0-next.0 + +### Minor Changes + +- 5446061: Removed support for testing "v1" extensions, where outputs are defined as an object rather than an array. +- e6e488c: **BREAKING**: The deprecated `.render()` method has been removed from the extension tester. + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 4a66456: Internal update to add support for passing an `ApiRegistry` when creating the node tree +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. +- f6d1874: Added the ability to provide additional `extensions` and `features` to `renderInTestApp` +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/frontend-app-api@0.9.0-next.0 + - @backstage/plugin-app@0.1.0-next.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + +## @backstage/test-utils@1.6.0-next.0 + +### Minor Changes + +- d47be30: Added the icons option to the renderInTestApp function's TestAppOptions to be forwarded to the icons option of `createApp`. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-app@0.1.0-next.0 + +### Minor Changes + +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-auth-backend@0.23.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- 3c2d690: Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. +- Updated dependencies + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-google-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-oidc-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-okta-provider@0.1.0-next.0 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.2.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- ecbc47e: Fix a bug where the signer was checked from the payload instead of the header +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-google-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 3c2d690: Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-okta-provider@0.1.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/catalog-model@1.6.0 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-backend-module-github@0.7.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend-module-gitlab@0.4.2-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.9.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-logs@0.1.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.4 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-devtools-backend@0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-events-node@0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + +## @backstage/plugin-notifications-backend@0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-notifications-common@0.0.5 + +## @backstage/plugin-notifications-backend-module-email@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-scaffolder@1.25.0-next.0 + +### Minor Changes + +- 5143616: Added EntityOwnerPicker component to the TemplateListPage to allow filtering on owner + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-scaffolder-backend@1.25.0-next.0 + +### Minor Changes + +- 62898bd: `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. + +### Patch Changes + +- c160951: Found the issue during testing the clean up of the workspace for the database implementation. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 9e5923d: Added test cases for publish:gerrit action examples +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/plugin-notifications-common@0.0.5 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.12-next.0 + +## @backstage/plugin-scaffolder-react@1.12.0-next.0 + +### Minor Changes + +- 4512f71: Add `ui:backstage.review.name` option for custom item names on scaffolder review page, and also add support for rendering the `title` property instead of the key name. + +### Patch Changes + +- 3ebb64f: - Fix secret widget field not displaying as required. + - Fix secret widget not able to be required inside nested objects. + - Fix secret widget not able to be disabled. + - Support `minLength` and `maxLength` properties for secret widget. +- 8dd6ef6: Fix an issue where keys with duplicate final key parts are not all displayed in the `ReviewState`. Change the way the keys are formatted to include the full schema path, separated by `>`. +- 9a0672a: Scaffolder review page shows static amount of asterisks for secret fields. +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-react@1.8.0-next.0 + +### Minor Changes + +- 9d66d8c: Make use of the `useApp` hook to retrieve the specified search icon in the SearchBar + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- f26ff99: Slight type tweak to match newer React versions better +- 5446061: The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130). +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-signals-backend@0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 3ec5ccb: The `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + +## @backstage/backend-openapi-utils@0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/errors@1.2.4 + +## @backstage/backend-tasks@0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/cli@0.27.1-next.0 + +### Patch Changes + +- 1b5c264: Add `checks: 'read'` for default GitHub app permissions +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/eslint-plugin@0.1.8 + - @backstage/integration@1.14.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + +## @backstage/core-compat-api@0.2.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + +## @backstage/create-app@0.5.19-next.0 + +### Patch Changes + +- 019d9ad: Minor dockerfile syntax update +- Updated dependencies + - @backstage/cli-common@0.1.14 + +## @backstage/dev-utils@1.0.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + +## @backstage/repo-tools@0.9.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + +## @techdocs/cli@1.8.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.11-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## @backstage/plugin-api-docs@0.11.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-app-backend@0.3.74-next.0 + +### Patch Changes + +- d3f79d1: Fixing dependency metadata with the new `@backstage/plugin-app` package +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-app-node@0.1.25-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-app-node@0.1.25-next.0 + +### Patch Changes + +- d3f79d1: Fixing dependency metadata with the new `@backstage/plugin-app` package +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/config-loader@1.9.0 + +## @backstage/plugin-app-visualizer@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + +## @backstage/plugin-auth-node@0.5.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog@1.22.1-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 5446061: The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130). +- 180a45f: Entity presentation api now only fetches fields that are required to display entity title +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-catalog-backend@1.25.3-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 53cce86: Fixed an issue with the by-query call, where ordering by a field that does not exist on all entities led to not all results being returned +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-catalog-backend-module-aws@0.4.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-catalog-backend-module-azure@0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.3.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + +## @backstage/plugin-catalog-backend-module-github@0.7.3-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-gitlab@0.4.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- b446954: Remove dependency on backend-common +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.5.3-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 4b28e39: Updated the README to include documentation for the new backend support +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.6.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 3c2d690: Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-graph@0.4.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-import@0.12.3-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-node@1.12.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-catalog-react@1.12.4-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 5446061: The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130). +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-devtools@0.1.18-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 019d9ad: Minor dockerfile syntax update +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-events-backend@0.3.12-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-events-backend-module-azure@0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + +## @backstage/plugin-events-backend-module-gerrit@0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + +## @backstage/plugin-events-backend-module-github@0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-events-backend-module-gitlab@0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + +## @backstage/plugin-events-backend-test-utils@0.1.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.0-next.0 + +## @backstage/plugin-home@0.7.10-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-home-react@0.1.16 + +## @backstage/plugin-kubernetes@0.11.14-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- bfc0f42: Make k8s entity content appear on components & resources only by default in new FE system +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.2 + +## @backstage/plugin-kubernetes-backend@0.18.6-next.0 + +### Patch Changes + +- f55f8bf: The `KubernetesBuilder` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-kubernetes-node@0.1.19-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-kubernetes-cluster@0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.2 + +## @backstage/plugin-kubernetes-node@0.1.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-notifications@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-signals-react@0.0.5-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + +## @backstage/plugin-notifications-node@0.2.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-notifications-common@0.0.5 + +## @backstage/plugin-org@0.6.29-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-org-react@0.1.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + +## @backstage/plugin-permission-backend@0.5.49-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- fcb9356: Deprecated `createRouter` and its router options in favour of the new backend system. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-permission-node@0.8.3-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-proxy-backend@0.5.6-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- d298e6e: Deprecated `createRouter` and its router options in favour of the new backend system. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-node@0.4.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-scaffolder-node-test-utils@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.0 + - @backstage/backend-test-utils@0.6.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-search@1.4.16-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend@1.5.17-next.0 + +### Patch Changes + +- 5726390: Deprecate create router as the legacy backend system will no longer be supported. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. + +- 5726390: The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module: + + - `DefaultCatalogCollatorFactory`: ; + - `ToolDocumentCollatorFactory`: ; + - `DefaultTechDocsCollatorFactory`: . + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-elasticsearch@1.5.6-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 5726390: Internal refactor to use `LoggerService` and `DatabaseService` instead of the legacy `Logger` and `PluginDatabaseManager` types. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-explore@0.2.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. + +- 5726390: The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module: + + - `DefaultCatalogCollatorFactory`: ; + - `ToolDocumentCollatorFactory`: ; + - `DefaultTechDocsCollatorFactory`: . + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-pg@0.5.35-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 5726390: Internal refactor to use `LoggerService` and `DatabaseService` instead of the legacy `Logger` and `PluginDatabaseManager` types. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-techdocs@0.2.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. + +- 5726390: The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module: + + - `DefaultCatalogCollatorFactory`: ; + - `ToolDocumentCollatorFactory`: ; + - `DefaultTechDocsCollatorFactory`: . + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.12.11-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-node@1.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-signals@0.0.10-next.0 + +### Patch Changes + +- 5add8e1: Added a `SignalsDisplay` extension to allows the signals plugin to be installed in an app as follows: + + ```tsx + export default app.createRoot( + <> + + + + + + {routes} + + , + ); + ``` + + With this in place you can remove the explicit installation via the `plugins` option for `createApp`. + +- Updated dependencies + - @backstage/plugin-signals-react@0.0.5-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + +## @backstage/plugin-signals-node@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-signals-react@0.0.5-next.0 + +### Patch Changes + +- 0389801: Fix for `useSignal` returning the inverse value for `isSignalsAvailable`. +- Updated dependencies + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-techdocs@1.10.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + +## @backstage/plugin-techdocs-backend@1.10.13-next.0 + +### Patch Changes + +- 086c32d: Dedicated token for techdocs cache sync +- 5b679ac: The `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.12.11-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + +## @backstage/plugin-techdocs-node@1.12.11-next.0 + +### Patch Changes + +- 4417dd4: Fix typo and unify TechDocs casing in doc strings +- 33ebb28: As the `@backstage/backend-common` package is deprecated, we have updated the `techdocs-node` package to stop depending on it. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-react@1.2.8-next.0 + +### Patch Changes + +- 5ee3d27: Fixed issue in useShadowRootElements which could lead to unlimited render loops +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + +## @backstage/plugin-user-settings@0.8.12-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-signals-react@0.0.5-next.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.2.24-next.0 + +### Patch Changes + +- 164ce3e: In preparation to stop supporting to the legacy backend system, the `createRouter` function is now deprecated and we strongly recommend you [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) your backend to the new system. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 1b98099: Replaced usage of the deprecated identity service with the new HTTP auth service for the new backend system. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.101-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/frontend-app-api@0.9.0-next.0 + - @backstage/plugin-catalog-import@0.12.3-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-user-settings@0.8.12-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/plugin-kubernetes@0.11.14-next.0 + - @backstage/plugin-scaffolder@1.25.0-next.0 + - @backstage/plugin-api-docs@0.11.9-next.0 + - @backstage/plugin-devtools@0.1.18-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/plugin-search@1.4.16-next.0 + - @backstage/plugin-home@0.7.10-next.0 + - @backstage/plugin-org@0.6.29-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.0 + - @backstage/cli@0.27.1-next.0 + - @backstage/plugin-signals@0.0.10-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-unprocessed-entities@0.2.7 + - @backstage/plugin-notifications@0.3.1-next.0 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-search-common@1.2.14 + +## example-app-next@0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/frontend-app-api@0.9.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-import@0.12.3-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-user-settings@0.8.12-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/plugin-kubernetes@0.11.14-next.0 + - @backstage/plugin-scaffolder@1.25.0-next.0 + - @backstage/plugin-api-docs@0.11.9-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/plugin-search@1.4.16-next.0 + - @backstage/plugin-home@0.7.10-next.0 + - @backstage/plugin-org@0.6.29-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.0 + - @backstage/plugin-app@0.1.0-next.0 + - @backstage/cli@0.27.1-next.0 + - @backstage/plugin-signals@0.0.10-next.0 + - @backstage/plugin-app-visualizer@0.1.10-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-unprocessed-entities@0.2.7 + - @backstage/plugin-notifications@0.3.1-next.0 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-search-common@1.2.14 + +## app-next-example-plugin@0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + +## example-backend@0.0.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-backend@1.10.13-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-search-backend@1.5.17-next.0 + - @backstage/plugin-kubernetes-backend@0.18.6-next.0 + - @backstage/plugin-scaffolder-backend@1.25.0-next.0 + - @backstage/plugin-app-backend@0.3.74-next.0 + - @backstage/plugin-signals-backend@0.2.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.0 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-permission-backend@0.5.49-next.0 + - @backstage/plugin-proxy-backend@0.5.6-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.0-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.0 + - @backstage/plugin-devtools-backend@0.4.0-next.0 + - @backstage/plugin-notifications-backend@0.4.0-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-permission-common@0.8.1 + +## example-backend-legacy@0.2.102-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-backend@1.10.13-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-search-backend@1.5.17-next.0 + - @backstage/plugin-kubernetes-backend@0.18.6-next.0 + - @backstage/plugin-scaffolder-backend@1.25.0-next.0 + - @backstage/plugin-app-backend@0.3.74-next.0 + - @backstage/plugin-signals-backend@0.2.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.0 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-backend@0.3.12-next.0 + - @backstage/plugin-permission-backend@0.5.49-next.0 + - @backstage/plugin-proxy-backend@0.5.6-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.5.6-next.0 + - @backstage/plugin-search-backend-module-pg@0.5.35-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.0 + - @backstage/plugin-devtools-backend@0.4.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-permission-common@0.8.1 + +## e2e-test@0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.19-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/errors@1.2.4 + +## techdocs-cli-embedded-app@0.2.100-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/cli@0.27.1-next.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + +## @internal/plugin-todo-list-backend@1.0.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 diff --git a/docs/tooling/cli/03-commands.md b/docs/tooling/cli/03-commands.md index a6652bd036..913a71f514 100644 --- a/docs/tooling/cli/03-commands.md +++ b/docs/tooling/cli/03-commands.md @@ -326,7 +326,7 @@ Options: ## versions\:bump Bump all `@backstage` packages to the latest versions. This checks for updates -in the package registry, and will update entries `package.json` files when necessary. +in the package registry, and will update entries `package.json` files when necessary. See more how this command can be configured and used [for keeping Backstage updated](../../getting-started/keeping-backstage-updated.md). ```text Usage: backstage-cli versions:bump [options] diff --git a/docs/tutorials/yarn-migration.md b/docs/tutorials/yarn-migration.md index bc5980b2f5..5c87125277 100644 --- a/docs/tutorials/yarn-migration.md +++ b/docs/tutorials/yarn-migration.md @@ -85,7 +85,7 @@ FROM node:16-bullseye-slim # highlight-add-start # Set Python interpreter for `node-gyp` to use -ENV PYTHON /usr/bin/python3 +ENV PYTHON=/usr/bin/python3 # highlight-add-end # Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, diff --git a/microsite/data/plugins/ai-assistant.yaml b/microsite/data/plugins/ai-assistant.yaml index 997846c14b..6ac4b6cd9e 100644 --- a/microsite/data/plugins/ai-assistant.yaml +++ b/microsite/data/plugins/ai-assistant.yaml @@ -1,6 +1,6 @@ --- title: AI Assistant - RAG AI -author: RoadieHQ +author: roadie.io authorUrl: https://roadie.io category: Search description: Query LLMs with contextual information from your catalog. diff --git a/microsite/data/plugins/analytics-module-generic.yaml b/microsite/data/plugins/analytics-module-generic.yaml new file mode 100644 index 0000000000..260319aca6 --- /dev/null +++ b/microsite/data/plugins/analytics-module-generic.yaml @@ -0,0 +1,9 @@ +--- +title: 'Analytics Module: Generic HTTP' +author: Josephine Pfeiffer +authorUrl: https://josie.lol +category: Monitoring +description: Track usage of your Backstage instance using generic POST endpoints. +documentation: https://github.com/pfeifferj/backstage-plugin-analytics-generic/blob/main/README.md +npmPackageName: '@pfeifferj/backstage-plugin-analytics-generic' +addedDate: '2024-08-23' diff --git a/microsite/data/plugins/aws-app-development.yaml b/microsite/data/plugins/aws-app-development.yaml index 47b40b8b3b..5df0598c5e 100644 --- a/microsite/data/plugins/aws-app-development.yaml +++ b/microsite/data/plugins/aws-app-development.yaml @@ -1,10 +1,10 @@ --- -title: OPA on AWS +title: Harmonix on AWS author: Amazon Web Services authorUrl: https://aws.amazon.com/ category: Infrastructure -description: Orchestrate Platforms and Applications (OPA) allows customers to build and manage AWS Apps & Environments within Backstage -documentation: https://opaonaws.io -iconUrl: https://github.com/awslabs/app-development-for-backstage-io-on-aws/blob/main/website/static/img/white_OPA_text02.png?raw=true +description: Harmonix allows customers to build and manage AWS Apps & Environments within Backstage +documentation: https://harmonixonaws.io +iconUrl: https://github.com/awslabs/harmonix/blob/main/docs/images/harmonix-white.png?raw=true npmPackageName: '@aws/plugin-aws-apps-for-backstage' addedDate: '2023-05-10' diff --git a/microsite/data/plugins/blackduck.yaml b/microsite/data/plugins/blackduck.yaml index 4ef192d2fc..7a7858fff2 100644 --- a/microsite/data/plugins/blackduck.yaml +++ b/microsite/data/plugins/blackduck.yaml @@ -4,9 +4,9 @@ author: Deepankumar authorUrl: https://github.com/deepan10 category: Security description: BlackDuck scanned vulnerabilities in Backstage. -documentation: https://github.com/deepan10/backstage-plugin-blackduck +documentation: https://github.com/backstage/community-plugins/tree/main/workspaces/blackduck/plugins/blackduck iconUrl: https://avatars.githubusercontent.com/u/431461?s=200&v=4 -npmPackageName: '@deepan10/backstage-plugin-blackduck' +npmPackageName: '@backstage-community/plugin-blackduck' tags: - security addedDate: '2022-12-03' diff --git a/microsite/data/plugins/gravatar.yaml b/microsite/data/plugins/gravatar.yaml new file mode 100644 index 0000000000..d31c16a155 --- /dev/null +++ b/microsite/data/plugins/gravatar.yaml @@ -0,0 +1,14 @@ +--- +title: Gravatar catalog processor +author: roadie.io +authorUrl: https://roadie.io +category: Catalog +description: The processor generates a Gravatar URL for each user based on their email address and the generated Gravatar URL is added to the spec.profile.picture field of User entities. +documentation: https://github.com/RoadieHQ/roadie-backstage-plugins/blob/main/plugins/backend/catalog-backend-module-gravatar/README.md +iconUrl: https://gravatar.com/avatar/gravatar +npmPackageName: '@roadiehq/catalog-backend-module-gravatar' +tags: + - gravatar + - catalog + - processor +addedDate: '2024-08-26' diff --git a/microsite/data/plugins/kubelog.yaml b/microsite/data/plugins/kubelog.yaml new file mode 100644 index 0000000000..fcadba2978 --- /dev/null +++ b/microsite/data/plugins/kubelog.yaml @@ -0,0 +1,10 @@ +--- +title: Kubelog +author: Julio Fernandez +authorUrl: https://github.com/jfvilas +category: Observability +description: Kubelog provides your Backstage users the ability to view logs of your Kubernetes objects linked to Backstage entities. Security controls can be applied. +documentation: https://github.com/jfvilas/kubelog +iconUrl: https://raw.githubusercontent.com/jfvilas/kubelog/master/src/assets/kubelog-logo.png +npmPackageName: '@jfvilas/plugin-kubelog' +addedDate: 2024-08-25 diff --git a/microsite/data/plugins/launchdarkly.yaml b/microsite/data/plugins/launchdarkly.yaml new file mode 100644 index 0000000000..30e4cab685 --- /dev/null +++ b/microsite/data/plugins/launchdarkly.yaml @@ -0,0 +1,9 @@ +--- +title: LaunchDarkly +author: roadie.io +authorUrl: https://roadie.io +category: CI/CD +description: View LaunchDarkly feature flags for your entities in Backstage. +documentation: https://github.com/RoadieHQ/roadie-backstage-plugins/blob/launchdarkly/plugins/frontend/backstage-plugin-launchdarkly/README.md +npmPackageName: '@roadiehq/backstage-plugin-launchdarkly' +addedDate: '2024-07-27' diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index 67018c6e33..a6909f139f 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -195,6 +195,10 @@ const config: Config = { from: '/docs/local-dev/debugging/', to: '/docs/tooling/local-dev/debugging', }, + { + from: '/docs/plugins/url-reader/', + to: '/docs/backend-system/core-services/url-reader', + }, ], }, ], diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 92567c844d..baca4f4db6 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -274,8 +274,7 @@ "items": [ "plugins/proxying", "plugins/backend-plugin", - "plugins/call-existing-api", - "plugins/url-reader" + "plugins/call-existing-api" ] }, { @@ -483,6 +482,7 @@ "frontend-system/building-apps/index", "frontend-system/building-apps/configuring-extensions", "frontend-system/building-apps/built-in-extensions", + "frontend-system/building-apps/plugin-conversion", "frontend-system/building-apps/migrating" ] }, diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 00732d79f2..d5d78e9371 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -197,118 +197,117 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": - version: 7.23.5 - resolution: "@babel/code-frame@npm:7.23.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.24.7, @babel/code-frame@npm:^7.8.3": + version: 7.24.7 + resolution: "@babel/code-frame@npm:7.24.7" dependencies: - "@babel/highlight": ^7.23.4 - chalk: ^2.4.2 - checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a + "@babel/highlight": ^7.24.7 + picocolors: ^1.0.0 + checksum: 830e62cd38775fdf84d612544251ce773d544a8e63df667728cc9e0126eeef14c6ebda79be0f0bc307e8318316b7f58c27ce86702e0a1f5c321d842eb38ffda4 languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/compat-data@npm:7.23.5" - checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.2, @babel/compat-data@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/compat-data@npm:7.25.4" + checksum: b12a91d27c3731a4b0bdc9312a50b1911f41f7f728aaf0d4b32486e2257fd2cb2d3ea1a295e98449600c48f2c7883a3196ca77cda1cef7d97a10c2e83d037974 languageName: node linkType: hard -"@babel/core@npm:^7.19.6, @babel/core@npm:^7.23.3": - version: 7.23.9 - resolution: "@babel/core@npm:7.23.9" +"@babel/core@npm:^7.21.3, @babel/core@npm:^7.23.3": + version: 7.25.2 + resolution: "@babel/core@npm:7.25.2" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.23.5 - "@babel/generator": ^7.23.6 - "@babel/helper-compilation-targets": ^7.23.6 - "@babel/helper-module-transforms": ^7.23.3 - "@babel/helpers": ^7.23.9 - "@babel/parser": ^7.23.9 - "@babel/template": ^7.23.9 - "@babel/traverse": ^7.23.9 - "@babel/types": ^7.23.9 + "@babel/code-frame": ^7.24.7 + "@babel/generator": ^7.25.0 + "@babel/helper-compilation-targets": ^7.25.2 + "@babel/helper-module-transforms": ^7.25.2 + "@babel/helpers": ^7.25.0 + "@babel/parser": ^7.25.0 + "@babel/template": ^7.25.0 + "@babel/traverse": ^7.25.2 + "@babel/types": ^7.25.2 convert-source-map: ^2.0.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.3 semver: ^6.3.1 - checksum: 634a511f74db52a5f5a283c1121f25e2227b006c095b84a02a40a9213842489cd82dc7d61cdc74e10b5bcd9bb0a4e28bab47635b54c7e2256d47ab57356e2a76 + checksum: 9a1ef604a7eb62195f70f9370cec45472a08114e3934e3eaaedee8fd754edf0730e62347c7b4b5e67d743ce57b5bb8cf3b92459482ca94d06e06246ef021390a languageName: node linkType: hard -"@babel/generator@npm:^7.23.3, @babel/generator@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/generator@npm:7.23.6" +"@babel/generator@npm:^7.23.3, @babel/generator@npm:^7.25.0, @babel/generator@npm:^7.25.4": + version: 7.25.5 + resolution: "@babel/generator@npm:7.25.5" dependencies: - "@babel/types": ^7.23.6 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 + "@babel/types": ^7.25.4 + "@jridgewell/gen-mapping": ^0.3.5 + "@jridgewell/trace-mapping": ^0.3.25 jsesc: ^2.5.1 - checksum: 1a1a1c4eac210f174cd108d479464d053930a812798e09fee069377de39a893422df5b5b146199ead7239ae6d3a04697b45fc9ac6e38e0f6b76374390f91fc6c + checksum: d7713f02536a8144eca810e9b13ae854b05fec462348eaf52e7b50df2c0a312bc43bfff0e8e10d6dd982e8986d61175ac8e67d7358a8b4dad9db4d6733bf0c9c languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" +"@babel/helper-annotate-as-pure@npm:^7.22.5, @babel/helper-annotate-as-pure@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.24.7" dependencies: - "@babel/types": ^7.22.5 - checksum: 53da330f1835c46f26b7bf4da31f7a496dee9fd8696cca12366b94ba19d97421ce519a74a837f687749318f94d1a37f8d1abcbf35e8ed22c32d16373b2f6198d + "@babel/types": ^7.24.7 + checksum: 6178566099a6a0657db7a7fa601a54fb4731ca0b8614fbdccfd8e523c210c13963649bc8fdfd53ce7dd14d05e3dda2fb22dea5b30113c488b9eb1a906d60212e languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": - version: 7.22.15 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.15" +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.7" dependencies: - "@babel/types": ^7.22.15 - checksum: 639c697a1c729f9fafa2dd4c9af2e18568190299b5907bd4c2d0bc818fcbd1e83ffeecc2af24327a7faa7ac4c34edd9d7940510a5e66296c19bad17001cf5c7a + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 71a6158a9fdebffb82fdc400d5555ba8f2e370cea81a0d578155877bdc4db7d5252b75c43b2fdf3f72b3f68348891f99bd35ae315542daad1b7ace8322b1abcb languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/helper-compilation-targets@npm:7.23.6" +"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.24.7, @babel/helper-compilation-targets@npm:^7.24.8, @babel/helper-compilation-targets@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-compilation-targets@npm:7.25.2" dependencies: - "@babel/compat-data": ^7.23.5 - "@babel/helper-validator-option": ^7.23.5 - browserslist: ^4.22.2 + "@babel/compat-data": ^7.25.2 + "@babel/helper-validator-option": ^7.24.8 + browserslist: ^4.23.1 lru-cache: ^5.1.1 semver: ^6.3.1 - checksum: c630b98d4527ac8fe2c58d9a06e785dfb2b73ec71b7c4f2ddf90f814b5f75b547f3c015f110a010fd31f76e3864daaf09f3adcd2f6acdbfb18a8de3a48717590 + checksum: aed33c5496cb9db4b5e2d44e26bf8bc474074cc7f7bb5ebe1d4a20fdeb362cb3ba9e1596ca18c7484bcd6e5c3a155ab975e420d520c0ae60df81f9de04d0fd16 languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.22.11, @babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.22.5": - version: 7.22.15 - resolution: "@babel/helper-create-class-features-plugin@npm:7.22.15" +"@babel/helper-create-class-features-plugin@npm:^7.24.7, @babel/helper-create-class-features-plugin@npm:^7.25.0, @babel/helper-create-class-features-plugin@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/helper-create-class-features-plugin@npm:7.25.4" dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 - "@babel/helper-member-expression-to-functions": ^7.22.15 - "@babel/helper-optimise-call-expression": ^7.22.5 - "@babel/helper-replace-supers": ^7.22.9 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-member-expression-to-functions": ^7.24.8 + "@babel/helper-optimise-call-expression": ^7.24.7 + "@babel/helper-replace-supers": ^7.25.0 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + "@babel/traverse": ^7.25.4 semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: 52c500d8d164abb3a360b1b7c4b8fff77bc4a5920d3a2b41ae6e1d30617b0dc0b972c1f5db35b1752007e04a748908b4a99bc872b73549ae837e87dcdde005a3 + checksum: 4544ebda4516eb25efdebd47ca024bd7bdb1eb6e7cc3ad89688c8ef8e889734c2f4411ed78981899c641394f013f246f2af63d92a0e9270f6c453309b4cb89ba languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": - version: 7.22.15 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15" +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.24.7, @babel/helper-create-regexp-features-plugin@npm:^7.25.0, @babel/helper-create-regexp-features-plugin@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.25.2" dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-annotate-as-pure": ^7.24.7 regexpu-core: ^5.3.1 semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: 0243b8d4854f1dc8861b1029a46d3f6393ad72f366a5a08e36a4648aa682044f06da4c6e87a456260e1e1b33c999f898ba591a0760842c1387bcc93fbf2151a6 + checksum: df55fdc6a1f3090dd37d91347df52d9322d52affa239543808dc142f8fe35e6787e67d8612337668198fac85826fafa9e6772e6c28b7d249ec94e6fafae5da6e languageName: node linkType: hard @@ -327,218 +326,238 @@ __metadata: languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.22.20, @babel/helper-environment-visitor@npm:^7.22.5": - version: 7.22.20 - resolution: "@babel/helper-environment-visitor@npm:7.22.20" - checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 +"@babel/helper-define-polyfill-provider@npm:^0.6.2": + version: 0.6.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" + dependencies: + "@babel/helper-compilation-targets": ^7.22.6 + "@babel/helper-plugin-utils": ^7.22.5 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 2bba965ea9a4887ddf9c11d51d740ab473bd7597b787d042c325f6a45912dfe908c2d6bb1d837bf82f7e9fa51e6ad5150563c58131d2bb85515e63d971414a9c languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.22.5, @babel/helper-function-name@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/helper-function-name@npm:7.23.0" +"@babel/helper-member-expression-to-functions@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-member-expression-to-functions@npm:7.24.8" dependencies: - "@babel/template": ^7.22.15 - "@babel/types": ^7.23.0 - checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 + "@babel/traverse": ^7.24.8 + "@babel/types": ^7.24.8 + checksum: bf923d05d81b06857f4ca4fe9c528c9c447a58db5ea39595bb559eae2fce01a8266173db0fd6a2ec129d7bbbb9bb22f4e90008252f7c66b422c76630a878a4bc languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-hoist-variables@npm:7.22.5" +"@babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-imports@npm:7.24.7" dependencies: - "@babel/types": ^7.22.5 - checksum: 394ca191b4ac908a76e7c50ab52102669efe3a1c277033e49467913c7ed6f7c64d7eacbeabf3bed39ea1f41731e22993f763b1edce0f74ff8563fd1f380d92cc + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 8ac15d96d262b8940bc469052a048e06430bba1296369be695fabdf6799f201dd0b00151762b56012a218464e706bc033f27c07f6cec20c6f8f5fd6543c67054 languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" +"@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.24.8, @babel/helper-module-transforms@npm:^7.25.0, @babel/helper-module-transforms@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/helper-module-transforms@npm:7.25.2" dependencies: - "@babel/types": ^7.23.0 - checksum: 494659361370c979ada711ca685e2efe9460683c36db1b283b446122596602c901e291e09f2f980ecedfe6e0f2bd5386cb59768285446530df10c14df1024e75 - languageName: node - linkType: hard - -"@babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5": - version: 7.22.15 - resolution: "@babel/helper-module-imports@npm:7.22.15" - dependencies: - "@babel/types": ^7.22.15 - checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.23.0, @babel/helper-module-transforms@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/helper-module-transforms@npm:7.23.3" - dependencies: - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-module-imports": ^7.22.15 - "@babel/helper-simple-access": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/helper-validator-identifier": ^7.22.20 + "@babel/helper-module-imports": ^7.24.7 + "@babel/helper-simple-access": ^7.24.7 + "@babel/helper-validator-identifier": ^7.24.7 + "@babel/traverse": ^7.25.2 peerDependencies: "@babel/core": ^7.0.0 - checksum: 5d0895cfba0e16ae16f3aa92fee108517023ad89a855289c4eb1d46f7aef4519adf8e6f971e1d55ac20c5461610e17213f1144097a8f932e768a9132e2278d71 + checksum: 282d4e3308df6746289e46e9c39a0870819630af5f84d632559171e4fae6045684d771a65f62df3d569e88ccf81dc2def78b8338a449ae3a94bb421aa14fc367 languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" +"@babel/helper-optimise-call-expression@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.24.7" dependencies: - "@babel/types": ^7.22.5 - checksum: c70ef6cc6b6ed32eeeec4482127e8be5451d0e5282d5495d5d569d39eb04d7f1d66ec99b327f45d1d5842a9ad8c22d48567e93fc502003a47de78d122e355f7c + "@babel/types": ^7.24.7 + checksum: 280654eaf90e92bf383d7eed49019573fb35a98c9e992668f701ad099957246721044be2068cf6840cb2299e0ad393705a1981c88c23a1048096a8d59e5f79a3 languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.22.5 - resolution: "@babel/helper-plugin-utils@npm:7.22.5" - checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.24.8 + resolution: "@babel/helper-plugin-utils@npm:7.24.8" + checksum: 73b1a83ba8bcee21dc94de2eb7323207391715e4369fd55844bb15cf13e3df6f3d13a40786d990e6370bf0f571d94fc31f70dec96c1d1002058258c35ca3767a languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.22.5, @babel/helper-remap-async-to-generator@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20" +"@babel/helper-remap-async-to-generator@npm:^7.24.7, @babel/helper-remap-async-to-generator@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/helper-remap-async-to-generator@npm:7.25.0" dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-wrap-function": ^7.22.20 + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-wrap-function": ^7.25.0 + "@babel/traverse": ^7.25.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 2fe6300a6f1b58211dffa0aed1b45d4958506d096543663dba83bd9251fe8d670fa909143a65b45e72acb49e7e20fbdb73eae315d9ddaced467948c3329986e7 + checksum: 47f3065e43fe9d6128ddb4291ffb9cf031935379265fd13de972b5f241943121f7583efb69cd2e1ecf39e3d0f76f047547d56c3fcc2c853b326fad5465da0bd7 languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.22.5, @babel/helper-replace-supers@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/helper-replace-supers@npm:7.22.20" +"@babel/helper-replace-supers@npm:^7.24.7, @babel/helper-replace-supers@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/helper-replace-supers@npm:7.25.0" dependencies: - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-member-expression-to-functions": ^7.22.15 - "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-member-expression-to-functions": ^7.24.8 + "@babel/helper-optimise-call-expression": ^7.24.7 + "@babel/traverse": ^7.25.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: a0008332e24daedea2e9498733e3c39b389d6d4512637e000f96f62b797e702ee24a407ccbcd7a236a551590a38f31282829a8ef35c50a3c0457d88218cae639 + checksum: f669fc2487c22d40b808f94b9c3ee41129484d5ef0ba689bdd70f216ff91e10b6b021d2f8cd37e7bdd700235a2a6ae6622526344f064528190383bf661ac65f8 languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-simple-access@npm:7.22.5" +"@babel/helper-simple-access@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-simple-access@npm:7.24.7" dependencies: - "@babel/types": ^7.22.5 - checksum: fe9686714caf7d70aedb46c3cce090f8b915b206e09225f1e4dbc416786c2fdbbee40b38b23c268b7ccef749dd2db35f255338fb4f2444429874d900dede5ad2 + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: ddbf55f9dea1900213f2a1a8500fabfd21c5a20f44dcfa957e4b0d8638c730f88751c77f678644f754f1a1dc73f4eb8b766c300deb45a9daad000e4247957819 languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.24.7" dependencies: - "@babel/types": ^7.22.5 - checksum: 1012ef2295eb12dc073f2b9edf3425661e9b8432a3387e62a8bc27c42963f1f216ab3124228015c748770b2257b4f1fda882ca8fa34c0bf485e929ae5bc45244 + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 11b28fe534ce2b1a67c4d8e51a7b5711a2a0a0cae802f74614eee54cca58c744d9a62f6f60103c41759e81c537d270bfd665bf368a6bea214c6052f2094f8407 languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.22.6": - version: 7.22.6 - resolution: "@babel/helper-split-export-declaration@npm:7.22.6" +"@babel/helper-string-parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-string-parser@npm:7.24.8" + checksum: 39b03c5119216883878655b149148dc4d2e284791e969b19467a9411fccaa33f7a713add98f4db5ed519535f70ad273cdadfd2eb54d47ebbdeac5083351328ce + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-identifier@npm:7.24.7" + checksum: 6799ab117cefc0ecd35cd0b40ead320c621a298ecac88686a14cffceaac89d80cdb3c178f969861bf5fa5e4f766648f9161ea0752ecfe080d8e89e3147270257 + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.24.7, @babel/helper-validator-option@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-validator-option@npm:7.24.8" + checksum: a52442dfa74be6719c0608fee3225bd0493c4057459f3014681ea1a4643cd38b68ff477fe867c4b356da7330d085f247f0724d300582fa4ab9a02efaf34d107c + languageName: node + linkType: hard + +"@babel/helper-wrap-function@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/helper-wrap-function@npm:7.25.0" dependencies: - "@babel/types": ^7.22.5 - checksum: e141cace583b19d9195f9c2b8e17a3ae913b7ee9b8120246d0f9ca349ca6f03cb2c001fd5ec57488c544347c0bb584afec66c936511e447fd20a360e591ac921 + "@babel/template": ^7.25.0 + "@babel/traverse": ^7.25.0 + "@babel/types": ^7.25.0 + checksum: 0095b4741704066d1687f9bbd5370bb88c733919e4275e49615f70c180208148ff5f24ab58d186ce92f8f5d28eab034ec6617e9264590cc4744c75302857629c languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.23.4": - version: 7.23.4 - resolution: "@babel/helper-string-parser@npm:7.23.4" - checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-validator-identifier@npm:7.22.20" - checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc - languageName: node - linkType: hard - -"@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/helper-validator-option@npm:7.23.5" - checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e - languageName: node - linkType: hard - -"@babel/helper-wrap-function@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-wrap-function@npm:7.22.20" +"@babel/helpers@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/helpers@npm:7.25.0" dependencies: - "@babel/helper-function-name": ^7.22.5 - "@babel/template": ^7.22.15 - "@babel/types": ^7.22.19 - checksum: 221ed9b5572612aeb571e4ce6a256f2dee85b3c9536f1dd5e611b0255e5f59a3d0ec392d8d46d4152149156a8109f92f20379b1d6d36abb613176e0e33f05fca + "@babel/template": ^7.25.0 + "@babel/types": ^7.25.0 + checksum: 739e3704ff41a30f5eaac469b553f4d3ab02be6ced083f5925851532dfbd9efc5c347728e77b754ed0b262a4e5e384e60932a62c192d338db7e4b7f3adf9f4a7 languageName: node linkType: hard -"@babel/helpers@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/helpers@npm:7.23.9" +"@babel/highlight@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/highlight@npm:7.24.7" dependencies: - "@babel/template": ^7.23.9 - "@babel/traverse": ^7.23.9 - "@babel/types": ^7.23.9 - checksum: 2678231192c0471dbc2fc403fb19456cc46b1afefcfebf6bc0f48b2e938fdb0fef2e0fe90c8c8ae1f021dae5012b700372e4b5d15867f1d7764616532e4a6324 - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.23.4": - version: 7.23.4 - resolution: "@babel/highlight@npm:7.23.4" - dependencies: - "@babel/helper-validator-identifier": ^7.22.20 + "@babel/helper-validator-identifier": ^7.24.7 chalk: ^2.4.2 js-tokens: ^4.0.0 - checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 + picocolors: ^1.0.0 + checksum: 5cd3a89f143671c4ac129960024ba678b669e6fc673ce078030f5175002d1d3d52bc10b22c5b916a6faf644b5028e9a4bd2bb264d053d9b05b6a98690f1d46f1 languageName: node linkType: hard -"@babel/parser@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/parser@npm:7.23.9" +"@babel/parser@npm:^7.25.0, @babel/parser@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/parser@npm:7.25.4" + dependencies: + "@babel/types": ^7.25.4 bin: parser: ./bin/babel-parser.js - checksum: e7cd4960ac8671774e13803349da88d512f9292d7baa952173260d3e8f15620a28a3701f14f709d769209022f9e7b79965256b8be204fc550cfe783cdcabe7c7 + checksum: fe4f083d4ad34f019dd7fad672cd007003004fb0a3df9b7315a5da9a5e8e56c1fed95acab6862e7d76cfccb2e8e364bcc307e9117718e6bb6dfb2e87ad065abf languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.15" +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.3": + version: 7.25.3 + resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.3" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/traverse": ^7.25.3 peerDependencies: "@babel/core": ^7.0.0 - checksum: 8910ca21a7ec7c06f7b247d4b86c97c5aa15ef321518f44f6f490c5912fdf82c605aaa02b90892e375d82ccbedeadfdeadd922c1b836c9dd4c596871bf654753 + checksum: d3dba60f360defe70eb43e35a1b17ea9dd4a99e734249e15be3d5c288019644f96f88d7ff51990118fda0845b4ad50f6d869e0382232b1d8b054d113d4eea7e2 languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.15" +"@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:7.25.0" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 - "@babel/plugin-transform-optional-chaining": ^7.22.15 + "@babel/helper-plugin-utils": ^7.24.8 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: fd56d1e6435f2c008ca9050ea906ff7eedcbec43f532f2bf2e7e905d8bf75bf5e4295ea9593f060394e2c8e45737266ccbf718050bad2dd7be4e7613c60d1b5b + languageName: node + linkType: hard + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.25.0" + dependencies: + "@babel/helper-plugin-utils": ^7.24.8 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 13ed301b108d85867d64226bbc4032b07dd1a23aab68e9e32452c4fe3930f2198bb65bdae9c262c4104bd5e45647bc1830d25d43d356ee9a137edd8d5fab8350 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + "@babel/plugin-transform-optional-chaining": ^7.24.7 peerDependencies: "@babel/core": ^7.13.0 - checksum: fbefedc0da014c37f1a50a8094ce7dbbf2181ae93243f23d6ecba2499b5b20196c2124d6a4dfe3e9e0125798e80593103e456352a4beb4e5c6f7c75efb80fdac + checksum: 07b92878ac58a98ea1fdf6a8b4ec3413ba4fa66924e28b694d63ec5b84463123fbf4d7153b56cf3cedfef4a3482c082fe3243c04f8fb2c041b32b0e29b4a9e21 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.25.0" + dependencies: + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/traverse": ^7.25.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: c8d08b8d6cc71451ad2a50cf7db72ab5b41c1e5e2e4d56cf6837a25a61270abd682c6b8881ab025f11a552d2024b3780519bb051459ebb71c27aed13d9917663 languageName: node linkType: hard @@ -606,25 +625,25 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" +"@babel/plugin-syntax-import-assertions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b8b5572db04a7bef1e6cd20debf447e4eef7cb012616f5eceb8fa3e23ce469b8f76ee74fd6d1e158ba17a8f58b0aec579d092fb67c5a30e83ccfbc5754916c1 + checksum: c4d67be4eb1d4637e361477dbe01f5b392b037d17c1f861cfa0faa120030e137aab90a9237931b8040fd31d1e5d159e11866fa1165f78beef7a3be876a391a17 languageName: node linkType: hard -"@babel/plugin-syntax-import-attributes@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" +"@babel/plugin-syntax-import-attributes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 197b3c5ea2a9649347f033342cb222ab47f4645633695205c0250c6bf2af29e643753b8bb24a2db39948bef08e7c540babfd365591eb57fc110cb30b425ffc47 + checksum: 590dbb5d1a15264f74670b427b8d18527672c3d6c91d7bae7e65f80fd810edbc83d90e68065088644cbad3f2457ed265a54a9956fb789fcb9a5b521822b3a275 languageName: node linkType: hard @@ -650,14 +669,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" +"@babel/plugin-syntax-jsx@npm:^7.22.5, @babel/plugin-syntax-jsx@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce + checksum: 7a5ca629d8ca1e1ee78705a78e58c12920d07ed8006d7e7232b31296a384ff5e41d7b649bde5561196041037bbb9f9715be1d1c20975df87ca204f34ad15b965 languageName: node linkType: hard @@ -749,14 +768,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" +"@babel/plugin-syntax-typescript@npm:^7.24.7": + version: 7.25.4 + resolution: "@babel/plugin-syntax-typescript@npm:7.25.4" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a + checksum: 9b89b8930cd5983f64251d75c9fcdc17a8dc73837d6de12220ff972888ecff4054a6467cf0c423cad242aa96c0f0564a39a0823073728cc02239b80d13f02230 languageName: node linkType: hard @@ -772,468 +791,477 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" +"@babel/plugin-transform-arrow-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 35abb6c57062802c7ce8bd96b2ef2883e3124370c688bbd67609f7d2453802fb73944df8808f893b6c67de978eb2bcf87bbfe325e46d6f39b5fcb09ece11d01a + checksum: 707c209b5331c7dc79bd326128c6a6640dbd62a78da1653c844db20c4f36bf7b68454f1bc4d2d051b3fde9136fa291f276ec03a071bb00ee653069ff82f91010 languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.15" +"@babel/plugin-transform-async-generator-functions@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.25.4" dependencies: - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-remap-async-to-generator": ^7.22.9 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-remap-async-to-generator": ^7.25.0 "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/traverse": ^7.25.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: fad98786b446ce63bde0d14a221e2617eef5a7bbca62b49d96f16ab5e1694521234cfba6145b830fbf9af16d60a8a3dbf148e8694830bd91796fe333b0599e73 + checksum: 4235444735a1946f8766fe56564a8134c2c36c73e6cf83b3f2ed5624ebc84ff5979506a6a5b39acdb23aa09d442a6af471710ed408ccce533a2c4d2990b9df6a languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" +"@babel/plugin-transform-async-to-generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.7" dependencies: - "@babel/helper-module-imports": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-remap-async-to-generator": ^7.22.5 + "@babel/helper-module-imports": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-remap-async-to-generator": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b95f23f99dcb379a9f0a1c2a3bbea3f8dc0e1b16dc1ac8b484fe378370169290a7a63d520959a9ba1232837cf74a80e23f6facbe14fd42a3cda6d3c2d7168e62 + checksum: 13704fb3b83effc868db2b71bfb2c77b895c56cb891954fc362e95e200afd523313b0e7cf04ce02f45b05e76017c5b5fa8070c92613727a35131bb542c253a36 languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" +"@babel/plugin-transform-block-scoped-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 416b1341858e8ca4e524dee66044735956ced5f478b2c3b9bc11ec2285b0c25d7dbb96d79887169eb938084c95d0a89338c8b2fe70d473bd9dc92e5d9db1732c + checksum: 249cdcbff4e778b177245f9652b014ea4f3cd245d83297f10a7bf6d97790074089aa62bcde8c08eb299c5e68f2faed346b587d3ebac44d625ba9a83a4ee27028 languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.23.0" +"@babel/plugin-transform-block-scoping@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.25.0" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0cfe925cc3b5a3ad407e2253fab3ceeaa117a4b291c9cb245578880872999bca91bd83ffa0128ae9ca356330702e1ef1dcb26804f28d2cef678239caf629f73e + checksum: b1a8f932f69ad2a47ae3e02b4cedd2a876bfc2ac9cf72a503fd706cdc87272646fe9eed81e068c0fc639647033de29f7fa0c21cddd1da0026f83dbaac97316a8 languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-class-properties@npm:7.22.5" +"@babel/plugin-transform-class-properties@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/plugin-transform-class-properties@npm:7.25.4" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.25.4 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b830152dfc2ff2f647f0abe76e6251babdfbef54d18c4b2c73a6bf76b1a00050a5d998dac80dc901a48514e95604324943a9dd39317073fe0928b559e0e0c579 + checksum: b73f7d968639c6c2dfc13f4c5a8fe45cefd260f0faa7890ae12e65d41211072544ff5e128c8b61a86887b29ffd3df8422dbdfbf61648488e71d4bb599c41f4a5 languageName: node linkType: hard -"@babel/plugin-transform-class-static-block@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-class-static-block@npm:7.22.11" +"@babel/plugin-transform-class-static-block@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-class-static-block@npm:7.24.7" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.11 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-class-static-block": ^7.14.5 peerDependencies: "@babel/core": ^7.12.0 - checksum: 69f040506fad66f1c6918d288d0e0edbc5c8a07c8b4462c1184ad2f9f08995d68b057126c213871c0853ae0c72afc60ec87492049dfacb20902e32346a448bcb + checksum: 324049263504f18416f1c3e24033baebfafd05480fdd885c8ebe6f2b415b0fc8e0b98d719360f9e30743cc78ac387fabc0b3c6606d2b54135756ffb92963b382 languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-classes@npm:7.22.15" +"@babel/plugin-transform-classes@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/plugin-transform-classes@npm:7.25.4" dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-compilation-targets": ^7.22.15 - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 - "@babel/helper-optimise-call-expression": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-replace-supers": ^7.22.9 - "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-compilation-targets": ^7.25.2 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-replace-supers": ^7.25.0 + "@babel/traverse": ^7.25.4 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d3f4d0c107dd8a3557ea3575cc777fab27efa92958b41e4a9822f7499725c1f554beae58855de16ddec0a7b694e45f59a26cea8fbde4275563f72f09c6e039a0 + checksum: 0bf20e46eeb691bd60cee5d1b01950fc37accec88018ecace25099f7c8d8509c1ac54d11b8caf9f2157c6945969520642a3bc421159c1a14e80224dc9a7611de languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" +"@babel/plugin-transform-computed-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/template": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/template": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c2a77a0f94ec71efbc569109ec14ea2aa925b333289272ced8b33c6108bdbb02caf01830ffc7e49486b62dec51911924d13f3a76f1149f40daace1898009e131 + checksum: 0cf8c1b1e4ea57dec8d4612460d84fd4cdbf71a7499bb61ee34632cf89018a59eee818ffca88a8d99ee7057c20a4257044d7d463fda6daef9bf1db9fa81563cb languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/plugin-transform-destructuring@npm:7.23.0" +"@babel/plugin-transform-destructuring@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-destructuring@npm:7.24.8" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: cd6dd454ccc2766be551e4f8a04b1acc2aa539fa19e5c7501c56cc2f8cc921dd41a7ffb78455b4c4b2f954fcab8ca4561ba7c9c7bd5af9f19465243603d18cc3 + checksum: 0b4bd3d608979a1e5bd97d9d42acd5ad405c7fffa61efac4c7afd8e86ea6c2d91ab2d94b6a98d63919571363fe76e0b03c4ff161f0f60241b895842596e4a999 languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" +"@babel/plugin-transform-dotall-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.7" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 409b658d11e3082c8f69e9cdef2d96e4d6d11256f005772425fb230cc48fd05945edbfbcb709dab293a1a2f01f9c8a5bb7b4131e632b23264039d9f95864b453 + checksum: 67b10fc6abb1f61f0e765288eb4c6d63d1d0f9fc0660e69f6f2170c56fa16bc74e49857afc644beda112b41771cd90cf52df0940d11e97e52617c77c7dcff171 languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" +"@babel/plugin-transform-duplicate-keys@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bb1280fbabaab6fab2ede585df34900712698210a3bd413f4df5bae6d8c24be36b496c92722ae676a7a67d060a4624f4d6c23b923485f906bfba8773c69f55b4 + checksum: d1da2ff85ecb56a63f4ccfd9dc9ae69400d85f0dadf44ecddd9e71c6e5c7a9178e74e3a9637555f415a2bb14551e563f09f98534ab54f53d25e8439fdde6ba2d languageName: node linkType: hard -"@babel/plugin-transform-dynamic-import@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.11" +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:7.25.0" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-regexp-features-plugin": ^7.25.0 + "@babel/helper-plugin-utils": ^7.24.8 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 608d6b0e77341189508880fd1a9f605a38d0803dd6f678ea3920ab181b17b377f6d5221ae8cf0104c7a044d30d4ddb0366bd064447695671d78457a656bb264f + languageName: node + linkType: hard + +"@babel/plugin-transform-dynamic-import@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-dynamic-import": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 78fc9c532210bf9e8f231747f542318568ac360ee6c27e80853962c984283c73da3f8f8aebe83c2096090a435b356b092ed85de617a156cbe0729d847632be45 + checksum: 776509ff62ab40c12be814a342fc56a5cc09b91fb63032b2633414b635875fd7da03734657be0f6db2891fe6e3033b75d5ddb6f2baabd1a02e4443754a785002 languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" +"@babel/plugin-transform-exponentiation-operator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.7" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f2d660c1b1d51ad5fec1cd5ad426a52187204068c4158f8c4aa977b31535c61b66898d532603eef21c15756827be8277f724c869b888d560f26d7fe848bb5eae + checksum: 23c84a23eb56589fdd35a3540f9a1190615be069110a2270865223c03aee3ba4e0fc68fe14850800cf36f0712b26e4964d3026235261f58f0405a29fe8dac9b1 languageName: node linkType: hard -"@babel/plugin-transform-export-namespace-from@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.11" +"@babel/plugin-transform-export-namespace-from@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 73af5883a321ed56a4bfd43c8a7de0164faebe619287706896fc6ee2f7a4e69042adaa1338c0b8b4bdb9f7e5fdceb016fb1d40694cb43ca3b8827429e8aac4bf + checksum: 3bd3a10038f10ae0dea1ee42137f3edcf7036b5e9e570a0d1cbd0865f03658990c6c2d84fa2475f87a754e7dc5b46766c16f7ce5c9b32c3040150b6a21233a80 languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-for-of@npm:7.22.15" +"@babel/plugin-transform-for-of@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-for-of@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f395ae7bce31e14961460f56cf751b5d6e37dd27d7df5b1f4e49fec1c11b6f9cf71991c7ffbe6549878591e87df0d66af798cf26edfa4bfa6b4c3dba1fb2f73a + checksum: a53b42dc93ab4b7d1ebd3c695b52be22b3d592f6a3dbdb3dc2fea2c8e0a7e1508fe919864c455cde552aec44ce7518625fccbb70c7063373ca228d884f4f49ea languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-function-name@npm:7.22.5" +"@babel/plugin-transform-function-name@npm:^7.25.1": + version: 7.25.1 + resolution: "@babel/plugin-transform-function-name@npm:7.25.1" dependencies: - "@babel/helper-compilation-targets": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-compilation-targets": ^7.24.8 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/traverse": ^7.25.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: cff3b876357999cb8ae30e439c3ec6b0491a53b0aa6f722920a4675a6dd5b53af97a833051df4b34791fe5b3dd326ccf769d5c8e45b322aa50ee11a660b17845 + checksum: 743f3ea03bbc5a90944849d5a880b6bd9243dddbde581a46952da76e53a0b74c1e2424133fe8129d7a152c1f8c872bcd27e0b6728d7caadabd1afa7bb892e1e0 languageName: node linkType: hard -"@babel/plugin-transform-json-strings@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-json-strings@npm:7.22.11" +"@babel/plugin-transform-json-strings@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-json-strings@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-json-strings": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 50665e5979e66358c50e90a26db53c55917f78175127ac2fa05c7888d156d418ffb930ec0a109353db0a7c5f57c756ce01bfc9825d24cbfd2b3ec453f2ed8cba + checksum: 88874d0b7a1ddea66c097fc0abb68801ffae194468aa44b828dde9a0e20ac5d8647943793de86092eabaa2911c96f67a6b373793d4bb9c932ef81b2711c06c2e languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-literals@npm:7.22.5" +"@babel/plugin-transform-literals@npm:^7.25.2": + version: 7.25.2 + resolution: "@babel/plugin-transform-literals@npm:7.25.2" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ec37cc2ffb32667af935ab32fe28f00920ec8a1eb999aa6dc6602f2bebd8ba205a558aeedcdccdebf334381d5c57106c61f52332045730393e73410892a9735b + checksum: 70c9bb40e377a306bd8f500899fb72127e527517914466e95dc6bb53fa7a0f51479db244a54a771b5780fc1eab488fedd706669bf11097b81a23c81ab7423eb1 languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.11" +"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c664e9798e85afa7f92f07b867682dee7392046181d82f5d21bae6f2ca26dfe9c8375cdc52b7483c3fc09a983c1989f60eff9fbc4f373b0c0a74090553d05739 + checksum: 3367ce0be243704dc6fce23e86a592c4380f01998ee5dd9f94c54b1ef7b971ac6f8a002901eb51599ac6cbdc0d067af8d1a720224fca1c40fde8bb8aab804aac languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" +"@babel/plugin-transform-member-expression-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ec4b0e07915ddd4fda0142fd104ee61015c208608a84cfa13643a95d18760b1dc1ceb6c6e0548898b8c49e5959a994e46367260176dbabc4467f729b21868504 + checksum: 2720c57aa3bf70576146ba7d6ea03227f4611852122d76d237924f7b008dafc952e6ae61a19e5024f26c665f44384bbd378466f01b6bd1305b3564a3b7fb1a5d languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.22.5": - version: 7.23.0 - resolution: "@babel/plugin-transform-modules-amd@npm:7.23.0" +"@babel/plugin-transform-modules-amd@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-amd@npm:7.24.7" dependencies: - "@babel/helper-module-transforms": ^7.23.0 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5d92875170a37b8282d4bcd805f55829b8fab0f9c8d08b53d32a7a0bfdc62b868e489b52d329ae768ecafc0c993eed0ad7a387baa673ac33211390a9f833ab5d + checksum: f1dd0fb2f46c0f8f21076b8c7ccd5b33a85ce6dcb31518ea4c648d9a5bb2474cd4bd87c9b1b752e68591e24b022e334ba0d07631fef2b6b4d8a4b85cf3d581f5 languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.22.15, @babel/plugin-transform-modules-commonjs@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.23.0" +"@babel/plugin-transform-modules-commonjs@npm:^7.24.7, @babel/plugin-transform-modules-commonjs@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.8" dependencies: - "@babel/helper-module-transforms": ^7.23.0 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-simple-access": ^7.22.5 + "@babel/helper-module-transforms": ^7.24.8 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-simple-access": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7fb25997194053e167c4207c319ff05362392da841bd9f42ddb3caf9c8798a5d203bd926d23ddf5830fdf05eddc82c2810f40d1287e3a4f80b07eff13d1024b5 + checksum: a4cf95b1639c33382064b44558f73ee5fac023f2a94d16e549d2bb55ceebd5cbc10fcddd505d08cd5bc97f5a64af9fd155512358b7dcf7b1a0082e8945cf21c5 languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.22.11": - version: 7.23.0 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.0" +"@babel/plugin-transform-modules-systemjs@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.25.0" dependencies: - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-module-transforms": ^7.23.0 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.20 + "@babel/helper-module-transforms": ^7.25.0 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-validator-identifier": ^7.24.7 + "@babel/traverse": ^7.25.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2d481458b22605046badea2317d5cc5c94ac3031c2293e34c96f02063f5b02af0979c4da6a8fbc67cc249541575dc9c6d710db6b919ede70b7337a22d9fd57a7 + checksum: fe673bec08564e491847324bb80a1e6edfb229f5c37e58a094d51e95306e7b098e1d130fc43e992d22debd93b9beac74441ffc3f6ea5d78f6b2535896efa0728 languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" +"@babel/plugin-transform-modules-umd@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-umd@npm:7.24.7" dependencies: - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 46622834c54c551b231963b867adbc80854881b3e516ff29984a8da989bd81665bd70e8cba6710345248e97166689310f544aee1a5773e262845a8f1b3e5b8b4 + checksum: 9ff1c464892efe042952ba778468bda6131b196a2729615bdcc3f24cdc94014f016a4616ee5643c5845bade6ba698f386833e61056d7201314b13a7fd69fac88 languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.7" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0 - checksum: 3ee564ddee620c035b928fdc942c5d17e9c4b98329b76f9cefac65c111135d925eb94ed324064cd7556d4f5123beec79abea1d4b97d1c8a2a5c748887a2eb623 + checksum: f1c6c7b5d60a86b6d7e4dd098798e1d393d55e993a0b57a73b53640c7a94985b601a96bdacee063f809a9a700bcea3a2ff18e98fa561554484ac56b761d774bd languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-new-target@npm:7.22.5" +"@babel/plugin-transform-new-target@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-new-target@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6b72112773487a881a1d6ffa680afde08bad699252020e86122180ee7a88854d5da3f15d9bca3331cf2e025df045604494a8208a2e63b486266b07c14e2ffbf3 + checksum: 3cb94cd1076b270f768f91fdcf9dd2f6d487f8dbfff3df7ca8d07b915900b86d02769a35ba1407d16fe49499012c8f055e1741299e2c880798b953d942a8fa1b languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.11" +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 167babecc8b8fe70796a7b7d34af667ebbf43da166c21689502e5e8cc93180b7a85979c77c9f64b7cce431b36718bd0a6df9e5e0ffea4ae22afb22cfef886372 + checksum: 4a9221356401d87762afbc37a9e8e764afc2daf09c421117537820f8cfbed6876888372ad3a7bcfae2d45c95f026651f050ab4020b777be31d3ffb00908dbdd3 languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.11" +"@babel/plugin-transform-numeric-separator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-numeric-separator": ^7.10.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: af064d06a4a041767ec396a5f258103f64785df290e038bba9f0ef454e6c914f2ac45d862bbdad8fac2c7ad47fa4e95356f29053c60c100a0160b02a995fe2a3 + checksum: 561b5f1d08b2c3f92ce849f092751558b5e6cfeb7eb55c79e7375c34dd9c3066dce5e630bb439affef6adcf202b6cbcaaa23870070276fa5bb429c8f5b8c7514 languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.15" +"@babel/plugin-transform-object-rest-spread@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.7" dependencies: - "@babel/compat-data": ^7.22.9 - "@babel/helper-compilation-targets": ^7.22.15 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-compilation-targets": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-transform-parameters": ^7.22.15 + "@babel/plugin-transform-parameters": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 62197a6f12289c1c1bd57f3bed9f0f765ca32390bfe91e0b5561dd94dd9770f4480c4162dec98da094bc0ba99d2c2ebba68de47c019454041b0b7a68ba2ec66d + checksum: 169d257b9800c13e1feb4c37fb05dae84f702e58b342bb76e19e82e6692b7b5337c9923ee89e3916a97c0dd04a3375bdeca14f5e126f110bbacbeb46d1886ca2 languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-object-super@npm:7.22.5" +"@babel/plugin-transform-object-super@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-object-super@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-replace-supers": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-replace-supers": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b71887877d74cb64dbccb5c0324fa67e31171e6a5311991f626650e44a4083e5436a1eaa89da78c0474fb095d4ec322d63ee778b202d33aa2e4194e1ed8e62d7 + checksum: f71e607a830ee50a22fa1a2686524d3339440cf9dea63032f6efbd865cfe4e35000e1e3f3492459e5c986f7c0c07dc36938bf3ce61fc9ba5f8ab732d0b64ab37 languageName: node linkType: hard -"@babel/plugin-transform-optional-catch-binding@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.11" +"@babel/plugin-transform-optional-catch-binding@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f17abd90e1de67c84d63afea29c8021c74abb2794d3a6eeafb0bbe7372d3db32aefca386e392116ec63884537a4a2815d090d26264d259bacc08f6e3ed05294c + checksum: 7229f3a5a4facaab40f4fdfc7faabc157dc38a67d66bed7936599f4bc509e0bff636f847ac2aa45294881fce9cf8a0a460b85d2a465b7b977de9739fce9b18f6 languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.15": - version: 7.23.0 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.0" +"@babel/plugin-transform-optional-chaining@npm:^7.24.7, @babel/plugin-transform-optional-chaining@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.8" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f702634f2b97e5260dbec0d4bde05ccb6f4d96d7bfa946481aeacfa205ca846cb6e096a38312f9d51fdbdac1f258f211138c5f7075952e46a5bf8574de6a1329 + checksum: 45e55e3a2fffb89002d3f89aef59c141610f23b60eee41e047380bffc40290b59f64fc649aa7ec5281f73d41b2065410d788acc6afaad2a9f44cad6e8af04442 languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-parameters@npm:7.22.15" +"@babel/plugin-transform-parameters@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-parameters@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 541188bb7d1876cad87687b5c7daf90f63d8208ae83df24acb1e2b05020ad1c78786b2723ca4054a83fcb74fb6509f30c4cacc5b538ee684224261ad5fb047c1 + checksum: ab534b03ac2eff94bc79342b8f39a4584666f5305a6c63c1964afda0b1b004e6b861e49d1683548030defe248e3590d3ff6338ee0552cb90c064f7e1479968c3 languageName: node linkType: hard -"@babel/plugin-transform-private-methods@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-private-methods@npm:7.22.5" +"@babel/plugin-transform-private-methods@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/plugin-transform-private-methods@npm:7.25.4" dependencies: - "@babel/helper-create-class-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.25.4 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 321479b4fcb6d3b3ef622ab22fd24001e43d46e680e8e41324c033d5810c84646e470f81b44cbcbef5c22e99030784f7cac92f1829974da7a47a60a7139082c3 + checksum: cb1dabfc03e2977990263d65bc8f43a9037dffbb5d9a5f825c00d05447ff68015099408c1531d9dd88f18a41a90f5062dc48f3a1d52b415d2d2ee4827dedff09 languageName: node linkType: hard -"@babel/plugin-transform-private-property-in-object@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.11" +"@babel/plugin-transform-private-property-in-object@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.7" dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-create-class-features-plugin": ^7.22.11 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-create-class-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4d029d84901e53c46dead7a46e2990a7bc62470f4e4ca58a0d063394f86652fd58fe4eea1eb941da3669cd536b559b9d058b342b59300026346b7a2a51badac8 + checksum: 8cee9473095305cc787bb653fd681719b49363281feabf677db8a552e8e41c94441408055d7e5fd5c7d41b315e634fa70b145ad0c7c54456216049df4ed57350 languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" +"@babel/plugin-transform-property-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-property-literals@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 796176a3176106f77fcb8cd04eb34a8475ce82d6d03a88db089531b8f0453a2fb8b0c6ec9a52c27948bc0ea478becec449893741fc546dfc3930ab927e3f9f2e + checksum: 9aeefc3aab6c6bf9d1fae1cf3a2d38c7d886fd3c6c81b7c608c477f5758aee2e7abf52f32724310fe861da61af934ee2508b78a5b5f234b9740c9134e1c14437 languageName: node linkType: hard -"@babel/plugin-transform-react-constant-elements@npm:^7.18.12": - version: 7.20.2 - resolution: "@babel/plugin-transform-react-constant-elements@npm:7.20.2" +"@babel/plugin-transform-react-constant-elements@npm:^7.21.3": + version: 7.25.1 + resolution: "@babel/plugin-transform-react-constant-elements@npm:7.25.1" dependencies: - "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7b041b726e7c14b8c26a0dd240defac5f93a1f449371c6bdc5e6b46d581211300cc1a79da4140bdf20347f49e175dcb4f469812399206864024d1fdc81171193 + checksum: 6126abf8bc3980c1e27fd217f8b2f226b20cce9be300eaf9d30548556dd1e906b7daa4580d9ae1dae35eb5ed5c98e7222e0cb91efb0a232d05aae5875dcfe55c languageName: node linkType: hard @@ -1286,26 +1314,26 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.22.10": - version: 7.22.10 - resolution: "@babel/plugin-transform-regenerator@npm:7.22.10" +"@babel/plugin-transform-regenerator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-regenerator@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 regenerator-transform: ^0.15.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e13678d62d6fa96f11cb8b863f00e8693491e7adc88bfca3f2820f80cbac8336e7dec3a596eee6a1c4663b7ececc3564f2cd7fb44ed6d4ce84ac2bb7f39ecc6e + checksum: 20c6c3fb6fc9f407829087316653388d311e8c1816b007609bb09aeef254092a7157adace8b3aaa8f34be752503717cb85c88a5fe482180a9b11bcbd676063be languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" +"@babel/plugin-transform-reserved-words@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-reserved-words@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3ffd7dbc425fe8132bfec118b9817572799cab1473113a635d25ab606c1f5a2341a636c04cf6b22df3813320365ed5a965b5eeb3192320a10e4cc2c137bd8bfc + checksum: 3d5876954d5914d7270819479504f30c4bf5452a65c677f44e2dab2db50b3c9d4b47793c45dfad7abf4f377035dd79e4b3f554ae350df9f422201d370ce9f8dd languageName: node linkType: hard @@ -1325,141 +1353,145 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" +"@babel/plugin-transform-shorthand-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: a5ac902c56ea8effa99f681340ee61bac21094588f7aef0bc01dff98246651702e677552fa6d10e548c4ac22a3ffad047dd2f8c8f0540b68316c2c203e56818b + checksum: 7b524245814607188212b8eb86d8c850e5974203328455a30881b4a92c364b93353fae14bc2af5b614ef16300b75b8c1d3b8f3a08355985b4794a7feb240adc3 languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-spread@npm:7.22.5" +"@babel/plugin-transform-spread@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-spread@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5587f0deb60b3dfc9b274e269031cc45ec75facccf1933ea2ea71ced9fd3ce98ed91bb36d6cd26817c14474b90ed998c5078415f0eab531caf301496ce24c95c + checksum: 4c4254c8b9cceb1a8f975fa9b92257ddb08380a35c0a3721b8f4b9e13a3d82e403af2e0fba577b9f2452dd8f06bc3dea71cc53b1e2c6af595af5db52a13429d6 languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" +"@babel/plugin-transform-sticky-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 63b2c575e3e7f96c32d52ed45ee098fb7d354b35c2223b8c8e76840b32cc529ee0c0ceb5742fd082e56e91e3d82842a367ce177e82b05039af3d602c9627a729 + checksum: 118fc7a7ebf7c20411b670c8a030535fdfe4a88bc5643bb625a584dbc4c8a468da46430a20e6bf78914246962b0f18f1b9d6a62561a7762c4f34a038a5a77179 languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" +"@babel/plugin-transform-template-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-template-literals@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 27e9bb030654cb425381c69754be4abe6a7c75b45cd7f962cd8d604b841b2f0fb7b024f2efc1c25cc53f5b16d79d5e8cfc47cacbdaa983895b3aeefa3e7e24ff + checksum: ad44e5826f5a98c1575832dbdbd033adfe683cdff195e178528ead62507564bf02f479b282976cfd3caebad8b06d5fd7349c1cdb880dec3c56daea4f1f179619 languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" +"@babel/plugin-transform-typeof-symbol@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.8" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 82a53a63ffc3010b689ca9a54e5f53b2718b9f4b4a9818f36f9b7dba234f38a01876680553d2716a645a61920b5e6e4aaf8d4a0064add379b27ca0b403049512 + checksum: 8663a8e7347cedf181001d99c88cf794b6598c3d82f324098510fe8fb8bd22113995526a77aa35a3cc5d70ffd0617a59dd0d10311a9bf0e1a3a7d3e59b900c00 languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/plugin-transform-typescript@npm:7.22.15" +"@babel/plugin-transform-typescript@npm:^7.24.7": + version: 7.25.2 + resolution: "@babel/plugin-transform-typescript@npm:7.25.2" dependencies: - "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-create-class-features-plugin": ^7.22.15 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/plugin-syntax-typescript": ^7.22.5 + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-create-class-features-plugin": ^7.25.0 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + "@babel/plugin-syntax-typescript": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c5d96cdbf0e1512707aa1c1e3ac6b370a25fd9c545d26008ce44eb13a47bd7fd67a1eb799c98b5ccc82e33a345fda55c0055e1fe3ed97646ed405dd13020b226 + checksum: b0267128d93560a4350919f7230a3b497e20fb8611d9f04bb3560d6b38877305ccad4c40903160263361c6930a84dbcb5b21b8ea923531bda51f67bffdc2dd0b languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.22.10": - version: 7.22.10 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.10" +"@babel/plugin-transform-unicode-escapes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 807f40ed1324c8cb107c45358f1903384ca3f0ef1d01c5a3c5c9b271c8d8eec66936a3dcc8d75ddfceea9421420368c2e77ae3adef0a50557e778dfe296bf382 + checksum: 4af0a193e1ddea6ff82b2b15cc2501b872728050bd625740b813c8062fec917d32d530ff6b41de56c15e7296becdf3336a58db81f5ca8e7c445c1306c52f3e01 languageName: node linkType: hard -"@babel/plugin-transform-unicode-property-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.5" +"@babel/plugin-transform-unicode-property-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.7" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2495e5f663cb388e3d888b4ba3df419ac436a5012144ac170b622ddfc221f9ea9bdba839fa2bc0185cb776b578030666406452ec7791cbf0e7a3d4c88ae9574c + checksum: aae13350c50973f5802ca7906d022a6a0cc0e3aebac9122d0450bbd51e78252d4c2032ad69385e2759fcbdd3aac5d571bd7e26258907f51f8e1a51b53be626c2 languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" +"@babel/plugin-transform-unicode-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.7" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6b5d1404c8c623b0ec9bd436c00d885a17d6a34f3f2597996343ddb9d94f6379705b21582dfd4cec2c47fd34068872e74ab6b9580116c0566b3f9447e2a7fa06 + checksum: 1cb4e70678906e431da0a05ac3f8350025fee290304ad7482d9cfaa1ca67b2e898654de537c9268efbdad5b80d3ebadf42b4a88ea84609bd8a4cce7b11b48afd languageName: node linkType: hard -"@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" +"@babel/plugin-transform-unicode-sets-regex@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.25.4" dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-create-regexp-features-plugin": ^7.25.2 + "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: "@babel/core": ^7.0.0 - checksum: c042070f980b139547f8b0179efbc049ac5930abec7fc26ed7a41d89a048d8ab17d362200e204b6f71c3c20d6991a0e74415e1a412a49adc8131c2a40c04822e + checksum: 6d1a7e9fdde4ffc9a81c0e3f261b96a9a0dfe65da282ec96fe63b36c597a7389feac638f1df2a8a4f8c9128337bba8e984f934e9f19077930f33abf1926759ea languageName: node linkType: hard -"@babel/preset-env@npm:^7.19.4, @babel/preset-env@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/preset-env@npm:7.22.20" +"@babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:^7.22.9": + version: 7.25.4 + resolution: "@babel/preset-env@npm:7.25.4" dependencies: - "@babel/compat-data": ^7.22.20 - "@babel/helper-compilation-targets": ^7.22.15 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-validator-option": ^7.22.15 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.22.15 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.22.15 + "@babel/compat-data": ^7.25.4 + "@babel/helper-compilation-targets": ^7.25.2 + "@babel/helper-plugin-utils": ^7.24.8 + "@babel/helper-validator-option": ^7.24.8 + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ^7.25.3 + "@babel/plugin-bugfix-safari-class-field-initializer-scope": ^7.25.0 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.25.0 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.24.7 + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ^7.25.0 "@babel/plugin-proposal-private-property-in-object": 7.21.0-placeholder-for-preset-env.2 "@babel/plugin-syntax-async-generators": ^7.8.4 "@babel/plugin-syntax-class-properties": ^7.12.13 "@babel/plugin-syntax-class-static-block": ^7.14.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.22.5 - "@babel/plugin-syntax-import-attributes": ^7.22.5 + "@babel/plugin-syntax-import-assertions": ^7.24.7 + "@babel/plugin-syntax-import-attributes": ^7.24.7 "@babel/plugin-syntax-import-meta": ^7.10.4 "@babel/plugin-syntax-json-strings": ^7.8.3 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 @@ -1471,64 +1503,64 @@ __metadata: "@babel/plugin-syntax-private-property-in-object": ^7.14.5 "@babel/plugin-syntax-top-level-await": ^7.14.5 "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 - "@babel/plugin-transform-arrow-functions": ^7.22.5 - "@babel/plugin-transform-async-generator-functions": ^7.22.15 - "@babel/plugin-transform-async-to-generator": ^7.22.5 - "@babel/plugin-transform-block-scoped-functions": ^7.22.5 - "@babel/plugin-transform-block-scoping": ^7.22.15 - "@babel/plugin-transform-class-properties": ^7.22.5 - "@babel/plugin-transform-class-static-block": ^7.22.11 - "@babel/plugin-transform-classes": ^7.22.15 - "@babel/plugin-transform-computed-properties": ^7.22.5 - "@babel/plugin-transform-destructuring": ^7.22.15 - "@babel/plugin-transform-dotall-regex": ^7.22.5 - "@babel/plugin-transform-duplicate-keys": ^7.22.5 - "@babel/plugin-transform-dynamic-import": ^7.22.11 - "@babel/plugin-transform-exponentiation-operator": ^7.22.5 - "@babel/plugin-transform-export-namespace-from": ^7.22.11 - "@babel/plugin-transform-for-of": ^7.22.15 - "@babel/plugin-transform-function-name": ^7.22.5 - "@babel/plugin-transform-json-strings": ^7.22.11 - "@babel/plugin-transform-literals": ^7.22.5 - "@babel/plugin-transform-logical-assignment-operators": ^7.22.11 - "@babel/plugin-transform-member-expression-literals": ^7.22.5 - "@babel/plugin-transform-modules-amd": ^7.22.5 - "@babel/plugin-transform-modules-commonjs": ^7.22.15 - "@babel/plugin-transform-modules-systemjs": ^7.22.11 - "@babel/plugin-transform-modules-umd": ^7.22.5 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.22.5 - "@babel/plugin-transform-new-target": ^7.22.5 - "@babel/plugin-transform-nullish-coalescing-operator": ^7.22.11 - "@babel/plugin-transform-numeric-separator": ^7.22.11 - "@babel/plugin-transform-object-rest-spread": ^7.22.15 - "@babel/plugin-transform-object-super": ^7.22.5 - "@babel/plugin-transform-optional-catch-binding": ^7.22.11 - "@babel/plugin-transform-optional-chaining": ^7.22.15 - "@babel/plugin-transform-parameters": ^7.22.15 - "@babel/plugin-transform-private-methods": ^7.22.5 - "@babel/plugin-transform-private-property-in-object": ^7.22.11 - "@babel/plugin-transform-property-literals": ^7.22.5 - "@babel/plugin-transform-regenerator": ^7.22.10 - "@babel/plugin-transform-reserved-words": ^7.22.5 - "@babel/plugin-transform-shorthand-properties": ^7.22.5 - "@babel/plugin-transform-spread": ^7.22.5 - "@babel/plugin-transform-sticky-regex": ^7.22.5 - "@babel/plugin-transform-template-literals": ^7.22.5 - "@babel/plugin-transform-typeof-symbol": ^7.22.5 - "@babel/plugin-transform-unicode-escapes": ^7.22.10 - "@babel/plugin-transform-unicode-property-regex": ^7.22.5 - "@babel/plugin-transform-unicode-regex": ^7.22.5 - "@babel/plugin-transform-unicode-sets-regex": ^7.22.5 + "@babel/plugin-transform-arrow-functions": ^7.24.7 + "@babel/plugin-transform-async-generator-functions": ^7.25.4 + "@babel/plugin-transform-async-to-generator": ^7.24.7 + "@babel/plugin-transform-block-scoped-functions": ^7.24.7 + "@babel/plugin-transform-block-scoping": ^7.25.0 + "@babel/plugin-transform-class-properties": ^7.25.4 + "@babel/plugin-transform-class-static-block": ^7.24.7 + "@babel/plugin-transform-classes": ^7.25.4 + "@babel/plugin-transform-computed-properties": ^7.24.7 + "@babel/plugin-transform-destructuring": ^7.24.8 + "@babel/plugin-transform-dotall-regex": ^7.24.7 + "@babel/plugin-transform-duplicate-keys": ^7.24.7 + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ^7.25.0 + "@babel/plugin-transform-dynamic-import": ^7.24.7 + "@babel/plugin-transform-exponentiation-operator": ^7.24.7 + "@babel/plugin-transform-export-namespace-from": ^7.24.7 + "@babel/plugin-transform-for-of": ^7.24.7 + "@babel/plugin-transform-function-name": ^7.25.1 + "@babel/plugin-transform-json-strings": ^7.24.7 + "@babel/plugin-transform-literals": ^7.25.2 + "@babel/plugin-transform-logical-assignment-operators": ^7.24.7 + "@babel/plugin-transform-member-expression-literals": ^7.24.7 + "@babel/plugin-transform-modules-amd": ^7.24.7 + "@babel/plugin-transform-modules-commonjs": ^7.24.8 + "@babel/plugin-transform-modules-systemjs": ^7.25.0 + "@babel/plugin-transform-modules-umd": ^7.24.7 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.24.7 + "@babel/plugin-transform-new-target": ^7.24.7 + "@babel/plugin-transform-nullish-coalescing-operator": ^7.24.7 + "@babel/plugin-transform-numeric-separator": ^7.24.7 + "@babel/plugin-transform-object-rest-spread": ^7.24.7 + "@babel/plugin-transform-object-super": ^7.24.7 + "@babel/plugin-transform-optional-catch-binding": ^7.24.7 + "@babel/plugin-transform-optional-chaining": ^7.24.8 + "@babel/plugin-transform-parameters": ^7.24.7 + "@babel/plugin-transform-private-methods": ^7.25.4 + "@babel/plugin-transform-private-property-in-object": ^7.24.7 + "@babel/plugin-transform-property-literals": ^7.24.7 + "@babel/plugin-transform-regenerator": ^7.24.7 + "@babel/plugin-transform-reserved-words": ^7.24.7 + "@babel/plugin-transform-shorthand-properties": ^7.24.7 + "@babel/plugin-transform-spread": ^7.24.7 + "@babel/plugin-transform-sticky-regex": ^7.24.7 + "@babel/plugin-transform-template-literals": ^7.24.7 + "@babel/plugin-transform-typeof-symbol": ^7.24.8 + "@babel/plugin-transform-unicode-escapes": ^7.24.7 + "@babel/plugin-transform-unicode-property-regex": ^7.24.7 + "@babel/plugin-transform-unicode-regex": ^7.24.7 + "@babel/plugin-transform-unicode-sets-regex": ^7.25.4 "@babel/preset-modules": 0.1.6-no-external-plugins - "@babel/types": ^7.22.19 - babel-plugin-polyfill-corejs2: ^0.4.5 - babel-plugin-polyfill-corejs3: ^0.8.3 - babel-plugin-polyfill-regenerator: ^0.5.2 - core-js-compat: ^3.31.0 + babel-plugin-polyfill-corejs2: ^0.4.10 + babel-plugin-polyfill-corejs3: ^0.10.6 + babel-plugin-polyfill-regenerator: ^0.6.1 + core-js-compat: ^3.37.1 semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 99357a5cb30f53bacdc0d1cd6dff0f052ea6c2d1ba874d969bba69897ef716e87283e84a59dc52fb49aa31fd1b6f55ed756c64c04f5678380700239f6030b881 + checksum: 752be43f0b78a2eefe5007076aed3d21b505e1c09d134b61e7de8838f1bbb1e7af81023d39adb14b6eae23727fb5a9fd23f8115a44df043319be22319be17913 languageName: node linkType: hard @@ -1561,18 +1593,18 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.18.6, @babel/preset-typescript@npm:^7.22.5": - version: 7.23.0 - resolution: "@babel/preset-typescript@npm:7.23.0" +"@babel/preset-typescript@npm:^7.21.0, @babel/preset-typescript@npm:^7.22.5": + version: 7.24.7 + resolution: "@babel/preset-typescript@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-validator-option": ^7.22.15 - "@babel/plugin-syntax-jsx": ^7.22.5 - "@babel/plugin-transform-modules-commonjs": ^7.23.0 - "@babel/plugin-transform-typescript": ^7.22.15 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-validator-option": ^7.24.7 + "@babel/plugin-syntax-jsx": ^7.24.7 + "@babel/plugin-transform-modules-commonjs": ^7.24.7 + "@babel/plugin-transform-typescript": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3d5fce83e83f11c07e0ea13542bca181abb3b482b8981ec9c64e6add9d7beed3c54d063dc4bc9fd383165c71114a245abef89a289680833c5a8552fe3e7c4407 + checksum: 12929b24757f3bd6548103475f86478eda4c872bc7cefd920b29591eee8f4a4f350561d888e133d632d0c9402b8615fdcec9138e5127a6567dcb22f804ff207f languageName: node linkType: hard @@ -1602,43 +1634,40 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.22.15, @babel/template@npm:^7.22.5, @babel/template@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/template@npm:7.23.9" +"@babel/template@npm:^7.24.7, @babel/template@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/template@npm:7.25.0" dependencies: - "@babel/code-frame": ^7.23.5 - "@babel/parser": ^7.23.9 - "@babel/types": ^7.23.9 - checksum: 6e67414c0f7125d7ecaf20c11fab88085fa98a96c3ef10da0a61e962e04fdf3a18a496a66047005ddd1bb682a7cc7842d556d1db2f3f3f6ccfca97d5e445d342 + "@babel/code-frame": ^7.24.7 + "@babel/parser": ^7.25.0 + "@babel/types": ^7.25.0 + checksum: 3f2db568718756d0daf2a16927b78f00c425046b654cd30b450006f2e84bdccaf0cbe6dc04994aa1f5f6a4398da2f11f3640a4d3ee31722e43539c4c919c817b languageName: node linkType: hard -"@babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/traverse@npm:7.23.9" +"@babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.24.8, @babel/traverse@npm:^7.25.0, @babel/traverse@npm:^7.25.1, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.25.3, @babel/traverse@npm:^7.25.4": + version: 7.25.4 + resolution: "@babel/traverse@npm:7.25.4" dependencies: - "@babel/code-frame": ^7.23.5 - "@babel/generator": ^7.23.6 - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-function-name": ^7.23.0 - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.23.9 - "@babel/types": ^7.23.9 + "@babel/code-frame": ^7.24.7 + "@babel/generator": ^7.25.4 + "@babel/parser": ^7.25.4 + "@babel/template": ^7.25.0 + "@babel/types": ^7.25.4 debug: ^4.3.1 globals: ^11.1.0 - checksum: a932f7aa850e158c00c97aad22f639d48c72805c687290f6a73e30c5c4957c07f5d28310c9bf59648e2980fe6c9d16adeb2ff92a9ca0f97fa75739c1328fc6c3 + checksum: 3b6d879b9d843b119501585269b3599f047011ae21eb7820d00aef62fc3a2bcdaf6f4cdf2679795a2d7c0b6b5d218974916e422f08dea08613dc42188ef21e4b languageName: node linkType: hard -"@babel/types@npm:^7.20.0, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.23.9 - resolution: "@babel/types@npm:7.23.9" +"@babel/types@npm:^7.21.3, @babel/types@npm:^7.22.15, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2, @babel/types@npm:^7.25.4, @babel/types@npm:^7.4.4": + version: 7.25.4 + resolution: "@babel/types@npm:7.25.4" dependencies: - "@babel/helper-string-parser": ^7.23.4 - "@babel/helper-validator-identifier": ^7.22.20 + "@babel/helper-string-parser": ^7.24.8 + "@babel/helper-validator-identifier": ^7.24.7 to-fast-properties: ^2.0.0 - checksum: 0a9b008e9bfc89beb8c185e620fa0f8ed6c771f1e1b2e01e1596870969096fec7793898a1d64a035176abf1dd13e2668ee30bf699f2d92c210a8128f4b151e65 + checksum: 497f8b583c54a92a59c3ec542144695064cd5c384fcca46ba1aa301d5e5dd6c1d011f312ca024cb0f9c956da07ae82fb4c348c31a30afa31a074c027720d2aa8 languageName: node linkType: hard @@ -1689,9 +1718,9 @@ __metadata: languageName: node linkType: hard -"@docusaurus/core@npm:3.2.1, @docusaurus/core@npm:^3.1.1": - version: 3.2.1 - resolution: "@docusaurus/core@npm:3.2.1" +"@docusaurus/core@npm:3.5.2, @docusaurus/core@npm:^3.1.1": + version: 3.5.2 + resolution: "@docusaurus/core@npm:3.5.2" dependencies: "@babel/core": ^7.23.3 "@babel/generator": ^7.23.3 @@ -1703,14 +1732,12 @@ __metadata: "@babel/runtime": ^7.22.6 "@babel/runtime-corejs3": ^7.22.6 "@babel/traverse": ^7.22.8 - "@docusaurus/cssnano-preset": 3.2.1 - "@docusaurus/logger": 3.2.1 - "@docusaurus/mdx-loader": 3.2.1 - "@docusaurus/react-loadable": 5.5.2 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 - "@svgr/webpack": ^6.5.1 + "@docusaurus/cssnano-preset": 3.5.2 + "@docusaurus/logger": 3.5.2 + "@docusaurus/mdx-loader": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 autoprefixer: ^10.4.14 babel-loader: ^9.1.3 babel-plugin-dynamic-import-node: ^2.3.3 @@ -1724,8 +1751,8 @@ __metadata: copy-webpack-plugin: ^11.0.0 core-js: ^3.31.1 css-loader: ^6.8.1 - css-minimizer-webpack-plugin: ^4.2.2 - cssnano: ^5.1.15 + css-minimizer-webpack-plugin: ^5.0.1 + cssnano: ^6.1.2 del: ^6.1.1 detect-port: ^1.5.1 escape-html: ^1.0.3 @@ -1745,7 +1772,7 @@ __metadata: prompts: ^2.4.2 react-dev-utils: ^12.0.1 react-helmet-async: ^1.3.0 - react-loadable: "npm:@docusaurus/react-loadable@5.5.2" + react-loadable: "npm:@docusaurus/react-loadable@6.0.0" react-loadable-ssr-addon-v5-slorber: ^1.0.1 react-router: ^5.3.4 react-router-config: ^5.1.1 @@ -1764,43 +1791,44 @@ __metadata: webpack-merge: ^5.9.0 webpackbar: ^5.0.2 peerDependencies: + "@mdx-js/react": ^3.0.0 react: ^18.0.0 react-dom: ^18.0.0 bin: docusaurus: bin/docusaurus.mjs - checksum: 9267f08b41240cb9d399abbd8a41ff66e0082551284325db3f17fcce9643bef81d06564797a7cc4c528fe8bde2858c20666e74a0308f3ecc80f3be1dbee14bb5 + checksum: 6c6282a75931f0f8f8f8768232b4436ff8679ae12b619f7bd01e0d83aa346e24ab0d9cecac034f9dc95c55059997efdd963d052d3e429583bfb8d3b54ab750d3 languageName: node linkType: hard -"@docusaurus/cssnano-preset@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/cssnano-preset@npm:3.2.1" +"@docusaurus/cssnano-preset@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/cssnano-preset@npm:3.5.2" dependencies: - cssnano-preset-advanced: ^5.3.10 - postcss: ^8.4.26 - postcss-sort-media-queries: ^4.4.1 + cssnano-preset-advanced: ^6.1.2 + postcss: ^8.4.38 + postcss-sort-media-queries: ^5.2.0 tslib: ^2.6.0 - checksum: ee23a1229d23732d936fe1d68732d1305abf0132b43a398336fee500504a3e7566d3b0c6222f89f565e24e68e00e353765e0cbbab5611a3b35ecf88305558b6d + checksum: 4bb1fae3741e14cbbdb64c1b0707435970838bf219831234a70cf382e6811ffac1cadf733d5e1fe7c278e7b2a9e533bfa802a5212b22ec46edd703208cf49f92 languageName: node linkType: hard -"@docusaurus/logger@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/logger@npm:3.2.1" +"@docusaurus/logger@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/logger@npm:3.5.2" dependencies: chalk: ^4.1.2 tslib: ^2.6.0 - checksum: 9d5db5253eda98871563faddb5318bcb6b17ddf5882ababad4803d526917844819751e84ee8028e794fd5507646db6409f9041fd7f41b7f7971015df11cc6376 + checksum: 7cbdcf54acd6e7787ca5a10b9c884be4b9e8fdae837862c66550a0bf3d02737f72c3188b2bddd61da6d8530eb2eb2b646ea599a79416e33c4998f1a87d2f6a8c languageName: node linkType: hard -"@docusaurus/mdx-loader@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/mdx-loader@npm:3.2.1" +"@docusaurus/mdx-loader@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/mdx-loader@npm:3.5.2" dependencies: - "@docusaurus/logger": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/logger": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 "@mdx-js/mdx": ^3.0.0 "@slorber/remark-comment": ^1.0.0 escape-html: ^1.0.3 @@ -1825,38 +1853,37 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 4609faf2d8b76085a3aa86ac5ca4ac3b3420e3cfd796f1b39c46f368c82b3db0db5b1308646cf35fdad0a1f6f088d367116eb0e2a8c3fa728ed886ee37516476 + checksum: 36186c2f3487631757b24ba3a21575d2253ca1e6ada82d556bf323da7ae7637c0880eb388bf375e207bc5f26dcd8b58cc76d763e6c2caf6ed80f88748444ce8d languageName: node linkType: hard -"@docusaurus/module-type-aliases@npm:3.2.1, @docusaurus/module-type-aliases@npm:^3.1.1": - version: 3.2.1 - resolution: "@docusaurus/module-type-aliases@npm:3.2.1" +"@docusaurus/module-type-aliases@npm:3.5.2, @docusaurus/module-type-aliases@npm:^3.1.1": + version: 3.5.2 + resolution: "@docusaurus/module-type-aliases@npm:3.5.2" dependencies: - "@docusaurus/react-loadable": 5.5.2 - "@docusaurus/types": 3.2.1 + "@docusaurus/types": 3.5.2 "@types/history": ^4.7.11 "@types/react": "*" "@types/react-router-config": "*" "@types/react-router-dom": "*" react-helmet-async: "*" - react-loadable: "npm:@docusaurus/react-loadable@5.5.2" + react-loadable: "npm:@docusaurus/react-loadable@6.0.0" peerDependencies: react: "*" react-dom: "*" - checksum: 37b4a40f9afebbe76e350c10c857737b544c141a988462436904ae16993a52e4429018d406e2f55ad57a533e5a108dd7cdb903434abb84721deeec0d5f195d80 + checksum: 0161db859d459bb25ac162f0c509fb1316dfb403a9e89f325a9bc7d9f35ae1825b9703a435777903ba93de827d4413b189bbd0c03018ac13d66b50633302ea80 languageName: node linkType: hard "@docusaurus/plugin-client-redirects@npm:^3.1.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-client-redirects@npm:3.2.1" + version: 3.5.2 + resolution: "@docusaurus/plugin-client-redirects@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/logger": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/logger": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 eta: ^2.2.0 fs-extra: ^11.1.1 lodash: ^4.17.21 @@ -1864,22 +1891,23 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 84f031a9f660028cf745ad4f3b8ea8820e34fdabed75321b06a8145078b3b8c496369c902dc3f46a2bb973cfaaa433474a9891bd836494ff72c65258fa67b64c + checksum: 040aa829e819a43cb8d30f6c776c27a303080a4cb2fc2c398266fe9ecbd006dc53c6dd862ebcf4f74d0547042c4df6c4bbf080b3702be6df18c95b95c3e38f1d languageName: node linkType: hard -"@docusaurus/plugin-content-blog@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-content-blog@npm:3.2.1" +"@docusaurus/plugin-content-blog@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-content-blog@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/logger": 3.2.1 - "@docusaurus/mdx-loader": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 - cheerio: ^1.0.0-rc.12 + "@docusaurus/core": 3.5.2 + "@docusaurus/logger": 3.5.2 + "@docusaurus/mdx-loader": 3.5.2 + "@docusaurus/theme-common": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 + cheerio: 1.0.0-rc.12 feed: ^4.2.2 fs-extra: ^11.1.1 lodash: ^4.17.21 @@ -1890,24 +1918,26 @@ __metadata: utility-types: ^3.10.0 webpack: ^5.88.1 peerDependencies: + "@docusaurus/plugin-content-docs": "*" react: ^18.0.0 react-dom: ^18.0.0 - checksum: d95147a28aad832cd2dc39af634e1902a8a36f958dd2ff5fa6eaa47b574b58df42609a64da823951826f647337ad35c1f1c8be8a0a085913e192936f38839413 + checksum: c5997b9d86ccf939998f9d56e65491ecf9e677d8425e95a79b3b428041d4dfc4ecb03a18ef595777c3ad5bd65f4a2dd30d99cb6f1b411161bf7cd32027ecc6d5 languageName: node linkType: hard -"@docusaurus/plugin-content-docs@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-content-docs@npm:3.2.1" +"@docusaurus/plugin-content-docs@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-content-docs@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/logger": 3.2.1 - "@docusaurus/mdx-loader": 3.2.1 - "@docusaurus/module-type-aliases": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/logger": 3.5.2 + "@docusaurus/mdx-loader": 3.5.2 + "@docusaurus/module-type-aliases": 3.5.2 + "@docusaurus/theme-common": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 "@types/react-router-config": ^5.0.7 combine-promises: ^1.1.0 fs-extra: ^11.1.1 @@ -1919,168 +1949,156 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: c182466c3ff513b36a8975a3899b07ffc4b227ab45ef69eacc0a77119d6f0cd6a0727a3e886cfcf4a56e4f522f64e1e6a2647ddc57eb8493b93c03240b1d9b39 + checksum: fb7ba7f8a6741b14bbe8db0bf1b12ff7a24d12c40d8276f32b9b393881d74bfed3bed4f1e5b0756cac0e43c4bd8106094d5cf6a3c527400e9713283fc3832dab languageName: node linkType: hard -"@docusaurus/plugin-content-pages@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-content-pages@npm:3.2.1" +"@docusaurus/plugin-content-pages@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-content-pages@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/mdx-loader": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/mdx-loader": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 fs-extra: ^11.1.1 tslib: ^2.6.0 webpack: ^5.88.1 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 3cce99f8aa863b97cbb54a50b448073222a0678528b09f5bec2196e73ec4740f412f8675ed05d283ff672756a5d3005f7a1e4d8c8f882cd0d6d5691cbccb604c + checksum: 8b3f1040e8ec006c9431508e73ef3f61cd5759bece3770189f7d52609f91bd156c9b18d0608f9cb14c456a1d1823be6633c573d5eee7cf9bd142b0f978c7a745 languageName: node linkType: hard -"@docusaurus/plugin-debug@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-debug@npm:3.2.1" +"@docusaurus/plugin-debug@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-debug@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils": 3.5.2 fs-extra: ^11.1.1 react-json-view-lite: ^1.2.0 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: b3fb1c8935463afb97f233042692c247d4147c03e18ef9fb37fbf0c46d4adaefa4af0d5c357025992dadfe7b83a9fd3754946b8947bfb8b9535dca390a3668d0 + checksum: a839e6c3a595ea202fdd7fbce638ab8df26ba73a8c7ead8c04d1bbb509ebe34e9633e7fe9eb54a7a733e93a03d74a60df4d9f6597b9621ff464280d4dd71db34 languageName: node linkType: hard -"@docusaurus/plugin-google-analytics@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-google-analytics@npm:3.2.1" +"@docusaurus/plugin-google-analytics@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-google-analytics@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: e1e881fd6adbe408029257d526759b9217f7d70e5e068c7e9241a5f0c3050b0fa46acfeb4f8a23c3f36e1739d0a3d810642d69c6648ff6801ce13b646e44e6c1 + checksum: 0b8c4d21333d40c2509d6ef807caaf69f085010c5deac514ab34f53b5486fd76766c90213dc98976a6c4d66fdfa14bf6b05594e51e8a53ec60c2a3fa08fd9a83 languageName: node linkType: hard -"@docusaurus/plugin-google-gtag@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-google-gtag@npm:3.2.1" +"@docusaurus/plugin-google-gtag@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-google-gtag@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 "@types/gtag.js": ^0.0.12 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: b7758289d8453e98baf95d41e754c1e4c8fd5b1c000ba444c4bdf13fc97674a3cddf3215b6406266729e23898641b5bae297c5422c5bd079ef04773fa5a15c1b + checksum: 5d53c2483c8c7e3a8e842bd091a774d4041f0e165d216b3c02f031a224a77258c9456e8b2acd0500b4a0eff474a83c1b82803628db9d4b132514409936c68ac4 languageName: node linkType: hard -"@docusaurus/plugin-google-tag-manager@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-google-tag-manager@npm:3.2.1" +"@docusaurus/plugin-google-tag-manager@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-google-tag-manager@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 82355aed046b12ce0fead68339e24a3c6f2f517bc2b80c9c26c502cc49d86c1b6d0f797d5269d1d5e73ac78fd748c8a2f4528f7f3feee1137ae8e73876426426 + checksum: 9a6fc2ca54ea677c6edfd78f4f392d7d9ae86afd085fcda96d5ac41efa441352c25a2519595d9d15fb9b838e2ae39837f0daf02e2406c5cd56199ae237bd7b7a languageName: node linkType: hard -"@docusaurus/plugin-sitemap@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/plugin-sitemap@npm:3.2.1" +"@docusaurus/plugin-sitemap@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/plugin-sitemap@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/logger": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/logger": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 fs-extra: ^11.1.1 sitemap: ^7.1.1 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: b2e4c4fddd0fbdd4a6a4c93a0f9c16b1294162146eb9911ce378f33d70396f08dfa98d92aed133bba2a8df2b1710c257bf00c0657933ee6cd9c5edb36c8054dc + checksum: 26b6bceb7ab87fe7f6f666742d1e81de32cdacc5aaa3d45d91002c7d64e3258f3d0aac87c6b0d442eaf34ede2af4b7521b50737f2e8e2718daff6fce10230213 languageName: node linkType: hard "@docusaurus/preset-classic@npm:^3.1.1": - version: 3.2.1 - resolution: "@docusaurus/preset-classic@npm:3.2.1" + version: 3.5.2 + resolution: "@docusaurus/preset-classic@npm:3.5.2" dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/plugin-content-blog": 3.2.1 - "@docusaurus/plugin-content-docs": 3.2.1 - "@docusaurus/plugin-content-pages": 3.2.1 - "@docusaurus/plugin-debug": 3.2.1 - "@docusaurus/plugin-google-analytics": 3.2.1 - "@docusaurus/plugin-google-gtag": 3.2.1 - "@docusaurus/plugin-google-tag-manager": 3.2.1 - "@docusaurus/plugin-sitemap": 3.2.1 - "@docusaurus/theme-classic": 3.2.1 - "@docusaurus/theme-common": 3.2.1 - "@docusaurus/theme-search-algolia": 3.2.1 - "@docusaurus/types": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/plugin-content-blog": 3.5.2 + "@docusaurus/plugin-content-docs": 3.5.2 + "@docusaurus/plugin-content-pages": 3.5.2 + "@docusaurus/plugin-debug": 3.5.2 + "@docusaurus/plugin-google-analytics": 3.5.2 + "@docusaurus/plugin-google-gtag": 3.5.2 + "@docusaurus/plugin-google-tag-manager": 3.5.2 + "@docusaurus/plugin-sitemap": 3.5.2 + "@docusaurus/theme-classic": 3.5.2 + "@docusaurus/theme-common": 3.5.2 + "@docusaurus/theme-search-algolia": 3.5.2 + "@docusaurus/types": 3.5.2 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 343c896f22bffbda9db4af7d652588f353c5f60336e545eb07be0dfe9bc29ca04a3978d88d5a8b3fa7caafc56a48b341349ffd08006885fa0d4de216cfdc5401 + checksum: ec578e62b3b13b1874b14235a448a913c2d2358ea9b9d9c60bb250be468ab62387c88ec44e1ee82ad5b3d7243306e31919888a80eae62e5e8eab0ae12194bf69 languageName: node linkType: hard -"@docusaurus/react-loadable@npm:5.5.2, react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version: 5.5.2 - resolution: "@docusaurus/react-loadable@npm:5.5.2" +"@docusaurus/theme-classic@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/theme-classic@npm:3.5.2" dependencies: - "@types/react": "*" - prop-types: ^15.6.2 - peerDependencies: - react: "*" - checksum: 930fb9e2936412a12461f210acdc154a433283921ca43ac3fc3b84cb6c12eb738b3a3719373022bf68004efeb1a928dbe36c467d7a1f86454ed6241576d936e7 - languageName: node - linkType: hard - -"@docusaurus/theme-classic@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/theme-classic@npm:3.2.1" - dependencies: - "@docusaurus/core": 3.2.1 - "@docusaurus/mdx-loader": 3.2.1 - "@docusaurus/module-type-aliases": 3.2.1 - "@docusaurus/plugin-content-blog": 3.2.1 - "@docusaurus/plugin-content-docs": 3.2.1 - "@docusaurus/plugin-content-pages": 3.2.1 - "@docusaurus/theme-common": 3.2.1 - "@docusaurus/theme-translations": 3.2.1 - "@docusaurus/types": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/mdx-loader": 3.5.2 + "@docusaurus/module-type-aliases": 3.5.2 + "@docusaurus/plugin-content-blog": 3.5.2 + "@docusaurus/plugin-content-docs": 3.5.2 + "@docusaurus/plugin-content-pages": 3.5.2 + "@docusaurus/theme-common": 3.5.2 + "@docusaurus/theme-translations": 3.5.2 + "@docusaurus/types": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 "@mdx-js/react": ^3.0.0 clsx: ^2.0.0 copy-text-to-clipboard: ^3.2.0 - infima: 0.2.0-alpha.43 + infima: 0.2.0-alpha.44 lodash: ^4.17.21 nprogress: ^0.2.0 postcss: ^8.4.26 @@ -2093,21 +2111,18 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 7b38e47e9334ba6ad84f6432ec9ae81caad7f6c630b2a332617b0f32f1559b0e56f3d8857c732da62d1d7213ad0f493853bf18b1707a2f8d8bcccef32f1d81a1 + checksum: 6c415b01ad24bb43eb166e2b780a84356ff14a627627f6a541c2803832d56c4f9409a5636048693d2d24804f59c2cc7bda925d9ef999a8276fe125477d2b2e1e languageName: node linkType: hard -"@docusaurus/theme-common@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/theme-common@npm:3.2.1" +"@docusaurus/theme-common@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/theme-common@npm:3.5.2" dependencies: - "@docusaurus/mdx-loader": 3.2.1 - "@docusaurus/module-type-aliases": 3.2.1 - "@docusaurus/plugin-content-blog": 3.2.1 - "@docusaurus/plugin-content-docs": 3.2.1 - "@docusaurus/plugin-content-pages": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 + "@docusaurus/mdx-loader": 3.5.2 + "@docusaurus/module-type-aliases": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 "@types/history": ^4.7.11 "@types/react": "*" "@types/react-router-config": "*" @@ -2117,24 +2132,25 @@ __metadata: tslib: ^2.6.0 utility-types: ^3.10.0 peerDependencies: + "@docusaurus/plugin-content-docs": "*" react: ^18.0.0 react-dom: ^18.0.0 - checksum: 13de70293476e05f1b52c2d99a1b26c73bf99ac92aba3c8ddc413b5336725d2b54c56c167d12244fdb0b518ee9cdecbbfb3258fb8cc91272e9b795361b131fbb + checksum: c78ec7f6035abc920a2a0bc1ad78920178a5452538a3a70794eca8d4b976725f6ccc464ee3092afd31ca59b4e061ad4c21cdce7f5e10b06567075814b2fc2002 languageName: node linkType: hard -"@docusaurus/theme-search-algolia@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/theme-search-algolia@npm:3.2.1" +"@docusaurus/theme-search-algolia@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/theme-search-algolia@npm:3.5.2" dependencies: "@docsearch/react": ^3.5.2 - "@docusaurus/core": 3.2.1 - "@docusaurus/logger": 3.2.1 - "@docusaurus/plugin-content-docs": 3.2.1 - "@docusaurus/theme-common": 3.2.1 - "@docusaurus/theme-translations": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-validation": 3.2.1 + "@docusaurus/core": 3.5.2 + "@docusaurus/logger": 3.5.2 + "@docusaurus/plugin-content-docs": 3.5.2 + "@docusaurus/theme-common": 3.5.2 + "@docusaurus/theme-translations": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-validation": 3.5.2 algoliasearch: ^4.18.0 algoliasearch-helper: ^3.13.3 clsx: ^2.0.0 @@ -2146,30 +2162,30 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: befbb86bf309f2b770ae21bc1d5c91eb6e840a5a72858cdfd3b21dbabadd1738d6d427ada7745f9d3424bb1a6e01839e20bf35c15a4c13d59b63d259e52de5df + checksum: e945e3001996477597bfad074eaef074cf4c5365ed3076c3109130a2252b266e4e2fac46904a0626eedeff23b9ac11e7b985cc71f5485ede52d3ddf379b7959b languageName: node linkType: hard -"@docusaurus/theme-translations@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/theme-translations@npm:3.2.1" +"@docusaurus/theme-translations@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/theme-translations@npm:3.5.2" dependencies: fs-extra: ^11.1.1 tslib: ^2.6.0 - checksum: 43bdb90d143576d2e8eb56bfe2c9daa0e4250cdb2dcfd10096b86466e6ee253548ac5ef2f9a4986a5bc9a573d118fe4695ee5004f0ef00b57b720dac7f124337 + checksum: dc523c74a13fb8552c03e547c6de1c21881d899cc74bf088a2bed716e0ef1a4ceba2726c43656d87fff60413ca191f5ea946b182e4ae4129c14da832b5194d82 languageName: node linkType: hard "@docusaurus/tsconfig@npm:^3.1.1": - version: 3.2.1 - resolution: "@docusaurus/tsconfig@npm:3.2.1" - checksum: ea3c28b79b0de069c50f7b3a67d3ff682b6ded2ef02d2c7a4c2eaeddc8fcf79c9d9f5e60fbd2966cf3d247fbb8f63897b80a61fdd8b485c745a12eb684ae241a + version: 3.5.2 + resolution: "@docusaurus/tsconfig@npm:3.5.2" + checksum: 808a17eaf422ae9a948c6558dd1e92d4700b067ead3a63a84049c6845bf94f84e311cd0e4d517047fe9ea057efe393bb22c2d5c92d727d06c9f895e971f2c3ea languageName: node linkType: hard -"@docusaurus/types@npm:3.2.1, @docusaurus/types@npm:^3.1.1": - version: 3.2.1 - resolution: "@docusaurus/types@npm:3.2.1" +"@docusaurus/types@npm:3.5.2, @docusaurus/types@npm:^3.1.1": + version: 3.5.2 + resolution: "@docusaurus/types@npm:3.5.2" dependencies: "@mdx-js/mdx": ^3.0.0 "@types/history": ^4.7.11 @@ -2183,13 +2199,13 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 4f19e162bff627675df160ae5c33c6063646050c4de5c9698018fbd9d198300b9ce7a7333e4d1b369b42cfa42296dc9fb36547e4e37664d594deb08639e6b620 + checksum: e39451b7b08673ad5e1551ee6e4286f90f2554cf9ba245abfa56670550f48afca9c57b01c10ffa21dacb734c0fcd067150eeb2b1c1ebb1692f1f538b1eed0029 languageName: node linkType: hard -"@docusaurus/utils-common@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/utils-common@npm:3.2.1" +"@docusaurus/utils-common@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/utils-common@npm:3.5.2" dependencies: tslib: ^2.6.0 peerDependencies: @@ -2197,31 +2213,33 @@ __metadata: peerDependenciesMeta: "@docusaurus/types": optional: true - checksum: bc0b7e74bc29134dbdb7fbc2e8f9f39f0f460923a07d0ccd7f0542088e92c47faf06bdbd253b7ba2b9250b0869118a3b7bf3faa3a075a2a35f5f8545eb3345f2 + checksum: 9d550c89663d4271456ae0832c82a1691207ccc95e21df3a05a4bd6bbd2624bb9e3ab7327d939c04b2023378987bcf99321b2c37be1af214852832f65d6db14a languageName: node linkType: hard -"@docusaurus/utils-validation@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/utils-validation@npm:3.2.1" +"@docusaurus/utils-validation@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/utils-validation@npm:3.5.2" dependencies: - "@docusaurus/logger": 3.2.1 - "@docusaurus/utils": 3.2.1 - "@docusaurus/utils-common": 3.2.1 + "@docusaurus/logger": 3.5.2 + "@docusaurus/utils": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + fs-extra: ^11.2.0 joi: ^17.9.2 js-yaml: ^4.1.0 + lodash: ^4.17.21 tslib: ^2.6.0 - checksum: c7b5142083c8e4798c7f6aa1f7a06bc2e93e8e08a8a7a2c5eaf24aa6939e12e401f180f02164764805c40ec0f7179479e0ee98a935c2cb77037ca73ab33d80fd + checksum: 5966e6d0e8f26292c629899f13b545501b53b345b0e2291bb47aaa80d7c9c5cf155e15a4ecd073a4095ee7c83c6db3612e0a34f81a8187fd20410b1aeb92d731 languageName: node linkType: hard -"@docusaurus/utils@npm:3.2.1": - version: 3.2.1 - resolution: "@docusaurus/utils@npm:3.2.1" +"@docusaurus/utils@npm:3.5.2": + version: 3.5.2 + resolution: "@docusaurus/utils@npm:3.5.2" dependencies: - "@docusaurus/logger": 3.2.1 - "@docusaurus/utils-common": 3.2.1 - "@svgr/webpack": ^6.5.1 + "@docusaurus/logger": 3.5.2 + "@docusaurus/utils-common": 3.5.2 + "@svgr/webpack": ^8.1.0 escape-string-regexp: ^4.0.0 file-loader: ^6.2.0 fs-extra: ^11.1.1 @@ -2237,13 +2255,14 @@ __metadata: shelljs: ^0.8.5 tslib: ^2.6.0 url-loader: ^4.1.1 + utility-types: ^3.10.0 webpack: ^5.88.1 peerDependencies: "@docusaurus/types": "*" peerDependenciesMeta: "@docusaurus/types": optional: true - checksum: ea862b178e303b49e644e77a663df6e42909632022918b77dc1ee69c4de46dde3f210052b1063e96a820e1443141f70e44aa51372f2bf9cfde65e080ea639889 + checksum: 0e0f4fc65ed076d4e4b551ecb61447b7c2468060d1655afff314515844ae34dc0546f467f53bff535f3144afc109e974da27fadb7c678a5d19966bed9e7a27c4 languageName: node linkType: hard @@ -2270,51 +2289,51 @@ __metadata: languageName: node linkType: hard -"@jest/schemas@npm:^29.0.0": - version: 29.0.0 - resolution: "@jest/schemas@npm:29.0.0" +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" dependencies: - "@sinclair/typebox": ^0.24.1 - checksum: 41355c78f09eb1097e57a3c5d0ca11c9099e235e01ea5fa4e3953562a79a6a9296c1d300f1ba50ca75236048829e056b00685cd2f1ff8285e56fd2ce01249acb + "@sinclair/typebox": ^0.27.8 + checksum: 910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 languageName: node linkType: hard -"@jest/types@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/types@npm:29.3.1" +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" dependencies: - "@jest/schemas": ^29.0.0 + "@jest/schemas": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: 6f9faf27507b845ff3839c1adc6dbd038d7046d03d37e84c9fc956f60718711a801a5094c7eeee6b39ccf42c0ab61347fdc0fa49ab493ae5a8efd2fd41228ee8 + checksum: a0bcf15dbb0eca6bdd8ce61a3fb055349d40268622a7670a3b2eb3c3dbafe9eb26af59938366d520b86907b9505b0f9b29b85cec11579a9e580694b87cd90fcc languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": - version: 0.3.2 - resolution: "@jridgewell/gen-mapping@npm:0.3.2" +"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" dependencies: - "@jridgewell/set-array": ^1.0.1 + "@jridgewell/set-array": ^1.2.1 "@jridgewell/sourcemap-codec": ^1.4.10 - "@jridgewell/trace-mapping": ^0.3.9 - checksum: 1832707a1c476afebe4d0fbbd4b9434fdb51a4c3e009ab1e9938648e21b7a97049fa6009393bdf05cab7504108413441df26d8a3c12193996e65493a4efb6882 + "@jridgewell/trace-mapping": ^0.3.24 + checksum: ff7a1764ebd76a5e129c8890aa3e2f46045109dabde62b0b6c6a250152227647178ff2069ea234753a690d8f3c4ac8b5e7b267bbee272bffb7f3b0a370ab6e52 languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0": - version: 3.1.0 - resolution: "@jridgewell/resolve-uri@npm:3.1.0" - checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 83b85f72c59d1c080b4cbec0fef84528963a1b5db34e4370fa4bd1e3ff64a0d80e0cee7369d11d73c704e0286fb2865b530acac7a871088fbe92b5edf1000870 languageName: node linkType: hard -"@jridgewell/set-array@npm:^1.0.1": - version: 1.1.2 - resolution: "@jridgewell/set-array@npm:1.1.2" - checksum: 69a84d5980385f396ff60a175f7177af0b8da4ddb81824cb7016a9ef914eee9806c72b6b65942003c63f7983d4f39a5c6c27185bbca88eb4690b62075602e28e +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 832e513a85a588f8ed4f27d1279420d8547743cc37fcad5a5a76fc74bb895b013dfe614d0eed9cb860048e6546b798f8f2652020b4b2ba0561b05caa8c654b10 languageName: node linkType: hard @@ -2328,20 +2347,20 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.14 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" - checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.5.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" + checksum: 05df4f2538b3b0f998ea4c1cd34574d0feba216fa5d4ccaef0187d12abf82eafe6021cec8b49f9bb4d90f2ba4582ccc581e72986a5fcf4176ae0cfeb04cf52ec languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.18 - resolution: "@jridgewell/trace-mapping@npm:0.3.18" +"@jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" dependencies: - "@jridgewell/resolve-uri": 3.1.0 - "@jridgewell/sourcemap-codec": 1.4.14 - checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: 9d3c40d225e139987b50c48988f8717a54a8c994d8a948ee42e1412e08988761d0754d7d10b803061cc3aebf35f92a5dbbab493bd0e1a9ef9e89a2130e83ba34 languageName: node linkType: hard @@ -2499,10 +2518,10 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.24.1": - version: 0.24.51 - resolution: "@sinclair/typebox@npm:0.24.51" - checksum: fd0d855e748ef767eb19da1a60ed0ab928e91e0f358c1dd198d600762c0015440b15755e96d1176e2a0db7e09c6a64ed487828ee10dd0c3e22f61eb09c478cd0 +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 00bd7362a3439021aa1ea51b0e0d0a0e8ca1351a3d54c606b115fdcc49b51b16db6e5f43b4fe7a28c38688523e22a94d49dd31168868b655f0d4d50f032d07a1 languageName: node linkType: hard @@ -2540,159 +2559,159 @@ __metadata: languageName: node linkType: hard -"@svgr/babel-plugin-add-jsx-attribute@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:6.5.1" +"@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: cab83832830a57735329ed68f67c03b57ca21fa037b0134847b0c5c0ef4beca89956d7dacfbf7b2a10fd901e7009e877512086db2ee918b8c69aee7742ae32c0 + checksum: 3fc8e35d16f5abe0af5efe5851f27581225ac405d6a1ca44cda0df064cddfcc29a428c48c2e4bef6cebf627c9ac2f652a096030edb02cf5a120ce28d3c234710 languageName: node linkType: hard -"@svgr/babel-plugin-remove-jsx-attribute@npm:*": - version: 6.5.0 - resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:6.5.0" +"@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7a4dfc1345f5855b010684e9c5301731842bf91d72b82ce5cc4c82c80b94de1036e447a8a00fb306a6dd575cb4c640d8ce3cfee6607ddbb804796a77284c7f22 + checksum: ff992893c6c4ac802713ba3a97c13be34e62e6d981c813af40daabcd676df68a72a61bd1e692bb1eda3587f1b1d700ea462222ae2153bb0f46886632d4f88d08 languageName: node linkType: hard -"@svgr/babel-plugin-remove-jsx-empty-expression@npm:*": - version: 6.5.0 - resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:6.5.0" +"@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 3e173f720d530f9f71f8506f3eb78583eec3d87d66e385efe1ef3b3ebfc4e3680ec30f36414726de6a163e99ca69f54886022967e49476dea522267e1986936e + checksum: 0fb691b63a21bac00da3aa2dccec50d0d5a5b347ff408d60803b84410d8af168f2656e4ba1ee1f24dab0ae4e4af77901f2928752bb0434c1f6788133ec599ec8 languageName: node linkType: hard -"@svgr/babel-plugin-replace-jsx-attribute-value@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:6.5.1" +"@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b7d2125758e766e1ebd14b92216b800bdc976959bc696dbfa1e28682919147c1df4bb8b1b5fd037d7a83026e27e681fea3b8d3741af8d3cf4c9dfa3d412125df + checksum: 1edda65ef4f4dd8f021143c8ec276a08f6baa6f733b8e8ee2e7775597bf6b97afb47fdeefd579d6ae6c959fe2e634f55cd61d99377631212228c8cfb351b8921 languageName: node linkType: hard -"@svgr/babel-plugin-svg-dynamic-title@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:6.5.1" +"@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 0fd42ebf127ae9163ef341e84972daa99bdcb9e6ed3f83aabd95ee173fddc43e40e02fa847fbc0a1058cf5549f72b7960a2c5e22c3e4ac18f7e3ac81277852ae + checksum: 876cec891488992e6a9aebb8155e2bea4ec461b4718c51de36e988e00e271c6d9d01ef6be17b9effd44b2b3d7db0b41c161a5904a46ae6f38b26b387ad7f3709 languageName: node linkType: hard -"@svgr/babel-plugin-svg-em-dimensions@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:6.5.1" +"@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: c1550ee9f548526fa66fd171e3ffb5696bfc4e4cd108a631d39db492c7410dc10bba4eb5a190e9df824bf806130ccc586ae7d2e43c547e6a4f93bbb29a18f344 + checksum: be0e2d391164428327d9ec469a52cea7d93189c6b0e2c290999e048f597d777852f701c64dca44cd45b31ed14a7f859520326e2e4ad7c3a4545d0aa235bc7e9a languageName: node linkType: hard -"@svgr/babel-plugin-transform-react-native-svg@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:6.5.1" +"@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 4c924af22b948b812629e80efb90ad1ec8faae26a232d8ca8a06b46b53e966a2c415a57806a3ff0ea806a622612e546422719b69ec6839717a7755dac19171d9 + checksum: 85b434a57572f53bd2b9f0606f253e1fcf57b4a8c554ec3f2d43ed17f50d8cae200cb3aaf1ec9d626e1456e8b135dce530ae047eb0bed6d4bf98a752d6640459 languageName: node linkType: hard -"@svgr/babel-plugin-transform-svg-component@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-transform-svg-component@npm:6.5.1" +"@svgr/babel-plugin-transform-svg-component@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-transform-svg-component@npm:8.0.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e496bb5ee871feb6bcab250b6e067322da7dd5c9c2b530b41e5586fe090f86611339b49d0a909c334d9b24cbca0fa755c949a2526c6ad03c6b5885666874cf5f + checksum: 04e2023d75693eeb0890341c40e449881184663056c249be7e5c80168e4aabb0fadd255e8d5d2dbf54b8c2a6e700efba994377135bfa4060dc4a2e860116ef8c languageName: node linkType: hard -"@svgr/babel-preset@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-preset@npm:6.5.1" +"@svgr/babel-preset@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-preset@npm:8.1.0" dependencies: - "@svgr/babel-plugin-add-jsx-attribute": ^6.5.1 - "@svgr/babel-plugin-remove-jsx-attribute": "*" - "@svgr/babel-plugin-remove-jsx-empty-expression": "*" - "@svgr/babel-plugin-replace-jsx-attribute-value": ^6.5.1 - "@svgr/babel-plugin-svg-dynamic-title": ^6.5.1 - "@svgr/babel-plugin-svg-em-dimensions": ^6.5.1 - "@svgr/babel-plugin-transform-react-native-svg": ^6.5.1 - "@svgr/babel-plugin-transform-svg-component": ^6.5.1 + "@svgr/babel-plugin-add-jsx-attribute": 8.0.0 + "@svgr/babel-plugin-remove-jsx-attribute": 8.0.0 + "@svgr/babel-plugin-remove-jsx-empty-expression": 8.0.0 + "@svgr/babel-plugin-replace-jsx-attribute-value": 8.0.0 + "@svgr/babel-plugin-svg-dynamic-title": 8.0.0 + "@svgr/babel-plugin-svg-em-dimensions": 8.0.0 + "@svgr/babel-plugin-transform-react-native-svg": 8.1.0 + "@svgr/babel-plugin-transform-svg-component": 8.0.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9f124be39a8e64f909162f925b3a63ddaa5a342a5e24fc0b7f7d9d4d7f7e3b916596c754fb557dc259928399cad5366a27cb231627a0d2dcc4b13ac521cf05af + checksum: 3a67930f080b8891e1e8e2595716b879c944d253112bae763dce59807ba23454d162216c8d66a0a0e3d4f38a649ecd6c387e545d1e1261dd69a68e9a3392ee08 languageName: node linkType: hard -"@svgr/core@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/core@npm:6.5.1" +"@svgr/core@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/core@npm:8.1.0" dependencies: - "@babel/core": ^7.19.6 - "@svgr/babel-preset": ^6.5.1 - "@svgr/plugin-jsx": ^6.5.1 + "@babel/core": ^7.21.3 + "@svgr/babel-preset": 8.1.0 camelcase: ^6.2.0 - cosmiconfig: ^7.0.1 - checksum: fd6d6d5da5aeb956703310480b626c1fb3e3973ad9fe8025efc1dcf3d895f857b70d100c63cf32cebb20eb83c9607bafa464c9436e18fe6fe4fafdc73ed6b1a5 + cosmiconfig: ^8.1.3 + snake-case: ^3.0.4 + checksum: da4a12865c7dc59829d58df8bd232d6c85b7115fda40da0d2f844a1a51886e2e945560596ecfc0345d37837ac457de86a931e8b8d8550e729e0c688c02250d8a languageName: node linkType: hard -"@svgr/hast-util-to-babel-ast@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/hast-util-to-babel-ast@npm:6.5.1" +"@svgr/hast-util-to-babel-ast@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/hast-util-to-babel-ast@npm:8.0.0" dependencies: - "@babel/types": ^7.20.0 + "@babel/types": ^7.21.3 entities: ^4.4.0 - checksum: 37923cce1b3f4e2039077b0c570b6edbabe37d1cf1a6ee35e71e0fe00f9cffac450eec45e9720b1010418131a999cb0047331ba1b6d1d2c69af1b92ac785aacf + checksum: 88401281a38bbc7527e65ff5437970414391a86158ef4b4046c89764c156d2d39ecd7cce77be8a51994c9fb3249170cb1eb8b9128b62faaa81743ef6ed3534ab languageName: node linkType: hard -"@svgr/plugin-jsx@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/plugin-jsx@npm:6.5.1" +"@svgr/plugin-jsx@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-jsx@npm:8.1.0" dependencies: - "@babel/core": ^7.19.6 - "@svgr/babel-preset": ^6.5.1 - "@svgr/hast-util-to-babel-ast": ^6.5.1 + "@babel/core": ^7.21.3 + "@svgr/babel-preset": 8.1.0 + "@svgr/hast-util-to-babel-ast": 8.0.0 svg-parser: ^2.0.4 peerDependencies: - "@svgr/core": ^6.0.0 - checksum: 42f22847a6bdf930514d7bedd3c5e1fd8d53eb3594779f9db16cb94c762425907c375cd8ec789114e100a4d38068aca6c7ab5efea4c612fba63f0630c44cc859 + "@svgr/core": "*" + checksum: 0418a9780753d3544912ee2dad5d2cf8d12e1ba74df8053651b3886aeda54d5f0f7d2dece0af5e0d838332c4f139a57f0dabaa3ca1afa4d1a765efce6a7656f2 languageName: node linkType: hard -"@svgr/plugin-svgo@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/plugin-svgo@npm:6.5.1" +"@svgr/plugin-svgo@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-svgo@npm:8.1.0" dependencies: - cosmiconfig: ^7.0.1 - deepmerge: ^4.2.2 - svgo: ^2.8.0 + cosmiconfig: ^8.1.3 + deepmerge: ^4.3.1 + svgo: ^3.0.2 peerDependencies: "@svgr/core": "*" - checksum: cd2833530ac0485221adc2146fd992ab20d79f4b12eebcd45fa859721dd779483158e11dfd9a534858fe468416b9412416e25cbe07ac7932c44ed5fa2021c72e + checksum: 59d9d214cebaacca9ca71a561f463d8b7e5a68ca9443e4792a42d903acd52259b1790c0680bc6afecc3f00a255a6cbd7ea278a9f625bac443620ea58a590c2d0 languageName: node linkType: hard -"@svgr/webpack@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/webpack@npm:6.5.1" +"@svgr/webpack@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/webpack@npm:8.1.0" dependencies: - "@babel/core": ^7.19.6 - "@babel/plugin-transform-react-constant-elements": ^7.18.12 - "@babel/preset-env": ^7.19.4 + "@babel/core": ^7.21.3 + "@babel/plugin-transform-react-constant-elements": ^7.21.3 + "@babel/preset-env": ^7.20.2 "@babel/preset-react": ^7.18.6 - "@babel/preset-typescript": ^7.18.6 - "@svgr/core": ^6.5.1 - "@svgr/plugin-jsx": ^6.5.1 - "@svgr/plugin-svgo": ^6.5.1 - checksum: d10582eb4fa82a5b6d314cb49f2c640af4fd3a60f5b76095d2b14e383ef6a43a6f4674b68774a21787dbde69dec0a251cfcfc3f9a96c82754ba5d5c6daf785f0 + "@babel/preset-typescript": ^7.21.0 + "@svgr/core": 8.1.0 + "@svgr/plugin-jsx": 8.1.0 + "@svgr/plugin-svgo": 8.1.0 + checksum: c6eec5b0cf2fb2ecd3a7a362d272eda35330b17c76802a3481f499b5d07ff8f87b31d2571043bff399b051a1767b1e2e499dbf186104d1c06d76f9f1535fac01 languageName: node linkType: hard @@ -3553,7 +3572,7 @@ __metadata: languageName: node linkType: hard -"ajv-keywords@npm:^5.0.0": +"ajv-keywords@npm:^5.1.0": version: 5.1.0 resolution: "ajv-keywords@npm:5.1.0" dependencies: @@ -3576,15 +3595,15 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.8.0": - version: 8.11.2 - resolution: "ajv@npm:8.11.2" +"ajv@npm:^8.0.0, ajv@npm:^8.9.0": + version: 8.17.1 + resolution: "ajv@npm:8.17.1" dependencies: - fast-deep-equal: ^3.1.1 + fast-deep-equal: ^3.1.3 + fast-uri: ^3.0.1 json-schema-traverse: ^1.0.0 require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 53435bf79ee7d1eabba8085962dba4c08d08593334b304db7772887f0b7beebc1b3d957432f7437ed4b60e53b5d966a57b439869890209c50fed610459999e3e + checksum: 1797bf242cfffbaf3b870d13565bd1716b73f214bb7ada9a497063aada210200da36e3ed40237285f3255acc4feeae91b1fb183625331bad27da95973f7253d9 languageName: node linkType: hard @@ -3765,21 +3784,21 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.12, autoprefixer@npm:^10.4.14": - version: 10.4.16 - resolution: "autoprefixer@npm:10.4.16" +"autoprefixer@npm:^10.4.14, autoprefixer@npm:^10.4.19": + version: 10.4.20 + resolution: "autoprefixer@npm:10.4.20" dependencies: - browserslist: ^4.21.10 - caniuse-lite: ^1.0.30001538 - fraction.js: ^4.3.6 + browserslist: ^4.23.3 + caniuse-lite: ^1.0.30001646 + fraction.js: ^4.3.7 normalize-range: ^0.1.2 - picocolors: ^1.0.0 + picocolors: ^1.0.1 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 45fad7086495048dacb14bb7b00313e70e135b5d8e8751dcc60548889400763705ab16fc2d99ea628b44c3472698fb0e39598f595ba28409c965ab159035afde + checksum: 187cec2ec356631932b212f76dc64f4419c117fdb2fb9eeeb40867d38ba5ca5ba734e6ceefc9e3af4eec8258e60accdf5cbf2b7708798598fde35cdc3de562d6 languageName: node linkType: hard @@ -3805,16 +3824,28 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.4.5": - version: 0.4.5 - resolution: "babel-plugin-polyfill-corejs2@npm:0.4.5" +"babel-plugin-polyfill-corejs2@npm:^0.4.10, babel-plugin-polyfill-corejs2@npm:^0.4.5": + version: 0.4.11 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.11" dependencies: "@babel/compat-data": ^7.22.6 - "@babel/helper-define-polyfill-provider": ^0.4.2 + "@babel/helper-define-polyfill-provider": ^0.6.2 semver: ^6.3.1 peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 33a8e06aa54e2858d211c743d179f0487b03222f9ca1bfd7c4865bca243fca942a3358cb75f6bb894ed476cbddede834811fbd6903ff589f055821146f053e1a + checksum: f098353ce7c7dde1a1d2710858e01b471e85689110c9e37813e009072347eb8c55d5f84d20d3bf1cab31755f20078ba90f8855fdc4686a9daa826a95ff280bd7 + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.10.6": + version: 0.10.6 + resolution: "babel-plugin-polyfill-corejs3@npm:0.10.6" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.2 + core-js-compat: ^3.38.0 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: f762f29f7acca576897c63149c850f0a72babd3fb9ea436a2e36f0c339161c4b912a77828541d8188ce8a91e50965c6687120cf36071eabb1b7aa92f279e2164 languageName: node linkType: hard @@ -3841,6 +3872,17 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-regenerator@npm:^0.6.1": + version: 0.6.2 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 150233571072b6b3dfe946242da39cba8587b7f908d1c006f7545fc88b0e3c3018d445739beb61e7a75835f0c2751dbe884a94ff9b245ec42369d9267e0e1b3f + languageName: node + linkType: hard + "backstage-microsite@workspace:.": version: 0.0.0-use.local resolution: "backstage-microsite@workspace:." @@ -3997,7 +4039,7 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.2, braces@npm:~3.0.2": +"braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" dependencies: @@ -4006,17 +4048,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.18.1, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.1, browserslist@npm:^4.22.2": - version: 4.22.3 - resolution: "browserslist@npm:4.22.3" +"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.18.1, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1, browserslist@npm:^4.23.3": + version: 4.23.3 + resolution: "browserslist@npm:4.23.3" dependencies: - caniuse-lite: ^1.0.30001580 - electron-to-chromium: ^1.4.648 - node-releases: ^2.0.14 - update-browserslist-db: ^1.0.13 + caniuse-lite: ^1.0.30001646 + electron-to-chromium: ^1.5.4 + node-releases: ^2.0.18 + update-browserslist-db: ^1.1.0 bin: browserslist: cli.js - checksum: e62b17348e92143fe58181b02a6a97c4a98bd812d1dc9274673a54f73eec53dbed1c855ebf73e318ee00ee039f23c9a6d0e7629d24f3baef08c7a5b469742d57 + checksum: 7906064f9970aeb941310b2fcb8b4ace4a1b50aa657c986677c6f1553a8cabcc94ee9c5922f715baffbedaa0e6cf0831b6fed7b059dde6873a4bfadcbe069c7e languageName: node linkType: hard @@ -4142,10 +4184,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001580": - version: 1.0.30001587 - resolution: "caniuse-lite@npm:1.0.30001587" - checksum: fb50aa9beaaae42f9feae92ce038f6ff71e97510f024ef1bef2666f3adcfd36d6c59e5675442e5fe795575193f71bc826cb7721d4b0f6d763e82d193bea57863 +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001646": + version: 1.0.30001653 + resolution: "caniuse-lite@npm:1.0.30001653" + checksum: 289cf06c26a46f3e6460ccd5feffa788ab0ab35d306898c48120c65cfb11959bfa560e9f739393769b4fd01150c69b0747ad3ad5ec3abf3dfafd66df3c59254e languageName: node linkType: hard @@ -4233,7 +4275,7 @@ __metadata: languageName: node linkType: hard -"cheerio@npm:^1.0.0-rc.12": +"cheerio@npm:1.0.0-rc.12": version: 1.0.0-rc.12 resolution: "cheerio@npm:1.0.0-rc.12" dependencies: @@ -4390,7 +4432,7 @@ __metadata: languageName: node linkType: hard -"colord@npm:^2.9.1": +"colord@npm:^2.9.3": version: 2.9.3 resolution: "colord@npm:2.9.3" checksum: 95d909bfbcfd8d5605cbb5af56f2d1ce2b323990258fd7c0d2eb0e6d3bb177254d7fb8213758db56bb4ede708964f78c6b992b326615f81a18a6aaf11d64c650 @@ -4602,12 +4644,12 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.32.2": - version: 3.33.0 - resolution: "core-js-compat@npm:3.33.0" +"core-js-compat@npm:^3.32.2, core-js-compat@npm:^3.37.1, core-js-compat@npm:^3.38.0": + version: 3.38.1 + resolution: "core-js-compat@npm:3.38.1" dependencies: - browserslist: ^4.22.1 - checksum: 83ae54008c09b8e0ae3c59457039866c342c7e28b0d30eebb638a5b51c01432e63fe97695c90645cbc6a8b073a4f9a8b0e75f0818bbf8b4b054e01f4c17d3181 + browserslist: ^4.23.3 + checksum: a0a5673bcd59f588f0cd0b59cdacd4712b82909738a87406d334dd412eb3d273ae72b275bdd8e8fef63fca9ef12b42ed651be139c7c44c8a1acb423c8906992e languageName: node linkType: hard @@ -4645,20 +4687,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^7.0.1": - version: 7.1.0 - resolution: "cosmiconfig@npm:7.1.0" - dependencies: - "@types/parse-json": ^4.0.0 - import-fresh: ^3.2.1 - parse-json: ^5.0.0 - path-type: ^4.0.0 - yaml: ^1.10.0 - checksum: c53bf7befc1591b2651a22414a5e786cd5f2eeaa87f3678a3d49d6069835a9d8d1aef223728e98aa8fec9a95bf831120d245096db12abe019fecb51f5696c96f - languageName: node - linkType: hard - -"cosmiconfig@npm:^8.2.0": +"cosmiconfig@npm:^8.1.3, cosmiconfig@npm:^8.2.0": version: 8.3.6 resolution: "cosmiconfig@npm:8.3.6" dependencies: @@ -4695,12 +4724,12 @@ __metadata: languageName: node linkType: hard -"css-declaration-sorter@npm:^6.3.1": - version: 6.3.1 - resolution: "css-declaration-sorter@npm:6.3.1" +"css-declaration-sorter@npm:^7.2.0": + version: 7.2.0 + resolution: "css-declaration-sorter@npm:7.2.0" peerDependencies: postcss: ^8.0.9 - checksum: ff0d9989ee21ec4c42430b9bb86c43f973ed5024d68f30edc1e3fb07a22828ce3c3e5b922019f2ccbff606722e43c407c5c76e3cddac523ac4afcb31e4b2601c + checksum: 69b2f63a1c7c593123fabcbb353618ed01eb75f6404da9321328fbb30d603d89c47195129fadf1dc316e1406a0881400b324c2bded9438c47196e1c96ec726dd languageName: node linkType: hard @@ -4722,16 +4751,16 @@ __metadata: languageName: node linkType: hard -"css-minimizer-webpack-plugin@npm:^4.2.2": - version: 4.2.2 - resolution: "css-minimizer-webpack-plugin@npm:4.2.2" +"css-minimizer-webpack-plugin@npm:^5.0.1": + version: 5.0.1 + resolution: "css-minimizer-webpack-plugin@npm:5.0.1" dependencies: - cssnano: ^5.1.8 - jest-worker: ^29.1.2 - postcss: ^8.4.17 - schema-utils: ^4.0.0 - serialize-javascript: ^6.0.0 - source-map: ^0.6.1 + "@jridgewell/trace-mapping": ^0.3.18 + cssnano: ^6.0.1 + jest-worker: ^29.4.3 + postcss: ^8.4.24 + schema-utils: ^4.0.1 + serialize-javascript: ^6.0.1 peerDependencies: webpack: ^5.0.0 peerDependenciesMeta: @@ -4747,7 +4776,7 @@ __metadata: optional: true lightningcss: optional: true - checksum: 5417e76a445f35832aa96807c835b8e92834a6cd285b1b788dfe3ca0fa90fec7eb2dd6efa9d3649f9d8244b99b7da2d065951603b94918e8f6a366a5049cacdd + checksum: 10055802c61d1ae72584eac03b6bd221ecbefde11d337be44a5459d8de075b38f91b80949f95cd0c3a10295615ee013f82130bfac5fe9b5b3e8e75531f232680 languageName: node linkType: hard @@ -4777,13 +4806,23 @@ __metadata: languageName: node linkType: hard -"css-tree@npm:^1.1.2, css-tree@npm:^1.1.3": - version: 1.1.3 - resolution: "css-tree@npm:1.1.3" +"css-tree@npm:^2.3.1": + version: 2.3.1 + resolution: "css-tree@npm:2.3.1" dependencies: - mdn-data: 2.0.14 - source-map: ^0.6.1 - checksum: 79f9b81803991b6977b7fcb1588799270438274d89066ce08f117f5cdb5e20019b446d766c61506dd772c839df84caa16042d6076f20c97187f5abe3b50e7d1f + mdn-data: 2.0.30 + source-map-js: ^1.0.1 + checksum: 493cc24b5c22b05ee5314b8a0d72d8a5869491c1458017ae5ed75aeb6c3596637dbe1b11dac2548974624adec9f7a1f3a6cf40593dc1f9185eb0e8279543fbc0 + languageName: node + linkType: hard + +"css-tree@npm:~2.2.0": + version: 2.2.1 + resolution: "css-tree@npm:2.2.1" + dependencies: + mdn-data: 2.0.28 + source-map-js: ^1.0.1 + checksum: b94aa8cc2f09e6f66c91548411fcf74badcbad3e150345074715012d16333ce573596ff5dfca03c2a87edf1924716db765120f94247e919d72753628ba3aba27 languageName: node linkType: hard @@ -4803,89 +4842,90 @@ __metadata: languageName: node linkType: hard -"cssnano-preset-advanced@npm:^5.3.10": - version: 5.3.10 - resolution: "cssnano-preset-advanced@npm:5.3.10" +"cssnano-preset-advanced@npm:^6.1.2": + version: 6.1.2 + resolution: "cssnano-preset-advanced@npm:6.1.2" dependencies: - autoprefixer: ^10.4.12 - cssnano-preset-default: ^5.2.14 - postcss-discard-unused: ^5.1.0 - postcss-merge-idents: ^5.1.1 - postcss-reduce-idents: ^5.2.0 - postcss-zindex: ^5.1.0 + autoprefixer: ^10.4.19 + browserslist: ^4.23.0 + cssnano-preset-default: ^6.1.2 + postcss-discard-unused: ^6.0.5 + postcss-merge-idents: ^6.0.3 + postcss-reduce-idents: ^6.0.3 + postcss-zindex: ^6.0.2 peerDependencies: - postcss: ^8.2.15 - checksum: d21cb382aea2f35c9eaa50686280bbd5158260edf73020731364b03bae0d887292da51ed0b20b369f51d2573ee8c02c695f604647b839a9ca746be8a44c3bb5b + postcss: ^8.4.31 + checksum: cf70e27915947412730abb3075587efb66bcea58d7f1b906a7225bb4a40c9ca40150251a2ac33363d4f55bbdeb9ba000c242fa6244ee36cba2477ac07fbbe791 languageName: node linkType: hard -"cssnano-preset-default@npm:^5.2.14": - version: 5.2.14 - resolution: "cssnano-preset-default@npm:5.2.14" +"cssnano-preset-default@npm:^6.1.2": + version: 6.1.2 + resolution: "cssnano-preset-default@npm:6.1.2" dependencies: - css-declaration-sorter: ^6.3.1 - cssnano-utils: ^3.1.0 - postcss-calc: ^8.2.3 - postcss-colormin: ^5.3.1 - postcss-convert-values: ^5.1.3 - postcss-discard-comments: ^5.1.2 - postcss-discard-duplicates: ^5.1.0 - postcss-discard-empty: ^5.1.1 - postcss-discard-overridden: ^5.1.0 - postcss-merge-longhand: ^5.1.7 - postcss-merge-rules: ^5.1.4 - postcss-minify-font-values: ^5.1.0 - postcss-minify-gradients: ^5.1.1 - postcss-minify-params: ^5.1.4 - postcss-minify-selectors: ^5.2.1 - postcss-normalize-charset: ^5.1.0 - postcss-normalize-display-values: ^5.1.0 - postcss-normalize-positions: ^5.1.1 - postcss-normalize-repeat-style: ^5.1.1 - postcss-normalize-string: ^5.1.0 - postcss-normalize-timing-functions: ^5.1.0 - postcss-normalize-unicode: ^5.1.1 - postcss-normalize-url: ^5.1.0 - postcss-normalize-whitespace: ^5.1.1 - postcss-ordered-values: ^5.1.3 - postcss-reduce-initial: ^5.1.2 - postcss-reduce-transforms: ^5.1.0 - postcss-svgo: ^5.1.0 - postcss-unique-selectors: ^5.1.1 + browserslist: ^4.23.0 + css-declaration-sorter: ^7.2.0 + cssnano-utils: ^4.0.2 + postcss-calc: ^9.0.1 + postcss-colormin: ^6.1.0 + postcss-convert-values: ^6.1.0 + postcss-discard-comments: ^6.0.2 + postcss-discard-duplicates: ^6.0.3 + postcss-discard-empty: ^6.0.3 + postcss-discard-overridden: ^6.0.2 + postcss-merge-longhand: ^6.0.5 + postcss-merge-rules: ^6.1.1 + postcss-minify-font-values: ^6.1.0 + postcss-minify-gradients: ^6.0.3 + postcss-minify-params: ^6.1.0 + postcss-minify-selectors: ^6.0.4 + postcss-normalize-charset: ^6.0.2 + postcss-normalize-display-values: ^6.0.2 + postcss-normalize-positions: ^6.0.2 + postcss-normalize-repeat-style: ^6.0.2 + postcss-normalize-string: ^6.0.2 + postcss-normalize-timing-functions: ^6.0.2 + postcss-normalize-unicode: ^6.1.0 + postcss-normalize-url: ^6.0.2 + postcss-normalize-whitespace: ^6.0.2 + postcss-ordered-values: ^6.0.2 + postcss-reduce-initial: ^6.1.0 + postcss-reduce-transforms: ^6.0.2 + postcss-svgo: ^6.0.3 + postcss-unique-selectors: ^6.0.4 peerDependencies: - postcss: ^8.2.15 - checksum: d3bbbe3d50c6174afb28d0bdb65b511fdab33952ec84810aef58b87189f3891c34aaa8b6a6101acd5314f8acded839b43513e39a75f91a698ddc985a1b1d9e95 + postcss: ^8.4.31 + checksum: 51d93e52df7141143947dc4695b5087c04b41ea153e4f4c0282ac012b62c7457c6aca244f604ae94fa3b4840903a30a1e7df38f8610e0b304d05e3065375ee56 languageName: node linkType: hard -"cssnano-utils@npm:^3.1.0": - version: 3.1.0 - resolution: "cssnano-utils@npm:3.1.0" +"cssnano-utils@npm:^4.0.2": + version: 4.0.2 + resolution: "cssnano-utils@npm:4.0.2" peerDependencies: - postcss: ^8.2.15 - checksum: 975c84ce9174cf23bb1da1e9faed8421954607e9ea76440cd3bb0c1bea7e17e490d800fca5ae2812d1d9e9d5524eef23ede0a3f52497d7ccc628e5d7321536f2 + postcss: ^8.4.31 + checksum: f04c6854e75d847c7a43aff835e003d5bc7387ddfc476f0ad3a2d63663d0cec41047d46604c1717bf6b5a8e24e54bb519e465ff78d62c7e073c7cbe2279bebaf languageName: node linkType: hard -"cssnano@npm:^5.1.15, cssnano@npm:^5.1.8": - version: 5.1.15 - resolution: "cssnano@npm:5.1.15" +"cssnano@npm:^6.0.1, cssnano@npm:^6.1.2": + version: 6.1.2 + resolution: "cssnano@npm:6.1.2" dependencies: - cssnano-preset-default: ^5.2.14 - lilconfig: ^2.0.3 - yaml: ^1.10.2 + cssnano-preset-default: ^6.1.2 + lilconfig: ^3.1.1 peerDependencies: - postcss: ^8.2.15 - checksum: ca9e1922178617c66c2f1548824b2c7af2ecf69cc3a187fc96bf8d29251c2e84d9e4966c69cf64a2a6a057a37dff7d6d057bc8a2a0957e6ea382e452ae9d0bbb + postcss: ^8.4.31 + checksum: 65aad92c5ee0089ffd4cd933c18c65edbf7634f7c3cd833a499dc948aa7e4168be22130dfe83bde07fcdc87f7c45a02d09040b7f439498208bc90b8d5a9abcc8 languageName: node linkType: hard -"csso@npm:^4.2.0": - version: 4.2.0 - resolution: "csso@npm:4.2.0" +"csso@npm:^5.0.5": + version: 5.0.5 + resolution: "csso@npm:5.0.5" dependencies: - css-tree: ^1.1.2 - checksum: 380ba9663da3bcea58dee358a0d8c4468bb6539be3c439dc266ac41c047217f52fd698fb7e4b6b6ccdfb8cf53ef4ceed8cc8ceccb8dfca2aa628319826b5b998 + css-tree: ~2.2.0 + checksum: 0ad858d36bf5012ed243e9ec69962a867509061986d2ee07cc040a4b26e4d062c00d4c07e5ba8d430706ceb02dd87edd30a52b5937fd45b1b6f2119c4993d59a languageName: node linkType: hard @@ -4942,10 +4982,10 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2": - version: 4.2.2 - resolution: "deepmerge@npm:4.2.2" - checksum: a8c43a1ed8d6d1ed2b5bf569fa4c8eb9f0924034baf75d5d406e47e157a451075c4db353efea7b6bcc56ec48116a8ce72fccf867b6e078e7c561904b5897530b +"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 languageName: node linkType: hard @@ -5239,10 +5279,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.648": - version: 1.4.665 - resolution: "electron-to-chromium@npm:1.4.665" - checksum: 6c863300909a2712a79bd8a00ff2d3747fb72edf3dfda892449aabd42a12448e8e215e318d628421bbdfc7e4e3bc1fb38a81bbd530a71036bb86a75868e9eb8a +"electron-to-chromium@npm:^1.5.4": + version: 1.5.13 + resolution: "electron-to-chromium@npm:1.5.13" + checksum: f18ac84dd3bf9a200654a6a9292b9ec4bced0cf9bd26cec9941b775f4470c581c9d043e70b37a124d9752dcc0f47fc96613d52b2defd8e59632852730cb418b9 languageName: node linkType: hard @@ -5351,10 +5391,10 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.1.1": - version: 3.1.1 - resolution: "escalade@npm:3.1.1" - checksum: a3e2a99f07acb74b3ad4989c48ca0c3140f69f923e56d0cba0526240ee470b91010f9d39001f2a4a313841d237ede70a729e92125191ba5d21e74b106800b133 +"escalade@npm:^3.1.1, escalade@npm:^3.1.2": + version: 3.1.2 + resolution: "escalade@npm:3.1.2" + checksum: 1ec0977aa2772075493002bdbd549d595ff6e9393b1cb0d7d6fcaf78c750da0c158f180938365486f75cb69fba20294351caddfce1b46552a7b6c3cde52eaa02 languageName: node linkType: hard @@ -5648,6 +5688,13 @@ __metadata: languageName: node linkType: hard +"fast-uri@npm:^3.0.1": + version: 3.0.1 + resolution: "fast-uri@npm:3.0.1" + checksum: 106143ff83705995225dcc559411288f3337e732bb2e264e79788f1914b6bd8f8bc3683102de60b15ba00e6ebb443633cabac77d4ebc5cb228c47cf955e199ff + languageName: node + linkType: hard + "fast-url-parser@npm:1.1.3": version: 1.1.3 resolution: "fast-url-parser@npm:1.1.3" @@ -5837,10 +5884,10 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.3.6": - version: 4.3.6 - resolution: "fraction.js@npm:4.3.6" - checksum: e96ae77e64ebfd442d3a5a01a3f0637b0663fc2440bcf2841b3ad9341ba24c81fb2e3e7142e43ef7d088558c6b3f8609df135b201adc7a1c674aea6a71384162 +"fraction.js@npm:^4.3.7": + version: 4.3.7 + resolution: "fraction.js@npm:4.3.7" + checksum: e1553ae3f08e3ba0e8c06e43a3ab20b319966dfb7ddb96fd9b5d0ee11a66571af7f993229c88ebbb0d4a816eb813a24ed48207b140d442a8f76f33763b8d1f3f languageName: node linkType: hard @@ -5851,14 +5898,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.1.1": - version: 11.1.1 - resolution: "fs-extra@npm:11.1.1" +"fs-extra@npm:^11.1.1, fs-extra@npm:^11.2.0": + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd + checksum: b12e42fa40ba47104202f57b8480dd098aa931c2724565e5e70779ab87605665594e76ee5fb00545f772ab9ace167fe06d2ab009c416dc8c842c5ae6df7aa7e8 languageName: node linkType: hard @@ -6649,7 +6696,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": +"import-fresh@npm:^3.1.0, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -6687,10 +6734,10 @@ __metadata: languageName: node linkType: hard -"infima@npm:0.2.0-alpha.43": - version: 0.2.0-alpha.43 - resolution: "infima@npm:0.2.0-alpha.43" - checksum: fc5f79240e940eddd750439511767092ccb4051e5e91d253ec7630a9e7ce691812da3aa0f05e46b4c0a95dbfadeae5714fd0073f8d2df12e5aaff0697a1d6aa2 +"infima@npm:0.2.0-alpha.44": + version: 0.2.0-alpha.44 + resolution: "infima@npm:0.2.0-alpha.44" + checksum: e9871f4056c0c8b311fcd32e2864d23a8f6807af5ff32d3c4d8271ad9971b5a7ea5016787a6b215893bb3e9f5f14326816bc05151d576dd375b0d79279cdfa8b languageName: node linkType: hard @@ -7066,17 +7113,17 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-util@npm:29.3.1" +"jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: f67c60f062b94d21cb60e84b3b812d64b7bfa81fe980151de5c17a74eb666042d0134e2e756d099b7606a1fcf1d633824d2e58197d01d76dde1e2dc00dfcd413 + checksum: 042ab4980f4ccd4d50226e01e5c7376a8556b472442ca6091a8f102488c0f22e6e8b89ea874111d2328a2080083bf3225c86f3788c52af0bd0345a00eb57a3ca languageName: node linkType: hard @@ -7091,15 +7138,15 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.1.2": - version: 29.3.1 - resolution: "jest-worker@npm:29.3.1" +"jest-worker@npm:^29.4.3": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" dependencies: "@types/node": "*" - jest-util: ^29.3.1 + jest-util: ^29.7.0 merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: 38687fcbdc2b7ddc70bbb5dfc703ae095b46b3c7f206d62ecdf5f4d16e336178e217302138f3b906125576bb1cfe4cfe8d43681276fa5899d138ed9422099fb3 + checksum: 30fff60af49675273644d408b650fc2eb4b5dcafc5a0a455f238322a8f9d8a98d847baca9d51ff197b6747f54c7901daa2287799230b856a0f48287d131f8c13 languageName: node linkType: hard @@ -7279,10 +7326,10 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^2.0.3": - version: 2.0.6 - resolution: "lilconfig@npm:2.0.6" - checksum: 40a3cd72f103b1be5975f2ac1850810b61d4053e20ab09be8d3aeddfe042187e1ba70b4651a7e70f95efa1642e7dc8b2ae395b317b7d7753b241b43cef7c0f7d +"lilconfig@npm:^3.1.1": + version: 3.1.2 + resolution: "lilconfig@npm:3.1.2" + checksum: 4e8b83ddd1d0ad722600994e6ba5d858ddca14f0587aa6b9c8185e17548149b5e13d4d583d811e9e9323157fa8c6a527e827739794c7502b59243c58e210b8c3 languageName: node linkType: hard @@ -7766,10 +7813,17 @@ __metadata: languageName: node linkType: hard -"mdn-data@npm:2.0.14": - version: 2.0.14 - resolution: "mdn-data@npm:2.0.14" - checksum: 9d0128ed425a89f4cba8f787dca27ad9408b5cb1b220af2d938e2a0629d17d879a34d2cb19318bdb26c3f14c77dd5dfbae67211f5caaf07b61b1f2c5c8c7dc16 +"mdn-data@npm:2.0.28": + version: 2.0.28 + resolution: "mdn-data@npm:2.0.28" + checksum: f51d587a6ebe8e426c3376c74ea6df3e19ec8241ed8e2466c9c8a3904d5d04397199ea4f15b8d34d14524b5de926d8724ae85207984be47e165817c26e49e0aa + languageName: node + linkType: hard + +"mdn-data@npm:2.0.30": + version: 2.0.30 + resolution: "mdn-data@npm:2.0.30" + checksum: d6ac5ac7439a1607df44b22738ecf83f48e66a0874e4482d6424a61c52da5cde5750f1d1229b6f5fa1b80a492be89465390da685b11f97d62b8adcc6e88189aa languageName: node linkType: hard @@ -8316,12 +8370,12 @@ __metadata: linkType: hard "micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: - braces: ^3.0.2 + braces: ^3.0.3 picomatch: ^2.3.1 - checksum: 02a17b671c06e8fefeeb6ef996119c1e597c942e632a21ef589154f23898c9c6a9858526246abb14f8bca6e77734aa9dcf65476fca47cedfb80d9577d52843fc + checksum: 79920eb634e6f400b464a954fcfa589c4e7c7143209488e44baf627f9affc8b1e306f41f4f0deedde97e69cb725920879462d3e750ab3bd3c1aed675bb3a8966 languageName: node linkType: hard @@ -8556,12 +8610,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.6": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: nanoid: bin/nanoid.cjs - checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3 + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 languageName: node linkType: hard @@ -8628,10 +8682,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.14": - version: 2.0.14 - resolution: "node-releases@npm:2.0.14" - checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 +"node-releases@npm:^2.0.18": + version: 2.0.18 + resolution: "node-releases@npm:2.0.18" + checksum: ef55a3d853e1269a6d6279b7692cd6ff3e40bc74947945101138745bfdc9a5edabfe72cb19a31a8e45752e1910c4c65c77d931866af6357f242b172b7283f5b3 languageName: node linkType: hard @@ -8660,13 +8714,6 @@ __metadata: languageName: node linkType: hard -"normalize-url@npm:^6.0.1": - version: 6.1.0 - resolution: "normalize-url@npm:6.1.0" - checksum: 4a4944631173e7d521d6b80e4c85ccaeceb2870f315584fa30121f505a6dfd86439c5e3fdd8cd9e0e291290c41d0c3599f0cb12ab356722ed242584c30348e50 - languageName: node - linkType: hard - "normalize-url@npm:^8.0.0": version: 8.0.0 resolution: "normalize-url@npm:8.0.0" @@ -9084,10 +9131,10 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0": - version: 1.0.0 - resolution: "picocolors@npm:1.0.0" - checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 +"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1": + version: 1.0.1 + resolution: "picocolors@npm:1.0.1" + checksum: fa68166d1f56009fc02a34cdfd112b0dd3cf1ef57667ac57281f714065558c01828cdf4f18600ad6851cbe0093952ed0660b1e0156bddf2184b6aaf5817553a5 languageName: node linkType: hard @@ -9116,88 +9163,88 @@ __metadata: languageName: node linkType: hard -"postcss-calc@npm:^8.2.3": - version: 8.2.4 - resolution: "postcss-calc@npm:8.2.4" +"postcss-calc@npm:^9.0.1": + version: 9.0.1 + resolution: "postcss-calc@npm:9.0.1" dependencies: - postcss-selector-parser: ^6.0.9 + postcss-selector-parser: ^6.0.11 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.2.2 - checksum: 314b4cebb0c4ed0cf8356b4bce71eca78f5a7842e6a3942a3bba49db168d5296b2bd93c3f735ae1c616f2651d94719ade33becc03c73d2d79c7394fb7f73eabb + checksum: 7327ed83bfec544ab8b3e38353baa72ff6d04378b856db4ad82dbd68ce0b73668867ef182b5d4025f9dd9aa9c64aacc50cd1bd9db8d8b51ccc4cb97866b9d72b languageName: node linkType: hard -"postcss-colormin@npm:^5.3.1": - version: 5.3.1 - resolution: "postcss-colormin@npm:5.3.1" +"postcss-colormin@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-colormin@npm:6.1.0" dependencies: - browserslist: ^4.21.4 + browserslist: ^4.23.0 caniuse-api: ^3.0.0 - colord: ^2.9.1 + colord: ^2.9.3 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: e5778baab30877cd1f51e7dc9d2242a162aeca6360a52956acd7f668c5bc235c2ccb7e4df0370a804d65ebe00c5642366f061db53aa823f9ed99972cebd16024 + postcss: ^8.4.31 + checksum: 55a1525de345d953bc7f32ecaa5ee6275ef0277c27d1f97ff06a1bd1a2fedf7f254e36dc1500621f1df20c25a6d2485a74a0b527d8ff74eb90726c76efe2ac8e languageName: node linkType: hard -"postcss-convert-values@npm:^5.1.3": - version: 5.1.3 - resolution: "postcss-convert-values@npm:5.1.3" +"postcss-convert-values@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-convert-values@npm:6.1.0" dependencies: - browserslist: ^4.21.4 + browserslist: ^4.23.0 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: df48cdaffabf9737f9cfdc58a3dc2841cf282506a7a944f6c70236cff295d3a69f63de6e0935eeb8a9d3f504324e5b4e240abc29e21df9e35a02585d3060aeb5 + postcss: ^8.4.31 + checksum: 43e9f66af9bdec3c76695f9dde36885abc01f662c370c490b45d895459caab2c5792f906f3ddad107129133e41485a65634da7f699eef916a636e47f6a37a299 languageName: node linkType: hard -"postcss-discard-comments@npm:^5.1.2": - version: 5.1.2 - resolution: "postcss-discard-comments@npm:5.1.2" +"postcss-discard-comments@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-discard-comments@npm:6.0.2" peerDependencies: - postcss: ^8.2.15 - checksum: abfd064ebc27aeaf5037643dd51ffaff74d1fa4db56b0523d073ace4248cbb64ffd9787bd6924b0983a9d0bd0e9bf9f10d73b120e50391dc236e0d26c812fa2a + postcss: ^8.4.31 + checksum: c1731ccc8d1e3d910412a61395988d3033365e6532d9e5432ad7c74add8c9dcb0af0c03d4e901bf0d2b59ea4e7297a0c77a547ff2ed1b1cc065559cc0de43b4e languageName: node linkType: hard -"postcss-discard-duplicates@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-discard-duplicates@npm:5.1.0" +"postcss-discard-duplicates@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-discard-duplicates@npm:6.0.3" peerDependencies: - postcss: ^8.2.15 - checksum: 88d6964201b1f4ed6bf7a32cefe68e86258bb6e42316ca01d9b32bdb18e7887d02594f89f4a2711d01b51ea6e3fcca8c54be18a59770fe5f4521c61d3eb6ca35 + postcss: ^8.4.31 + checksum: 308e3fb84c35e4703532de1efa5d6e8444cc5f167d0e40f42d7ea3fa3a37d9d636fd10729847d078e0c303eee16f8548d14b6f88a3fce4e38a2b452648465175 languageName: node linkType: hard -"postcss-discard-empty@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-discard-empty@npm:5.1.1" +"postcss-discard-empty@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-discard-empty@npm:6.0.3" peerDependencies: - postcss: ^8.2.15 - checksum: 970adb12fae5c214c0768236ad9a821552626e77dedbf24a8213d19cc2c4a531a757cd3b8cdd3fc22fb1742471b8692a1db5efe436a71236dec12b1318ee8ff4 + postcss: ^8.4.31 + checksum: bad305572faa066026a295faab37e718cee096589ab827b19c990c55620b2b2a1ce9f0145212651737a66086db01b2676c1927bbb8408c5f9cb42686d5959f00 languageName: node linkType: hard -"postcss-discard-overridden@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-discard-overridden@npm:5.1.0" +"postcss-discard-overridden@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-discard-overridden@npm:6.0.2" peerDependencies: - postcss: ^8.2.15 - checksum: d64d4a545aa2c81b22542895cfcddc787d24119f294d35d29b0599a1c818b3cc51f4ee80b80f5a0a09db282453dd5ac49f104c2117cc09112d0ac9b40b499a41 + postcss: ^8.4.31 + checksum: a38e0fe7a36f83cb9b73c1ba9ee2a48cf93c69ec0ea5753935824ffb71e958e58ae0393171c0f3d0014a397469d09bbb0d56bb5ab80f0280722967e2e273aebb languageName: node linkType: hard -"postcss-discard-unused@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-discard-unused@npm:5.1.0" +"postcss-discard-unused@npm:^6.0.5": + version: 6.0.5 + resolution: "postcss-discard-unused@npm:6.0.5" dependencies: - postcss-selector-parser: ^6.0.5 + postcss-selector-parser: ^6.0.16 peerDependencies: - postcss: ^8.2.15 - checksum: 5c09403a342a065033f5f22cefe6b402c76c2dc0aac31a736a2062d82c2a09f0ff2525b3df3a0c6f4e0ffc7a0392efd44bfe7f9d018e4cae30d15b818b216622 + postcss: ^8.4.31 + checksum: 7962640773240186de38125f142a6555b7f9b2493c4968e0f0b11c6629b2bf43ac70b9fc4ee78aa732d82670ad8bf802b2febc9a9864b022eb68530eded26836 languageName: node linkType: hard @@ -9215,89 +9262,89 @@ __metadata: languageName: node linkType: hard -"postcss-merge-idents@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-merge-idents@npm:5.1.1" +"postcss-merge-idents@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-merge-idents@npm:6.0.3" dependencies: - cssnano-utils: ^3.1.0 + cssnano-utils: ^4.0.2 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: ed8a673617ea6ae3e15d69558063cb1a5eeee01732f78cdc0196ab910324abc30828724ab8dfc4cda27e8c0077542e25688470f829819a2604625a673387ec72 + postcss: ^8.4.31 + checksum: b45780d6d103b8e45a580032747ee6e1842f81863672341a6b4961397e243ca896217bf1f3ee732376a766207d5f610ba8924cf08cf6d5bbd4b093133fd05d70 languageName: node linkType: hard -"postcss-merge-longhand@npm:^5.1.7": - version: 5.1.7 - resolution: "postcss-merge-longhand@npm:5.1.7" +"postcss-merge-longhand@npm:^6.0.5": + version: 6.0.5 + resolution: "postcss-merge-longhand@npm:6.0.5" dependencies: postcss-value-parser: ^4.2.0 - stylehacks: ^5.1.1 + stylehacks: ^6.1.1 peerDependencies: - postcss: ^8.2.15 - checksum: 81c3fc809f001b9b71a940148e242bdd6e2d77713d1bfffa15eb25c1f06f6648d5e57cb21645746d020a2a55ff31e1740d2b27900442913a9d53d8a01fb37e1b + postcss: ^8.4.31 + checksum: 9ae5acf47dc0c1f494684ae55672d55bba7f5ee11c9c0f266aabd7c798e9f7394c6096363cd95685fd21ef088740389121a317772cf523ca22c915009bca2617 languageName: node linkType: hard -"postcss-merge-rules@npm:^5.1.4": - version: 5.1.4 - resolution: "postcss-merge-rules@npm:5.1.4" +"postcss-merge-rules@npm:^6.1.1": + version: 6.1.1 + resolution: "postcss-merge-rules@npm:6.1.1" dependencies: - browserslist: ^4.21.4 + browserslist: ^4.23.0 caniuse-api: ^3.0.0 - cssnano-utils: ^3.1.0 - postcss-selector-parser: ^6.0.5 + cssnano-utils: ^4.0.2 + postcss-selector-parser: ^6.0.16 peerDependencies: - postcss: ^8.2.15 - checksum: 8ab6a569babe6cb412d6612adee74f053cea7edb91fa013398515ab36754b1fec830d68782ed8cdfb44cffdc6b78c79eab157bff650f428aa4460d3f3857447e + postcss: ^8.4.31 + checksum: 43f60a1c88806491cf752ae7871676de0e7a2a9d6d2fc6bc894068cc35a910a63d30f7c7d79545e0926c8b3a9ec583e5e8357203c40b5bad5ff58133b0c900f6 languageName: node linkType: hard -"postcss-minify-font-values@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-minify-font-values@npm:5.1.0" +"postcss-minify-font-values@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-minify-font-values@npm:6.1.0" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 35e858fa41efa05acdeb28f1c76579c409fdc7eabb1744c3bd76e895bb9fea341a016746362a67609688ab2471f587202b9a3e14ea28ad677754d663a2777ece + postcss: ^8.4.31 + checksum: 985e4dd2f89220a4442a822aad7dff016ab58a9dbb7bbca9d01c2d07d5a1e7d8c02e1c6e836abb4c9b4e825b4b80d99ee1f5899e74bf0d969095037738e6e452 languageName: node linkType: hard -"postcss-minify-gradients@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-minify-gradients@npm:5.1.1" +"postcss-minify-gradients@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-minify-gradients@npm:6.0.3" dependencies: - colord: ^2.9.1 - cssnano-utils: ^3.1.0 + colord: ^2.9.3 + cssnano-utils: ^4.0.2 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 27354072a07c5e6dab36731103b94ca2354d4ed3c5bc6aacfdf2ede5a55fa324679d8fee5450800bc50888dbb5e9ed67569c0012040c2be128143d0cebb36d67 + postcss: ^8.4.31 + checksum: 89b95088c3830f829f6d4636d1be4d4f13300bf9f1577c48c25169c81e11ec0026760b9abb32112b95d2c622f09d3b737f4d2975a7842927ccb567e1002ef7b3 languageName: node linkType: hard -"postcss-minify-params@npm:^5.1.4": - version: 5.1.4 - resolution: "postcss-minify-params@npm:5.1.4" +"postcss-minify-params@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-minify-params@npm:6.1.0" dependencies: - browserslist: ^4.21.4 - cssnano-utils: ^3.1.0 + browserslist: ^4.23.0 + cssnano-utils: ^4.0.2 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: bd63e2cc89edcf357bb5c2a16035f6d02ef676b8cede4213b2bddd42626b3d428403849188f95576fc9f03e43ebd73a29bf61d33a581be9a510b13b7f7f100d5 + postcss: ^8.4.31 + checksum: 1e1cc3057d9bcc532c70e40628e96e3aea0081d8072dffe983a270a8cd59c03ac585e57d036b70e43d4ee725f274a05a6a8efac5a715f448284e115c13f82a46 languageName: node linkType: hard -"postcss-minify-selectors@npm:^5.2.1": - version: 5.2.1 - resolution: "postcss-minify-selectors@npm:5.2.1" +"postcss-minify-selectors@npm:^6.0.4": + version: 6.0.4 + resolution: "postcss-minify-selectors@npm:6.0.4" dependencies: - postcss-selector-parser: ^6.0.5 + postcss-selector-parser: ^6.0.16 peerDependencies: - postcss: ^8.2.15 - checksum: 6fdbc84f99a60d56b43df8930707da397775e4c36062a106aea2fd2ac81b5e24e584a1892f4baa4469fa495cb87d1422560eaa8f6c9d500f9f0b691a5f95bab5 + postcss: ^8.4.31 + checksum: 150221a84422ca7627c67ee691ee51e0fe2c3583c8108801e9fc93d3be8b538c2eb04fcfdc908270d7eeaeaf01594a20b81311690a873efccb8a23aeafe1c354 languageName: node linkType: hard @@ -9345,192 +9392,191 @@ __metadata: languageName: node linkType: hard -"postcss-normalize-charset@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-charset@npm:5.1.0" +"postcss-normalize-charset@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-charset@npm:6.0.2" peerDependencies: - postcss: ^8.2.15 - checksum: e79d92971fc05b8b3c9b72f3535a574e077d13c69bef68156a0965f397fdf157de670da72b797f57b0e3bac8f38155b5dd1735ecab143b9cc4032d72138193b4 + postcss: ^8.4.31 + checksum: 5b8aeb17d61578a8656571cd5d5eefa8d4ee7126a99a41fdd322078002a06f2ae96f649197b9c01067a5f3e38a2e4b03e0e3fda5a0ec9e3d7ad056211ce86156 languageName: node linkType: hard -"postcss-normalize-display-values@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-display-values@npm:5.1.0" +"postcss-normalize-display-values@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-display-values@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: b6eb7b9b02c3bdd62bbc54e01e2b59733d73a1c156905d238e178762962efe0c6f5104544da39f32cade8a4fb40f10ff54b63a8ebfbdff51e8780afb9fbdcf86 + postcss: ^8.4.31 + checksum: da30a9394b0e4a269ccad8d240693a6cd564bcc60e24db67caee00f70ddfbc070ad76faed64c32e6eec9ed02e92565488b7879d4fd6c40d877c290eadbb0bb28 languageName: node linkType: hard -"postcss-normalize-positions@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-positions@npm:5.1.1" +"postcss-normalize-positions@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-positions@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: d9afc233729c496463c7b1cdd06732469f401deb387484c3a2422125b46ec10b4af794c101f8c023af56f01970b72b535e88373b9058ecccbbf88db81662b3c4 + postcss: ^8.4.31 + checksum: 44fb77583fae4d71b76e38226cf770570876bcf5af6940dc9aeac7a7e2252896b361e0249044766cff8dad445f925378f06a005d6541597573c20e599a62b516 languageName: node linkType: hard -"postcss-normalize-repeat-style@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-repeat-style@npm:5.1.1" +"postcss-normalize-repeat-style@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-repeat-style@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 2c6ad2b0ae10a1fda156b948c34f78c8f1e185513593de4d7e2480973586675520edfec427645fa168c337b0a6b3ceca26f92b96149741ca98a9806dad30d534 + postcss: ^8.4.31 + checksum: bebdac63bec6777ead3e265fc12527b261cf8d0da1b7f0abb12bda86fd53b7058e4afe392210ac74dac012e413bb1c2a46a1138c89f82b8bf70b81711f620f8c languageName: node linkType: hard -"postcss-normalize-string@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-string@npm:5.1.0" +"postcss-normalize-string@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-string@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 6e549c6e5b2831e34c7bdd46d8419e2278f6af1d5eef6d26884a37c162844e60339340c57e5e06058cdbe32f27fc6258eef233e811ed2f71168ef2229c236ada + postcss: ^8.4.31 + checksum: 5e8e253c528b542accafc142846fb33643c342a787c86e5b68c6287c7d8f63c5ae7d4d3fc28e3daf80821cc26a91add135e58bdd62ff9c735fca65d994898c7d languageName: node linkType: hard -"postcss-normalize-timing-functions@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-timing-functions@npm:5.1.0" +"postcss-normalize-timing-functions@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-timing-functions@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: da550f50e90b0b23e17b67449a7d1efd1aa68288e66d4aa7614ca6f5cc012896be1972b7168eee673d27da36504faccf7b9f835c0f7e81243f966a42c8c030aa + postcss: ^8.4.31 + checksum: 1970f5aad04be11f99d51c59e27debb6fd7b49d0fa4a8879062b42c82113f8e520a284448727add3b54de85deefb8bd5fe554f618406586e9ad8fc9d060609f1 languageName: node linkType: hard -"postcss-normalize-unicode@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-unicode@npm:5.1.1" +"postcss-normalize-unicode@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-normalize-unicode@npm:6.1.0" dependencies: - browserslist: ^4.21.4 + browserslist: ^4.23.0 postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 4c24d26cc9f4b19a9397db4e71dd600dab690f1de8e14a3809e2aa1452dbc3791c208c38a6316bbc142f29e934fdf02858e68c94038c06174d78a4937e0f273c + postcss: ^8.4.31 + checksum: 69ef35d06242061f0c504c128b83752e0f8daa30ebb26734de7d090460910be0b2efd8b17b1d64c3c85b95831a041faad9ad0aaba80e239406a79cfad3d63568 languageName: node linkType: hard -"postcss-normalize-url@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-url@npm:5.1.0" - dependencies: - normalize-url: ^6.0.1 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2.15 - checksum: 3bd4b3246d6600230bc827d1760b24cb3101827ec97570e3016cbe04dc0dd28f4dbe763245d1b9d476e182c843008fbea80823061f1d2219b96f0d5c724a24c0 - languageName: node - linkType: hard - -"postcss-normalize-whitespace@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-whitespace@npm:5.1.1" +"postcss-normalize-url@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-url@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 12d8fb6d1c1cba208cc08c1830959b7d7ad447c3f5581873f7e185f99a9a4230c43d3af21ca12c818e4690a5085a95b01635b762ad4a7bef69d642609b4c0e19 + postcss: ^8.4.31 + checksum: bef51a18bbfee4fbf0381fec3c91e6c0dace36fca053bbd5f228e653d2732b6df3985525d79c4f7fc89f840ed07eb6d226e9d7503ecdc6f16d6d80cacae9df33 languageName: node linkType: hard -"postcss-ordered-values@npm:^5.1.3": - version: 5.1.3 - resolution: "postcss-ordered-values@npm:5.1.3" - dependencies: - cssnano-utils: ^3.1.0 - postcss-value-parser: ^4.2.0 - peerDependencies: - postcss: ^8.2.15 - checksum: 6f3ca85b6ceffc68aadaf319d9ee4c5ac16d93195bf8cba2d1559b631555ad61941461cda6d3909faab86e52389846b2b36345cff8f0c3f4eb345b1b8efadcf9 - languageName: node - linkType: hard - -"postcss-reduce-idents@npm:^5.2.0": - version: 5.2.0 - resolution: "postcss-reduce-idents@npm:5.2.0" +"postcss-normalize-whitespace@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-normalize-whitespace@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: f0d644c86e160dd36ee4dd924ab7d6feacac867c87702e2f98f96b409430a62de4fec2dfc3c8731bda4e14196e29a752b4558942f0af2a3e6cd7f1f4b173db8e + postcss: ^8.4.31 + checksum: 6081eb3a4b305749eec02c00a95c2d236336a77ee636bb1d939f18d5dfa5ba82b7cf7fa072e83f9133d0bc984276596af3fe468bdd67c742ce69e9c63dbc218d languageName: node linkType: hard -"postcss-reduce-initial@npm:^5.1.2": - version: 5.1.2 - resolution: "postcss-reduce-initial@npm:5.1.2" +"postcss-ordered-values@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-ordered-values@npm:6.0.2" dependencies: - browserslist: ^4.21.4 + cssnano-utils: ^4.0.2 + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.4.31 + checksum: c3d96177b4ffa43754e835e30c40043cc75ab1e95eb6c55ac8723eb48c13a12e986250e63d96619bbbd1a098876a1c0c1b3b7a8e1de1108a009cf7aa0beac834 + languageName: node + linkType: hard + +"postcss-reduce-idents@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-reduce-idents@npm:6.0.3" + dependencies: + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.4.31 + checksum: 1feff316838f947386c908f50807cf1b9589fd09b8e8df633a01f2640af5492833cc892448938ceba10ab96826c44767b8f2e1569d587579423f2db81202f7c7 + languageName: node + linkType: hard + +"postcss-reduce-initial@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-reduce-initial@npm:6.1.0" + dependencies: + browserslist: ^4.23.0 caniuse-api: ^3.0.0 peerDependencies: - postcss: ^8.2.15 - checksum: 55db697f85231a81f1969d54c894e4773912d9ddb914f9b03d2e73abc4030f2e3bef4d7465756d0c1acfcc2c2d69974bfb50a972ab27546a7d68b5a4fc90282b + postcss: ^8.4.31 + checksum: 39e4034ffbf62a041b66944c5cebc4b17f656e76b97568f7f6230b0b886479e5c75b02ae4ba48c472cb0bde47489f9ed1fe6110ae8cff0d7b7165f53c2d64a12 languageName: node linkType: hard -"postcss-reduce-transforms@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-reduce-transforms@npm:5.1.0" +"postcss-reduce-transforms@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-reduce-transforms@npm:6.0.2" dependencies: postcss-value-parser: ^4.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: 0c6af2cba20e3ff63eb9ad045e634ddfb9c3e5c0e614c020db2a02f3aa20632318c4ede9e0c995f9225d9a101e673de91c0a6e10bb2fa5da6d6c75d15a55882f + postcss: ^8.4.31 + checksum: c424cc554eb5d253b7687b64925a13fc16759f058795d223854f5a20d9bca641b5f25d0559d03287e63f07a4629c24ac78156adcf604483fcad3c51721da0a08 languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.5, postcss-selector-parser@npm:^6.0.9": - version: 6.0.11 - resolution: "postcss-selector-parser@npm:6.0.11" +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.16, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": + version: 6.1.2 + resolution: "postcss-selector-parser@npm:6.1.2" dependencies: cssesc: ^3.0.0 util-deprecate: ^1.0.2 - checksum: 0b01aa9c2d2c8dbeb51e9b204796b678284be9823abc8d6d40a8b16d4149514e922c264a8ed4deb4d6dbced564b9be390f5942c058582d8656351516d6c49cde + checksum: ce9440fc42a5419d103f4c7c1847cb75488f3ac9cbe81093b408ee9701193a509f664b4d10a2b4d82c694ee7495e022f8f482d254f92b7ffd9ed9dea696c6f84 languageName: node linkType: hard -"postcss-sort-media-queries@npm:^4.4.1": - version: 4.4.1 - resolution: "postcss-sort-media-queries@npm:4.4.1" +"postcss-sort-media-queries@npm:^5.2.0": + version: 5.2.0 + resolution: "postcss-sort-media-queries@npm:5.2.0" dependencies: - sort-css-media-queries: 2.1.0 + sort-css-media-queries: 2.2.0 peerDependencies: - postcss: ^8.4.16 - checksum: 70b42e479bb1d15d8628678eefefd547d309e33e64262fe437630fe62d8e4b3adcae7f2b48ef8da9d3173576d4af109a9ffa9514573db1281deef324f5ea166f + postcss: ^8.4.23 + checksum: d4a976a64b53234762cc35c06ce97c1684bd7a64ead17e84c2047676c7307945be7c005235e6aac7c4620e1f835d6ba1a7dcf018ab7fe0a47657c62c96ad9f35 languageName: node linkType: hard -"postcss-svgo@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-svgo@npm:5.1.0" +"postcss-svgo@npm:^6.0.3": + version: 6.0.3 + resolution: "postcss-svgo@npm:6.0.3" dependencies: postcss-value-parser: ^4.2.0 - svgo: ^2.7.0 + svgo: ^3.2.0 peerDependencies: - postcss: ^8.2.15 - checksum: d86eb5213d9f700cf5efe3073799b485fb7cacae0c731db3d7749c9c2b1c9bc85e95e0baeca439d699ff32ea24815fc916c4071b08f67ed8219df229ce1129bd + postcss: ^8.4.31 + checksum: 1a7d1c8dea555884a7791e28ec2c22ea92331731067584ff5a23042a0e615f88fefde04e1140f11c262a728ef9fab6851423b40b9c47f9ae05353bd3c0ff051a languageName: node linkType: hard -"postcss-unique-selectors@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-unique-selectors@npm:5.1.1" +"postcss-unique-selectors@npm:^6.0.4": + version: 6.0.4 + resolution: "postcss-unique-selectors@npm:6.0.4" dependencies: - postcss-selector-parser: ^6.0.5 + postcss-selector-parser: ^6.0.16 peerDependencies: - postcss: ^8.2.15 - checksum: 637e7b786e8558265775c30400c54b6b3b24d4748923f4a39f16a65fd0e394f564ccc9f0a1d3c0e770618a7637a7502ea1d0d79f731d429cb202255253c23278 + postcss: ^8.4.31 + checksum: b09df9943b4e858e88b30f3d279ce867a0490df806f1f947d286b0a4e95ba923f1229c385e5bf365f4f124f1edccda41ec18ccad4ba8798d829279d6dc971203 languageName: node linkType: hard @@ -9541,23 +9587,23 @@ __metadata: languageName: node linkType: hard -"postcss-zindex@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-zindex@npm:5.1.0" +"postcss-zindex@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-zindex@npm:6.0.2" peerDependencies: - postcss: ^8.2.15 - checksum: 8581e0ee552622489dcb9fb9609a3ccc261a67a229ba91a70bd138fe102a2d04cedb14642b82b673d4cac7b559ef32574f2dafde2ff7816eecac024d231c5ead + postcss: ^8.4.31 + checksum: 394119e47b0fb098dc53d1bcf71b5500ab29605fe106526b2e81290bff179174ee00a82a4d4be5a42d4ef4138e8a3d6aabeef3b06cf7cb15b851848c8585d53b languageName: node linkType: hard -"postcss@npm:^8.4.17, postcss@npm:^8.4.21, postcss@npm:^8.4.26": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" +"postcss@npm:^8.4.21, postcss@npm:^8.4.24, postcss@npm:^8.4.26, postcss@npm:^8.4.38": + version: 8.4.41 + resolution: "postcss@npm:8.4.41" dependencies: - nanoid: ^3.3.6 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea + nanoid: ^3.3.7 + picocolors: ^1.0.1 + source-map-js: ^1.2.0 + checksum: f865894929eb0f7fc2263811cc853c13b1c75103028b3f4f26df777e27b201f1abe21cb4aa4c2e901c80a04f6fb325ee22979688fe55a70e2ea82b0a517d3b6f languageName: node linkType: hard @@ -9812,14 +9858,14 @@ __metadata: linkType: hard "react-dom@npm:^18.0.0": - version: 18.2.0 - resolution: "react-dom@npm:18.2.0" + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" dependencies: loose-envify: ^1.1.0 - scheduler: ^0.23.0 + scheduler: ^0.23.2 peerDependencies: - react: ^18.2.0 - checksum: 7d323310bea3a91be2965f9468d552f201b1c27891e45ddc2d6b8f717680c95a75ae0bc1e3f5cf41472446a2589a75aed4483aee8169287909fcd59ad149e8cc + react: ^18.3.1 + checksum: 298954ecd8f78288dcaece05e88b570014d8f6dce5db6f66e6ee91448debeb59dcd31561dddb354eee47e6c1bb234669459060deb238ed0213497146e555a0b9 languageName: node linkType: hard @@ -9881,6 +9927,17 @@ __metadata: languageName: node linkType: hard +"react-loadable@npm:@docusaurus/react-loadable@6.0.0": + version: 6.0.0 + resolution: "@docusaurus/react-loadable@npm:6.0.0" + dependencies: + "@types/react": "*" + peerDependencies: + react: "*" + checksum: 4c32061b2fc10689d5d8ba11ead71b69e4c8a55fcfeafb551a6747b1a7b496c4f2d8dbb5d023f5cafc2a9aea9d14582bdb324d11e6f9b8c3049d45b74439203f + languageName: node + linkType: hard + "react-router-config@npm:^5.1.1": version: 5.1.1 resolution: "react-router-config@npm:5.1.1" @@ -9930,11 +9987,11 @@ __metadata: linkType: hard "react@npm:^18.0.0": - version: 18.2.0 - resolution: "react@npm:18.2.0" + version: 18.3.1 + resolution: "react@npm:18.3.1" dependencies: loose-envify: ^1.1.0 - checksum: 88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b + checksum: a27bcfa8ff7c15a1e50244ad0d0c1cb2ad4375eeffefd266a64889beea6f6b64c4966c9b37d14ee32d6c9fcd5aa6ba183b6988167ab4d127d13e7cb5b386a376 languageName: node linkType: hard @@ -10387,15 +10444,15 @@ __metadata: linkType: hard "sass@npm:^1.57.1": - version: 1.75.0 - resolution: "sass@npm:1.75.0" + version: 1.77.8 + resolution: "sass@npm:1.77.8" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: bfb9f5ddb6a2e1fe0c1ba6191cdb17afa7b40c1eb892c7152f6a29ff2b06dc7a510bdb648f8cca0179dcb3965920ebeb8894f0710b0b450a99db563831345033 + checksum: 6b5dce17faa1bd1e349b4825bf7f76559a32f3f95d789cd2847623c88ee9635e1485d3458532a05fa5b9134cfbce79a4bad3f13dc63c2433632347674db0abae languageName: node linkType: hard @@ -10406,12 +10463,12 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.23.0": - version: 0.23.0 - resolution: "scheduler@npm:0.23.0" +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" dependencies: loose-envify: ^1.1.0 - checksum: d79192eeaa12abef860c195ea45d37cbf2bbf5f66e3c4dcd16f54a7da53b17788a70d109ee3d3dde1a0fd50e6a8fc171f4300356c5aee4fc0171de526bf35f8a + checksum: 3e82d1f419e240ef6219d794ff29c7ee415fbdc19e038f680a10c067108e06284f1847450a210b29bbaf97b9d8a97ced5f624c31c681248ac84c80d56ad5a2c4 languageName: node linkType: hard @@ -10437,15 +10494,15 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^4.0.0": - version: 4.0.0 - resolution: "schema-utils@npm:4.0.0" +"schema-utils@npm:^4.0.0, schema-utils@npm:^4.0.1": + version: 4.2.0 + resolution: "schema-utils@npm:4.2.0" dependencies: "@types/json-schema": ^7.0.9 - ajv: ^8.8.0 + ajv: ^8.9.0 ajv-formats: ^2.1.1 - ajv-keywords: ^5.0.0 - checksum: c843e92fdd1a5c145dbb6ffdae33e501867f9703afac67bdf35a685e49f85b1dcc10ea250033175a64bd9d31f0555bc6785b8359da0c90bcea30cf6dfbb55a8f + ajv-keywords: ^5.1.0 + checksum: 26a0463d47683258106e6652e9aeb0823bf0b85843039e068b57da1892f7ae6b6b1094d48e9ed5ba5cbe9f7166469d880858b9d91abe8bd249421eb813850cde languageName: node linkType: hard @@ -10730,6 +10787,16 @@ __metadata: languageName: node linkType: hard +"snake-case@npm:^3.0.4": + version: 3.0.4 + resolution: "snake-case@npm:3.0.4" + dependencies: + dot-case: ^3.0.4 + tslib: ^2.0.3 + checksum: 0a7a79900bbb36f8aaa922cf111702a3647ac6165736d5dc96d3ef367efc50465cac70c53cd172c382b022dac72ec91710608e5393de71f76d7142e6fd80e8a3 + languageName: node + linkType: hard + "sockjs@npm:^0.3.24": version: 0.3.24 resolution: "sockjs@npm:0.3.24" @@ -10762,17 +10829,17 @@ __metadata: languageName: node linkType: hard -"sort-css-media-queries@npm:2.1.0": - version: 2.1.0 - resolution: "sort-css-media-queries@npm:2.1.0" - checksum: 25cb8f08b148a2ed83d0bc1cf20ddb888d3dee2a3c986896099a21b28b999d5cca3e46a9ef64381bb36fca0fc820471713f2e8af2729ecc6e108ab2b3b315ea9 +"sort-css-media-queries@npm:2.2.0": + version: 2.2.0 + resolution: "sort-css-media-queries@npm:2.2.0" + checksum: c090c9a27be40f3e50f5f9bc9d85a8af0e2c5152565eca34bdb028d952749bce169bc5abef21a5a385ca6221a0869640c9faf58f082ac46de9085ebdb506291f languageName: node linkType: hard -"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.2": - version: 1.0.2 - resolution: "source-map-js@npm:1.0.2" - checksum: c049a7fc4deb9a7e9b481ae3d424cc793cb4845daa690bc5a05d428bf41bf231ced49b4cf0c9e77f9d42fdb3d20d6187619fc586605f5eabe995a316da8d377c +"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0": + version: 1.2.0 + resolution: "source-map-js@npm:1.2.0" + checksum: 791a43306d9223792e84293b00458bf102a8946e7188f3db0e4e22d8d530b5f80a4ce468eb5ec0bf585443ad55ebbd630bf379c98db0b1f317fd902500217f97 languageName: node linkType: hard @@ -10786,7 +10853,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0": +"source-map@npm:^0.6.0, source-map@npm:~0.6.0": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 @@ -10857,13 +10924,6 @@ __metadata: languageName: node linkType: hard -"stable@npm:^0.1.8": - version: 0.1.8 - resolution: "stable@npm:0.1.8" - checksum: 2ff482bb100285d16dd75cd8f7c60ab652570e8952c0bfa91828a2b5f646a0ff533f14596ea4eabd48bb7f4aeea408dce8f8515812b975d958a4cc4fa6b9dfeb - languageName: node - linkType: hard - "statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" @@ -11010,15 +11070,15 @@ __metadata: languageName: node linkType: hard -"stylehacks@npm:^5.1.1": - version: 5.1.1 - resolution: "stylehacks@npm:5.1.1" +"stylehacks@npm:^6.1.1": + version: 6.1.1 + resolution: "stylehacks@npm:6.1.1" dependencies: - browserslist: ^4.21.4 - postcss-selector-parser: ^6.0.4 + browserslist: ^4.23.0 + postcss-selector-parser: ^6.0.16 peerDependencies: - postcss: ^8.2.15 - checksum: 11175366ef52de65bf06cefba0ddc9db286dc3a1451fd2989e74c6ea47091a02329a4bf6ce10b1a36950056927b6bbbe47c5ab3a1f4c7032df932d010fbde5a2 + postcss: ^8.4.31 + checksum: 7bef69822280a23817caa43969de76d77ba34042e9f1f7baaeda8f22b1d8c20f1f839ad028552c169e158e387830f176feccd0324b07ef6ec657cba1dd0b2466 languageName: node linkType: hard @@ -11063,20 +11123,20 @@ __metadata: languageName: node linkType: hard -"svgo@npm:^2.7.0, svgo@npm:^2.8.0": - version: 2.8.0 - resolution: "svgo@npm:2.8.0" +"svgo@npm:^3.0.2, svgo@npm:^3.2.0": + version: 3.3.2 + resolution: "svgo@npm:3.3.2" dependencies: "@trysound/sax": 0.2.0 commander: ^7.2.0 - css-select: ^4.1.3 - css-tree: ^1.1.3 - csso: ^4.2.0 + css-select: ^5.1.0 + css-tree: ^2.3.1 + css-what: ^6.1.0 + csso: ^5.0.5 picocolors: ^1.0.0 - stable: ^0.1.8 bin: - svgo: bin/svgo - checksum: b92f71a8541468ffd0b81b8cdb36b1e242eea320bf3c1a9b2c8809945853e9d8c80c19744267eb91cabf06ae9d5fff3592d677df85a31be4ed59ff78534fa420 + svgo: ./bin/svgo + checksum: a3f8aad597dec13ab24e679c4c218147048dc1414fe04e99447c5f42a6e077b33d712d306df84674b5253b98c9b84dfbfb41fdd08552443b04946e43d03e054e languageName: node linkType: hard @@ -11449,17 +11509,17 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.13": - version: 1.0.13 - resolution: "update-browserslist-db@npm:1.0.13" +"update-browserslist-db@npm:^1.1.0": + version: 1.1.0 + resolution: "update-browserslist-db@npm:1.1.0" dependencies: - escalade: ^3.1.1 - picocolors: ^1.0.0 + escalade: ^3.1.2 + picocolors: ^1.0.1 peerDependencies: browserslist: ">= 4.21.0" bin: update-browserslist-db: cli.js - checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 + checksum: 7b74694d96f0c360f01b702e72353dc5a49df4fe6663d3ee4e5c628f061576cddf56af35a3a886238c01dd3d8f231b7a86a8ceaa31e7a9220ae31c1c1238e562 languageName: node linkType: hard @@ -11944,7 +12004,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^1.10.0, yaml@npm:^1.10.2, yaml@npm:^1.7.2": +"yaml@npm:^1.7.2": version: 1.10.2 resolution: "yaml@npm:1.10.2" checksum: ce4ada136e8a78a0b08dc10b4b900936912d15de59905b2bf415b4d33c63df1d555d23acb2a41b23cf9fb5da41c256441afca3d6509de7247daa062fd2c5ea5f diff --git a/mkdocs.yml b/mkdocs.yml index 33c9af3286..7bc56695b8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -138,7 +138,6 @@ nav: - Proxying: 'plugins/proxying.md' - Backend plugin: 'plugins/backend-plugin.md' - Call existing API: 'plugins/call-existing-api.md' - - URL Reader: 'plugins/url-reader.md' - Testing: - Testing with Jest: 'plugins/testing.md' - Publishing: diff --git a/package.json b/package.json index e99233dbf7..309ae364fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.30.0", + "version": "1.31.0-next.0", "private": true, "repository": { "type": "git", diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index af5b4a5c24..2893155e97 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # app-next-example-plugin +## 0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + ## 0.0.14 ### Patch Changes diff --git a/packages/app-next-example-plugin/api-report.md b/packages/app-next-example-plugin/api-report.md index 4698edec6b..06d325b844 100644 --- a/packages/app-next-example-plugin/api-report.md +++ b/packages/app-next-example-plugin/api-report.md @@ -4,44 +4,43 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) -const examplePlugin: BackstagePlugin< +const examplePlugin: FrontendPlugin< {}, {}, { - 'page:example': ExtensionDefinition< - { + 'page:example': ExtensionDefinition<{ + kind: 'page'; + namespace: undefined; + name: undefined; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + }>; } >; export default examplePlugin; diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index 5bb8318e2b..a923676370 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,6 +1,6 @@ { "name": "app-next-example-plugin", - "version": "0.0.14", + "version": "0.0.15-next.0", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 5d3a1d5a3c..2ea7f13f2a 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,49 @@ # example-app-next +## 0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/frontend-app-api@0.9.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-import@0.12.3-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-user-settings@0.8.12-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/plugin-kubernetes@0.11.14-next.0 + - @backstage/plugin-scaffolder@1.25.0-next.0 + - @backstage/plugin-api-docs@0.11.9-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/plugin-search@1.4.16-next.0 + - @backstage/plugin-home@0.7.10-next.0 + - @backstage/plugin-org@0.6.29-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.0 + - @backstage/plugin-app@0.1.0-next.0 + - @backstage/cli@0.27.1-next.0 + - @backstage/plugin-signals@0.0.10-next.0 + - @backstage/plugin-app-visualizer@0.1.10-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-unprocessed-entities@0.2.7 + - @backstage/plugin-notifications@0.3.1-next.0 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-search-common@1.2.14 + ## 0.0.14 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index fa37cce5c8..8d44e0a0b3 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.14", + "version": "0.0.15-next.0", "private": true, "repository": { "type": "git", @@ -24,6 +24,7 @@ "@backstage/frontend-plugin-api": "workspace:^", "@backstage/integration-react": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/plugin-app-visualizer": "workspace:^", "@backstage/plugin-auth-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 170decee42..618bc79ca5 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -26,8 +26,8 @@ import homePlugin, { import { coreExtensionData, createExtension, - createExtensionOverrides, ApiBlueprint, + createFrontendModule, } from '@backstage/frontend-plugin-api'; import { techdocsPlugin, @@ -45,7 +45,6 @@ import { createApiFactory, configApiRef } from '@backstage/core-plugin-api'; import { ScmAuth, ScmIntegrationsApi, - scmAuthApiRef, scmIntegrationsApiRef, } from '@backstage/integration-react'; import kubernetesPlugin from '@backstage/plugin-kubernetes/alpha'; @@ -86,7 +85,10 @@ TODO: const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, { extensions: [ // TODO: We likely also need a way to convert an entire tree similar to collectLegacyRoutes - convertLegacyPageExtension(TechDocsIndexPage), + convertLegacyPageExtension(TechDocsIndexPage, { + name: 'index', + defaultPath: '/docs', + }), convertLegacyPageExtension(TechDocsReaderPage, { defaultPath: '/docs/:namespace/:kind/:name/*', }), @@ -94,34 +96,48 @@ const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, { ], }); -const homePageExtension = createExtension({ - name: 'myhomepage', - attachTo: { id: 'page:home', input: 'props' }, - output: [coreExtensionData.reactElement, titleExtensionDataRef], - factory() { - return [ - coreExtensionData.reactElement(homePage), - titleExtensionDataRef('just a title'), - ]; - }, -}); - -const scmAuthExtension = ApiBlueprint.make({ - namespace: scmAuthApiRef.id, - params: { - factory: ScmAuth.createDefaultApiFactory(), - }, -}); - -const scmIntegrationApi = ApiBlueprint.make({ - namespace: scmIntegrationsApiRef.id, - params: { - factory: createApiFactory({ - api: scmIntegrationsApiRef, - deps: { configApi: configApiRef }, - factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), +const customHomePageModule = createFrontendModule({ + pluginId: 'home', + extensions: [ + createExtension({ + name: 'my-home-page', + attachTo: { id: 'page:home', input: 'props' }, + output: [coreExtensionData.reactElement, titleExtensionDataRef], + factory() { + return [ + coreExtensionData.reactElement(homePage), + titleExtensionDataRef('just a title'), + ]; + }, }), - }, + ], +}); + +const scmModule = createFrontendModule({ + pluginId: 'app', + extensions: [ + ApiBlueprint.make({ + name: 'scm-auth', + params: { + factory: ScmAuth.createDefaultApiFactory(), + }, + }), + ApiBlueprint.make({ + name: 'scm-integrations', + params: { + factory: createApiFactory({ + api: scmIntegrationsApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi), + }), + }, + }), + ], +}); + +const notFoundErrorPageModule = createFrontendModule({ + pluginId: 'app', + extensions: [notFoundErrorPage], }); const collectedLegacyPlugins = convertLegacyApp( @@ -139,15 +155,10 @@ const app = createApp({ appVisualizerPlugin, kubernetesPlugin, signInPageOverrides, + scmModule, + notFoundErrorPageModule, + customHomePageModule, ...collectedLegacyPlugins, - createExtensionOverrides({ - extensions: [ - homePageExtension, - scmAuthExtension, - scmIntegrationApi, - notFoundErrorPage, - ], - }), ], /* Handled through config instead */ // bindRoutes({ bind }) { diff --git a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx index f5108c6c15..458e716910 100644 --- a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx +++ b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx @@ -52,6 +52,7 @@ export function CustomNotFoundErrorPage() { } export default createComponentExtension({ + name: 'not-found-error-page', ref: coreComponentRefs.notFoundErrorPage, loader: { sync: () => CustomNotFoundErrorPage }, }); diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index f9cebb78e6..2cde44ff46 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,46 @@ # example-app +## 0.2.101-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/frontend-app-api@0.9.0-next.0 + - @backstage/plugin-catalog-import@0.12.3-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-user-settings@0.8.12-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/plugin-kubernetes@0.11.14-next.0 + - @backstage/plugin-scaffolder@1.25.0-next.0 + - @backstage/plugin-api-docs@0.11.9-next.0 + - @backstage/plugin-devtools@0.1.18-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/plugin-search@1.4.16-next.0 + - @backstage/plugin-home@0.7.10-next.0 + - @backstage/plugin-org@0.6.29-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.0 + - @backstage/cli@0.27.1-next.0 + - @backstage/plugin-signals@0.0.10-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-unprocessed-entities@0.2.7 + - @backstage/plugin-notifications@0.3.1-next.0 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-search-common@1.2.14 + ## 0.2.100 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index c27540f63e..cae6409f10 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.100", + "version": "0.2.101-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index d741dea098..ba556e8616 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -73,8 +73,7 @@ import { DelayingComponentFieldExtension } from './components/scaffolder/customS import { defaultPreviewTemplate } from './components/scaffolder/defaultPreviewTemplate'; import { searchPage } from './components/search/SearchPage'; import { providers } from './identityProviders'; -import * as plugins from './plugins'; - +import { SignalsDisplay } from '@backstage/plugin-signals'; import { techDocsPage } from './components/techdocs/TechDocsPage'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; @@ -86,7 +85,6 @@ import { NotificationsPage } from '@backstage/plugin-notifications'; const app = createApp({ apis, - plugins: Object.values(plugins), icons: { // Custom icon example alert: AlarmIcon, @@ -217,6 +215,7 @@ export default app.createRoot( <> + {routes} diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 076892c3b1..662b85702f 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/backend-app-api +## 0.10.0-next.0 + +### Minor Changes + +- 19ff127: **BREAKING**: The deprecated `identityServiceFactory` and `tokenManagerServiceFactory` have been removed. +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- cd38da8: Deprecate the `featureDiscoveryServiceFactory` in favor of using `@backstage/backend-defaults#discoveryFeatureLoader` instead. +- 7f779c7: `auth.externalAccess` should be optional in the config schema +- 51a69b5: Fix feature loaders in CJS double-default nested builds +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.9.0 ### Minor Changes diff --git a/packages/backend-app-api/api-report-alpha.md b/packages/backend-app-api/api-report-alpha.md index 8616eec29a..163cc325a1 100644 --- a/packages/backend-app-api/api-report-alpha.md +++ b/packages/backend-app-api/api-report-alpha.md @@ -4,14 +4,13 @@ ```ts import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; -// @alpha (undocumented) -export const featureDiscoveryServiceFactory: ServiceFactoryCompat< +// @alpha @deprecated (undocumented) +export const featureDiscoveryServiceFactory: ServiceFactory< FeatureDiscoveryService, 'root', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-app-api/api-report.md b/packages/backend-app-api/api-report.md index c7b47cb1a0..88fa34e6e4 100644 --- a/packages/backend-app-api/api-report.md +++ b/packages/backend-app-api/api-report.md @@ -4,10 +4,7 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { IdentityService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; -import { TokenManagerService } from '@backstage/backend-plugin-api'; // @public (undocumented) export interface Backend { @@ -35,26 +32,4 @@ export interface CreateSpecializedBackendOptions { // (undocumented) defaultServiceFactories: ServiceFactory[]; } - -// @public @deprecated -export type IdentityFactoryOptions = { - issuer?: string; - algorithms?: string[]; -}; - -// @public @deprecated (undocumented) -export const identityServiceFactory: ServiceFactoryCompat< - IdentityService, - 'plugin', - 'singleton', - IdentityFactoryOptions ->; - -// @public @deprecated (undocumented) -export const tokenManagerServiceFactory: ServiceFactoryCompat< - TokenManagerService, - 'plugin', - 'singleton', - undefined ->; ``` diff --git a/packages/backend-app-api/config.d.ts b/packages/backend-app-api/config.d.ts index 76d22db8b2..b5e56af67c 100644 --- a/packages/backend-app-api/config.d.ts +++ b/packages/backend-app-api/config.d.ts @@ -16,297 +16,12 @@ export interface Config { backend?: { - auth?: { - /** - * This disables the otherwise default auth policy, which requires all - * requests to be authenticated with either user or service credentials. - * - * Disabling this check means that the backend will no longer block - * unauthenticated requests, but instead allow them to pass through to - * plugins. - * - * If permissions are enabled, unauthenticated requests will be treated - * exactly as such, leaving it to the permission policy to determine what - * permissions should be allowed for an unauthenticated identity. Note - * that this will also apply to service-to-service calls between plugins - * unless you configure credentials for service calls. - */ - dangerouslyDisableDefaultAuthPolicy?: boolean; - - /** Controls how to store keys for plugin-to-plugin auth */ - pluginKeyStore?: - | { type: 'database' } - | { - type: 'static'; - static: { - /** - * Must be declared at least once and the first one will be used for signing. - */ - keys: Array<{ - /** - * Path to the public key file in the SPKI format. Should be an absolute path. - */ - publicKeyFile: string; - /** - * Path to the matching private key file in the PKCS#8 format. Should be an absolute path. - * - * The first array entry must specify a private key file, the rest must not. - */ - privateKeyFile?: string; - /** - * ID to uniquely identify this key within the JWK set. - */ - keyId: string; - /** - * JWS "alg" (Algorithm) Header Parameter value. Defaults to ES256. - * Must match the algorithm used to generate the keys in the provided files - */ - algorithm?: string; - }>; - }; - }; - - /** - * Configures methods of external access, ie ways for callers outside of - * the Backstage ecosystem to get authorized for access to APIs that do - * not permit unauthorized access. - */ - externalAccess?: Array< - | { - /** - * This is the legacy service-to-service access method, where a set - * of static keys were shared among plugins and used for symmetric - * signing and verification. These correspond to the old - * `backend.auth.keys` set and retain their behavior for backwards - * compatibility. Please migrate to other access methods when - * possible. - * - * Callers generate JWT tokens with the following payload: - * - * ```json - * { - * "sub": "backstage-plugin", - * "exp": - * } - * ``` - * - * And sign them with HS256, using the base64 decoded secret. The - * tokens are then passed along with requests in the Authorization - * header: - * - * ``` - * Authorization: Bearer eyJhbGciOiJIUzI... - * ``` - */ - type: 'legacy'; - options: { - /** - * Any set of base64 encoded random bytes to be used as both the - * signing and verification key. Should be sufficiently long so as - * not to be easy to guess by brute force. - * - * Can be generated eg using - * - * ```sh - * node -p 'require("crypto").randomBytes(24).toString("base64")' - * ``` - * - * @visibility secret - */ - secret: string; - - /** - * Sets the subject of the principal, when matching this token. - * Useful for debugging and tracking purposes. - */ - subject: string; - }; - /** - * Restricts what types of access that are permitted for this access - * method. If no access restrictions are given, it'll have unlimited - * access. This access restriction applies for the framework level; - * individual plugins may have their own access control mechanisms - * on top of this. - */ - accessRestrictions?: Array<{ - /** - * Permit access to make requests to this plugin. - * - * Can be further refined by setting additional fields below. - */ - plugin: string; - /** - * If given, this method is limited to only performing actions - * with these named permissions in this plugin. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permission?: string | Array; - /** - * If given, this method is limited to only performing actions - * whose permissions have these attributes. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permissionAttribute?: { - /** - * One of more of 'create', 'read', 'update', or 'delete'. - */ - action?: string | Array; - }; - }>; - } - | { - /** - * This access method consists of random static tokens that can be - * handed out to callers. - * - * The tokens are then passed along verbatim with requests in the - * Authorization header: - * - * ``` - * Authorization: Bearer eZv5o+fW3KnR3kVabMW4ZcDNLPl8nmMW - * ``` - */ - type: 'static'; - options: { - /** - * A raw token that can be any string, but for security reasons - * should be sufficiently long so as not to be easy to guess by - * brute force. - * - * Can be generated eg using - * - * ```sh - * node -p 'require("crypto").randomBytes(24).toString("base64")' - * ``` - * - * Since the tokens can be any string, you are free to add - * additional identifying data to them if you like. For example, - * adding a `freben-local-dev-` prefix for debugging purposes to a - * token that you know will be handed out for use as a personal - * access token during development. - * - * @visibility secret - */ - token: string; - - /** - * Sets the subject of the principal, when matching this token. - * Useful for debugging and tracking purposes. - */ - subject: string; - }; - /** - * Restricts what types of access that are permitted for this access - * method. If no access restrictions are given, it'll have unlimited - * access. This access restriction applies for the framework level; - * individual plugins may have their own access control mechanisms - * on top of this. - */ - accessRestrictions?: Array<{ - /** - * Permit access to make requests to this plugin. - * - * Can be further refined by setting additional fields below. - */ - plugin: string; - /** - * If given, this method is limited to only performing actions - * with these named permissions in this plugin. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permission?: string | Array; - /** - * If given, this method is limited to only performing actions - * whose permissions have these attributes. - * - * Note that this only applies where permissions checks are - * enabled in the first place. Endpoints that are not protected by - * the permissions system at all, are not affected by this - * setting. - */ - permissionAttribute?: { - /** - * One of more of 'create', 'read', 'update', or 'delete'. - */ - action?: string | Array; - }; - }>; - } - | { - /** - * This access method consists of a JWKS endpoint that can be used to - * verify JWT tokens. - * - * Callers generate JWT tokens via 3rd party tooling - * and pass them in the Authorization header: - * - * ``` - * Authorization: Bearer eZv5o+fW3KnR3kVabMW4ZcDNLPl8nmMW - * ``` - */ - type: 'jwks'; - options: { - /** - * The full URL of the JWKS endpoint. - */ - url: string; - /** - * Sets the algorithm(s) that should be used to verify the JWT tokens. - * The passed JWTs must have been signed using one of the listed algorithms. - */ - algorithm?: string | string[]; - /** - * Sets the issuer(s) that should be used to verify the JWT tokens. - * Passed JWTs must have an `iss` claim which matches one of the specified issuers. - */ - issuer?: string | string[]; - /** - * Sets the audience(s) that should be used to verify the JWT tokens. - * The passed JWTs must have an "aud" claim that matches one of the audiences specified, - * or have no audience specified. - */ - audience?: string | string[]; - /** - * Sets an optional subject prefix. Passes the subject to called plugins. - * Useful for debugging and tracking purposes. - */ - subjectPrefix?: string; - }; - } - >; - }; - packages?: 'all' | { include?: string[]; exclude?: string[] }; - }; - - /** Discovery options. */ - discovery?: { - /** - * Endpoints - * - * A list of target baseUrls and the associated plugins. - */ - endpoints: { - /** - * The target baseUrl to use for the plugin - * - * Can be either a string or an object with internal and external keys. - * Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId. - */ - target: string | { internal: string; external: string }; - /** Array of plugins which use the target baseUrl. */ - plugins: string[]; - }[]; + /** Used by the feature discovery service */ + packages?: + | 'all' + | { + include?: string[]; + exclude?: string[]; + }; }; } diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 27cbb0b916..8168bae97f 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-app-api", - "version": "0.9.0", + "version": "0.10.0-next.0", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" @@ -53,7 +53,6 @@ "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/cli-common": "workspace:^", - "@backstage/cli-node": "workspace:^", "@backstage/config": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", @@ -66,7 +65,6 @@ "cors": "^2.8.5", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "fs-extra": "^11.2.0", "helmet": "^6.0.0", "jose": "^5.0.0", "knex": "^3.0.0", @@ -91,7 +89,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/compression": "^1.7.0", - "@types/fs-extra": "^11.0.0", "@types/http-errors": "^2.0.0", "@types/minimist": "^1.2.0", "@types/morgan": "^1.9.0", diff --git a/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts index 254b6a47f1..6550a6b1d0 100644 --- a/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts +++ b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts @@ -147,7 +147,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { packages: 'all' } }, }), @@ -166,7 +166,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -195,7 +195,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -220,7 +220,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -245,7 +245,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -270,7 +270,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -300,7 +300,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { packages: {} } }, }), @@ -316,7 +316,7 @@ describe('featureDiscoveryServiceFactory', () => { await startTestBackend({ features: [ mock.factory, - featureDiscoveryServiceFactory(), + featureDiscoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: {} }, }), diff --git a/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts index c16a3f6534..05de659083 100644 --- a/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts +++ b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts @@ -15,145 +15,17 @@ */ import { - BackendFeature, - RootConfigService, - RootLoggerService, coreServices, createServiceFactory, } from '@backstage/backend-plugin-api'; -import { - featureDiscoveryServiceRef, - FeatureDiscoveryService, -} from '@backstage/backend-plugin-api/alpha'; -import { resolve as resolvePath, dirname } from 'path'; -import fs from 'fs-extra'; -import { BackstagePackageJson } from '@backstage/cli-node'; +import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { PackageDiscoveryService } from '../../../backend-defaults/src/PackageDiscoveryService'; -const DETECTED_PACKAGE_ROLES = [ - 'node-library', - 'backend', - 'backend-plugin', - 'backend-plugin-module', -]; - -/** @internal */ -async function findClosestPackageDir( - searchDir: string, -): Promise { - let path = searchDir; - - // Some confidence check to avoid infinite loop - for (let i = 0; i < 1000; i++) { - const packagePath = resolvePath(path, 'package.json'); - const exists = await fs.pathExists(packagePath); - if (exists) { - return path; - } - - const newPath = dirname(path); - if (newPath === path) { - return undefined; - } - path = newPath; - } - - throw new Error( - `Iteration limit reached when searching for root package.json at ${searchDir}`, - ); -} - -/** @internal */ -class PackageDiscoveryService implements FeatureDiscoveryService { - constructor( - private readonly config: RootConfigService, - private readonly logger: RootLoggerService, - ) {} - - getDependencyNames(path: string) { - const { dependencies } = require(path) as BackstagePackageJson; - const packagesConfig = this.config.getOptional('backend.packages'); - - const dependencyNames = Object.keys(dependencies || {}); - - if (packagesConfig === 'all') { - return dependencyNames; - } - - const includedPackagesConfig = this.config.getOptionalStringArray( - 'backend.packages.include', - ); - - const includedPackages = includedPackagesConfig - ? new Set(includedPackagesConfig) - : dependencyNames; - const excludedPackagesSet = new Set( - this.config.getOptionalStringArray('backend.packages.exclude'), - ); - - return [...includedPackages].filter(name => !excludedPackagesSet.has(name)); - } - - async getBackendFeatures(): Promise<{ features: Array }> { - const packagesConfig = this.config.getOptional('backend.packages'); - if (!packagesConfig || Object.keys(packagesConfig).length === 0) { - return { features: [] }; - } - - const packageDir = await findClosestPackageDir(process.argv[1]); - if (!packageDir) { - throw new Error('Package discovery failed to find package.json'); - } - const dependencyNames = this.getDependencyNames( - resolvePath(packageDir, 'package.json'), - ); - - const features: BackendFeature[] = []; - - for (const name of dependencyNames) { - const depPkg = require(require.resolve(`${name}/package.json`, { - paths: [packageDir], - })) as BackstagePackageJson; - if ( - !depPkg?.backstage?.role || - !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role) - ) { - continue; // Not a backstage backend package, ignore - } - - const exportedModulePaths = [ - require.resolve(name, { - paths: [packageDir], - }), - ]; - - // Find modules exported as alpha - try { - exportedModulePaths.push( - require.resolve(`${name}/alpha`, { paths: [packageDir] }), - ); - } catch { - /* ignore */ - } - - for (const modulePath of exportedModulePaths) { - const mod = require(modulePath); - - if (isBackendFeature(mod.default)) { - this.logger.info(`Detected: ${name}`); - features.push(mod.default); - } - if (isBackendFeatureFactory(mod.default)) { - this.logger.info(`Detected: ${name}`); - features.push(mod.default()); - } - } - } - - return { features }; - } -} - -/** @alpha */ +/** + * @alpha + * @deprecated The `featureDiscoveryServiceFactory` is deprecated in favor of using {@link @backstage/backend-defaults#discoveryFeatureLoader} instead. + */ export const featureDiscoveryServiceFactory = createServiceFactory({ service: featureDiscoveryServiceRef, deps: { @@ -164,21 +36,3 @@ export const featureDiscoveryServiceFactory = createServiceFactory({ return new PackageDiscoveryService(config, logger); }, }); - -function isBackendFeature(value: unknown): value is BackendFeature { - return ( - !!value && - ['object', 'function'].includes(typeof value) && - (value as BackendFeature).$$type === '@backstage/BackendFeature' - ); -} - -function isBackendFeatureFactory( - value: unknown, -): value is () => BackendFeature { - return ( - !!value && - typeof value === 'function' && - (value as any).$$type === '@backstage/BackendFeatureFactory' - ); -} diff --git a/packages/backend-app-api/src/index.ts b/packages/backend-app-api/src/index.ts index 02633f3732..c58379c3e4 100644 --- a/packages/backend-app-api/src/index.ts +++ b/packages/backend-app-api/src/index.ts @@ -21,4 +21,3 @@ */ export * from './wiring'; -export * from './services/implementations'; diff --git a/packages/backend-app-api/src/services/implementations/identity/identityServiceFactory.ts b/packages/backend-app-api/src/services/implementations/identity/identityServiceFactory.ts deleted file mode 100644 index c2f1a61007..0000000000 --- a/packages/backend-app-api/src/services/implementations/identity/identityServiceFactory.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { - coreServices, - createServiceFactory, -} from '@backstage/backend-plugin-api'; -import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; - -/** - * An identity client options object which allows extra configurations - * - * @public - * @deprecated Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead - */ -export type IdentityFactoryOptions = { - issuer?: string; - - /** - * JWS "alg" (Algorithm) Header Parameter values. Defaults to an array containing just ES256. - * More info on supported algorithms: https://github.com/panva/jose - */ - algorithms?: string[]; -}; - -/** - * @public - * @deprecated Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead - */ -export const identityServiceFactory = createServiceFactory( - (options?: IdentityFactoryOptions) => ({ - service: coreServices.identity, - deps: { - discovery: coreServices.discovery, - }, - async factory({ discovery }) { - return DefaultIdentityClient.create({ discovery, ...options }); - }, - }), -); diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.test.ts b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.test.ts deleted file mode 100644 index dc52130ec6..0000000000 --- a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 { tokenManagerServiceFactory } from './tokenManagerServiceFactory'; -import { ServiceFactoryTester } from '@backstage/backend-test-utils'; - -describe('tokenManagerFactory', () => { - it('should create a disabled manager without configuration', async () => { - const tokenManager = await ServiceFactoryTester.from( - tokenManagerServiceFactory, - ).getSubject(); - - await expect(tokenManager.authenticate('abc')).rejects.toThrow( - 'no legacy keys are configured', - ); - await expect(tokenManager.getToken()).rejects.toThrow( - 'no legacy keys are configured', - ); - }); -}); diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts deleted file mode 100644 index e7c4ce7af0..0000000000 --- a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 { - coreServices, - createServiceFactory, -} from '@backstage/backend-plugin-api'; -import { ServerTokenManager } from '@backstage/backend-common'; - -/** - * @public - * @deprecated Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead - */ -export const tokenManagerServiceFactory = createServiceFactory({ - service: coreServices.tokenManager, - deps: { - config: coreServices.rootConfig, - logger: coreServices.rootLogger, - }, - createRootContext({ config, logger }) { - return ServerTokenManager.fromConfig(config, { - logger, - allowDisabledTokenManager: true, - }); - }, - async factory(_deps, tokenManager) { - return tokenManager; - }, -}); diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index cf8845fcec..b524301f21 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -39,14 +39,14 @@ class MockLogger { } const baseFactories = [ - lifecycleServiceFactory(), - rootLifecycleServiceFactory(), + lifecycleServiceFactory, + rootLifecycleServiceFactory, createServiceFactory({ service: coreServices.rootLogger, deps: {}, factory: () => new MockLogger(), - })(), - loggerServiceFactory(), + }), + loggerServiceFactory, ]; const testPlugin = createBackendPlugin({ @@ -57,7 +57,7 @@ const testPlugin = createBackendPlugin({ async init() {}, }); }, -})(); +}); describe('BackendInitializer', () => { it('should initialize root scoped services', async () => { @@ -84,18 +84,18 @@ describe('BackendInitializer', () => { initialization: 'always', deps: {}, factory: factory1, - })(), + }), createServiceFactory({ service: ref2, deps: {}, factory: factory2, - })(), + }), createServiceFactory({ service: ref3, initialization: 'lazy', deps: {}, factory: factory3, - })(), + }), ]; const init = new BackendInitializer(services); @@ -249,18 +249,18 @@ describe('BackendInitializer', () => { initialization: 'always', deps: {}, factory: factory1, - })(), + }), createServiceFactory({ service: ref2, deps: {}, factory: factory2, - })(), + }), createServiceFactory({ service: ref3, initialization: 'lazy', deps: {}, factory: factory3, - })(), + }), ]; const init = new BackendInitializer(services); @@ -505,12 +505,12 @@ describe('BackendInitializer', () => { const extA = createExtensionPoint({ id: 'a' }); const extB = createExtensionPoint({ id: 'b' }); const init = new BackendInitializer([ - rootLifecycleServiceFactory(), + rootLifecycleServiceFactory, createServiceFactory({ service: coreServices.rootLogger, deps: {}, factory: () => new MockLogger(), - })(), + }), ]); init.add(testPlugin); init.add( @@ -576,4 +576,109 @@ describe('BackendInitializer', () => { "Illegal dependency: Module 'mod' for plugin 'test' attempted to depend on extension point 'a' for plugin 'test-a'. Extension points can only be used within their plugin's scope.", ); }); + + it('should reject plugins with missing dependencies', async () => { + const init = new BackendInitializer(baseFactories); + const ref = createServiceRef({ id: 'a' }); + init.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: { ref }, + async init() {}, + }); + }, + }), + ); + await expect(init.start()).rejects.toThrow( + "Service or extension point dependencies of plugin 'test' are missing for the following ref(s): serviceRef{a}", + ); + }); + + it('should reject modules with missing dependencies', async () => { + const init = new BackendInitializer(baseFactories); + const ref = createServiceRef({ id: 'a' }); + init.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ); + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'test-mod', + register(reg) { + reg.registerInit({ + deps: { ref }, + async init() {}, + }); + }, + }), + ); + await expect(init.start()).rejects.toThrow( + "Service or extension point dependencies of module 'test-mod' for plugin 'test' are missing for the following ref(s): serviceRef{a}", + ); + }); + + it('should properly load double-default CJS modules', async () => { + expect.assertions(3); + + const init = new BackendInitializer(baseFactories); + init.add( + createBackendFeatureLoader({ + loader() { + return [ + createBackendPlugin({ + pluginId: 'no-double-wrapping', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + expect(true).toBeTruthy(); + }, + }); + }, + }), + { + default: createBackendPlugin({ + pluginId: 'single-wrapping', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + expect(true).toBeTruthy(); + }, + }); + }, + }), + }, + { + default: { + default: createBackendPlugin({ + pluginId: 'double-wrapping', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + expect(true).toBeTruthy(); + }, + }); + }, + }), + }, + } as any, // not typescript valid, but can happen at runtime + ]; + }, + }), + ); + + await init.start(); + }); }); diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index c0da9eda62..7e29276a77 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -38,6 +38,7 @@ import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha' import { DependencyGraph } from '../lib/DependencyGraph'; import { ServiceRegistry } from './ServiceRegistry'; import { createInitializationLogger } from './createInitializationLogger'; +import { unwrapFeature } from './helpers'; export interface BackendRegisterInit { consumes: Set; @@ -92,8 +93,11 @@ export class BackendInitializer { if (missingRefs.size > 0) { const missing = Array.from(missingRefs).join(', '); + const target = moduleId + ? `module '${moduleId}' for plugin '${pluginId}'` + : `plugin '${pluginId}'`; throw new Error( - `No extension point or service available for the following ref(s): ${missing}`, + `Service or extension point dependencies of ${target} are missing for the following ref(s): ${missing}`, ); } @@ -156,6 +160,7 @@ export class BackendInitializer { } const featureDiscovery = await this.#serviceRegistry.get( + // TODO: Let's leave this in place and remove it once the deprecated service is removed. We can do that post-1.0 since it's alpha featureDiscoveryServiceRef, 'root', ); @@ -163,7 +168,7 @@ export class BackendInitializer { if (featureDiscovery) { const { features } = await featureDiscovery.getBackendFeatures(); for (const feature of features) { - this.#addFeature(feature); + this.#addFeature(unwrapFeature(feature)); } this.#serviceRegistry.checkForCircularDeps(); } @@ -417,6 +422,7 @@ export class BackendInitializer { const result = await loader .loader(Object.fromEntries(deps)) + .then(features => features.map(unwrapFeature)) .catch(error => { throw new ForwardedError( `Feature loader ${loader.description} failed`, diff --git a/packages/backend-app-api/src/wiring/BackstageBackend.ts b/packages/backend-app-api/src/wiring/BackstageBackend.ts index 9df7549bcb..49a8bbbbfb 100644 --- a/packages/backend-app-api/src/wiring/BackstageBackend.ts +++ b/packages/backend-app-api/src/wiring/BackstageBackend.ts @@ -16,6 +16,7 @@ import { BackendFeature, ServiceFactory } from '@backstage/backend-plugin-api'; import { BackendInitializer } from './BackendInitializer'; +import { unwrapFeature } from './helpers'; import { Backend } from './types'; export class BackstageBackend implements Backend { @@ -50,21 +51,3 @@ function isPromise(value: unknown | Promise): value is Promise { typeof value.then === 'function' ); } - -function unwrapFeature( - feature: BackendFeature | { default: BackendFeature }, -): BackendFeature { - if ('$$type' in feature) { - return feature; - } - - // This is a workaround where default exports get transpiled to `exports['default'] = ...` - // in CommonJS modules, which in turn results in a double `{ default: { default: ... } }` nesting - // when importing using a dynamic import. - // TODO: This is a broader issue than just this piece of code, and should move away from CommonJS. - if ('default' in feature) { - return feature.default; - } - - return feature; -} diff --git a/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts b/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts index 32807ca826..90ee6344e5 100644 --- a/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts +++ b/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts @@ -95,7 +95,7 @@ describe('ServiceRegistry', () => { }); it('should return an implementation for a registered ref', async () => { - const registry = ServiceRegistry.create([sf1()]); + const registry = ServiceRegistry.create([sf1]); await expect(registry.get(ref1, 'catalog')).resolves.toEqual({ x: 1 }); await expect(registry.get(ref1, 'scaffolder')).resolves.toEqual({ x: 1 }); expect(await registry.get(ref1, 'catalog')).toBe( @@ -110,7 +110,7 @@ describe('ServiceRegistry', () => { }); it('should handle multiple factories with different serviceRefs', async () => { - const registry = ServiceRegistry.create([sf1(), sf2()]); + const registry = ServiceRegistry.create([sf1, sf2]); await expect(registry.get(ref1, 'catalog')).resolves.toEqual({ x: 1, @@ -124,15 +124,15 @@ describe('ServiceRegistry', () => { }); it('should not be possible for root scoped services to depend on plugin scoped services', async () => { + // @ts-expect-error const factory = createServiceFactory({ - // @ts-expect-error service: ref2, deps: { pluginDep: ref1 }, async factory() { return { x: 2 }; }, }); - const registry = ServiceRegistry.create([factory(), sf1()]); + const registry = ServiceRegistry.create([factory, sf1]); await expect(registry.get(ref2, 'catalog')).rejects.toThrow( "Failed to instantiate 'root' scoped service '2' because it depends on 'plugin' scoped service '1'.", ); @@ -146,7 +146,7 @@ describe('ServiceRegistry', () => { return { x: rootDep.x }; }, }); - const registry = ServiceRegistry.create([factory(), sf2()]); + const registry = ServiceRegistry.create([factory, sf2]); await expect(registry.get(ref1, 'catalog')).resolves.toEqual({ x: 2, }); @@ -161,7 +161,7 @@ describe('ServiceRegistry', () => { return { x: rootDep.x }; }, }); - const registry = ServiceRegistry.create([factory(), sf2()]); + const registry = ServiceRegistry.create([factory, sf2]); await expect(registry.get(ref, 'catalog')).resolves.toEqual({ x: 2, }); @@ -176,41 +176,41 @@ describe('ServiceRegistry', () => { return { pluginId: meta.getId() }; }, }); - const registry = ServiceRegistry.create([factory()]); + const registry = ServiceRegistry.create([factory]); await expect(registry.get(ref, 'catalog')).resolves.toEqual({ pluginId: 'catalog', }); }); it('should use the last factory for each ref', async () => { - const registry = ServiceRegistry.create([sf2(), sf2b()]); + const registry = ServiceRegistry.create([sf2, sf2b]); await expect(registry.get(ref2, 'catalog')).resolves.toEqual({ x: 22, }); }); it('should use added service factories for each ref', async () => { - const registry = ServiceRegistry.create([sf2()]); - registry.add(sf2b()); + const registry = ServiceRegistry.create([sf2]); + registry.add(sf2b); await expect(registry.get(ref2, 'catalog')).resolves.toEqual({ x: 22, }); }); it('should not allow factories to be added after instantiation', async () => { - const registry = ServiceRegistry.create([sf2()]); + const registry = ServiceRegistry.create([sf2]); await expect(registry.get(ref2, 'catalog')).resolves.toEqual({ x: 2, }); - expect(() => registry.add(sf2b())).toThrow( + expect(() => registry.add(sf2b)).toThrow( 'Unable to set service factory with id 2, service has already been instantiated', ); }); it('should not allow the same factory to be added twice', async () => { - const registry = ServiceRegistry.create([sf2()]); - registry.add(sf2b()); - expect(() => registry.add(sf2b())).toThrow( + const registry = ServiceRegistry.create([sf2]); + registry.add(sf2b); + expect(() => registry.add(sf2b)).toThrow( 'Duplicate service implementations provided for 2', ); }); @@ -223,7 +223,7 @@ describe('ServiceRegistry', () => { }); it('should not use the defaultFactory from the ref if provided to the registry', async () => { - const registry = ServiceRegistry.create([sf1()]); + const registry = ServiceRegistry.create([sf1]); await expect(registry.get(refDefault1, 'catalog')).resolves.toEqual({ x: 1, }); @@ -279,7 +279,7 @@ describe('ServiceRegistry', () => { factory, }); - const registry = ServiceRegistry.create([myFactory()]); + const registry = ServiceRegistry.create([myFactory]); await Promise.all([ registry.get(ref1, 'catalog')!, @@ -301,7 +301,7 @@ describe('ServiceRegistry', () => { factory, }); - const registry = ServiceRegistry.create([myFactory()]); + const registry = ServiceRegistry.create([myFactory]); await Promise.all([ registry.get(ref1, 'catalog')!, @@ -323,7 +323,7 @@ describe('ServiceRegistry', () => { }, }); - const registry = ServiceRegistry.create([myFactory()]); + const registry = ServiceRegistry.create([myFactory]); await expect(registry.get(ref1, 'catalog')).rejects.toThrow( "Failed to instantiate service '1' for 'catalog' because the following dependent services are missing: '2'", @@ -350,7 +350,7 @@ describe('ServiceRegistry', () => { }, }); - const registry = ServiceRegistry.create([factoryA(), factoryB()]); + const registry = ServiceRegistry.create([factoryA, factoryB]); await expect(registry.get(refA, 'catalog')).rejects.toThrow( "Failed to instantiate service 'a' for 'catalog' because the factory function threw an error, Error: Failed to instantiate service 'b' for 'catalog' because the following dependent services are missing: 'c', 'd'", @@ -374,7 +374,7 @@ describe('ServiceRegistry', () => { factory: async ({ a }) => a, }); - expect(() => ServiceRegistry.create([factoryA(), factoryB()])).toThrow( + expect(() => ServiceRegistry.create([factoryA, factoryB])).toThrow( `Circular dependencies detected: 'a' -> 'b' -> 'a'`, ); @@ -411,12 +411,7 @@ describe('ServiceRegistry', () => { }); expect(() => - ServiceRegistry.create([ - factoryA(), - factoryB(), - factoryC(), - factoryD(), - ]), + ServiceRegistry.create([factoryA, factoryB, factoryC, factoryD]), ).toThrow( `Circular dependencies detected: 'a' -> 'b' -> 'a' @@ -448,7 +443,7 @@ describe('ServiceRegistry', () => { }); expect(() => - ServiceRegistry.create([factoryA(), factoryB(), factoryC()]), + ServiceRegistry.create([factoryA, factoryB, factoryC]), ).toThrow( `Circular dependencies detected: 'a' -> 'b' -> 'c' -> 'a'`, @@ -486,12 +481,7 @@ describe('ServiceRegistry', () => { }); expect(() => - ServiceRegistry.create([ - factoryA(), - factoryB(), - factoryC(), - factoryD(), - ]), + ServiceRegistry.create([factoryA, factoryB, factoryC, factoryD]), ).toThrow( `Circular dependencies detected: 'a' -> 'b' -> 'c' -> 'a'`, @@ -522,7 +512,7 @@ describe('ServiceRegistry', () => { }); expect(() => - ServiceRegistry.create([factoryA(), factoryB(), factoryC()]), + ServiceRegistry.create([factoryA, factoryB, factoryC]), ).toThrow( `Circular dependencies detected: 'a' -> 'c' -> 'a'`, @@ -553,7 +543,7 @@ describe('ServiceRegistry', () => { }); expect(() => - ServiceRegistry.create([factoryA(), factoryB(), factoryC()]), + ServiceRegistry.create([factoryA, factoryB, factoryC]), ).toThrow( `Circular dependencies detected: 'b' -> 'c' -> 'b'`, @@ -573,7 +563,7 @@ describe('ServiceRegistry', () => { }, }); - const registry = ServiceRegistry.create([myFactory()]); + const registry = ServiceRegistry.create([myFactory]); await expect(registry.get(ref1, 'catalog')).rejects.toThrow( "Failed to instantiate service '1' because createRootContext threw an error, Error: top-level error", @@ -589,7 +579,7 @@ describe('ServiceRegistry', () => { }, }); - const registry = ServiceRegistry.create([myFactory()]); + const registry = ServiceRegistry.create([myFactory]); await expect(registry.get(ref1, 'catalog')).rejects.toThrow( "Failed to instantiate service '1' for 'catalog' because the factory function threw an error, Error: error in plugin", diff --git a/packages/backend-app-api/src/wiring/helpers.ts b/packages/backend-app-api/src/wiring/helpers.ts new file mode 100644 index 0000000000..ffb7e3b079 --- /dev/null +++ b/packages/backend-app-api/src/wiring/helpers.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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 { BackendFeature } from '@backstage/backend-plugin-api'; + +/** @internal */ +export function unwrapFeature( + feature: BackendFeature | { default: BackendFeature }, +): BackendFeature { + if ('$$type' in feature) { + return feature; + } + + // This is a workaround where default exports get transpiled to `exports['default'] = ...` + // in CommonJS modules, which in turn results in a double `{ default: { default: ... } }` nesting + // when importing using a dynamic import. + // TODO: This is a broader issue than just this piece of code, and should move away from CommonJS. + if ('default' in feature) { + return feature.default; + } + + return feature; +} diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 40ba8fb687..3795ae2730 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/backend-common +## 0.25.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 8ba77ed: The `legacyPlugin` and `makeLegacyPlugin` helpers now provide their own shim implementation of the identity and token manager services, as these services are being removed from the new backend system. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 2e9ec14: Add `pg-format` as a dependency +- 19ff127: Internal refactor to re-declare the token manager service which was removed from `@backstage/backend-plugin-api`, but is still supported in this package for backwards compatibility. +- 66dbf0a: Allow the cache service to accept the human duration format for TTL +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + ## 0.24.0 ### Minor Changes diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 61efc0f3a1..7de2731518 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -9,7 +9,7 @@ import { AppConfig } from '@backstage/config'; import { AuthCallback } from 'isomorphic-git'; import { AuthService } from '@backstage/backend-plugin-api'; -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CacheService } from '@backstage/backend-plugin-api'; import { CacheServiceOptions } from '@backstage/backend-plugin-api'; import { CacheServiceSetOptions } from '@backstage/backend-plugin-api'; @@ -22,7 +22,6 @@ import Docker from 'dockerode'; import { ErrorRequestHandler } from 'express'; import express from 'express'; import { HttpAuthService } from '@backstage/backend-plugin-api'; -import { IdentityService } from '@backstage/backend-plugin-api'; import { isChildPath as isChildPath_2 } from '@backstage/backend-plugin-api'; import { isDatabaseConflictError as isDatabaseConflictError_2 } from '@backstage/backend-plugin-api'; import { KubeConfig } from '@kubernetes/client-node'; @@ -35,6 +34,7 @@ import { PermissionsService } from '@backstage/backend-plugin-api'; import { PluginMetadataService } from '@backstage/backend-plugin-api'; import { PushResult } from 'isomorphic-git'; import { ReadCommitResult } from 'isomorphic-git'; +import { Request as Request_2 } from 'express'; import { RequestHandler } from 'express'; import { resolvePackagePath as resolvePackagePath_2 } from '@backstage/backend-plugin-api'; import { resolveSafeChildPath as resolveSafeChildPath_2 } from '@backstage/backend-plugin-api'; @@ -43,7 +43,6 @@ import { Router } from 'express'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { Server } from 'http'; import { ServiceRef } from '@backstage/backend-plugin-api'; -import { TokenManagerService } from '@backstage/backend-plugin-api'; import { TransportStreamOptions } from 'winston-transport'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { UserInfoService } from '@backstage/backend-plugin-api'; @@ -100,7 +99,7 @@ export function createLegacyAuthAdapters< auth?: AuthService; httpAuth?: HttpAuthService; userInfo?: UserInfoService; - identity?: IdentityService; + identity?: LegacyIdentityService; tokenManager?: TokenManager; discovery: PluginEndpointDiscovery; }, @@ -321,6 +320,23 @@ export type KubernetesContainerRunnerOptions = { // @public @deprecated (undocumented) export type LegacyCreateRouter = (deps: TEnv) => Promise; +// @public @deprecated +export interface LegacyIdentityService { + // (undocumented) + getIdentity(options: { request: Request_2 }): Promise< + | { + expiresInSeconds?: number; + token: string; + identity: { + type: 'user'; + userEntityRef: string; + ownershipEntityRefs: string[]; + }; + } + | undefined + >; +} + // @public @deprecated export const legacyPlugin: ( name: string, @@ -335,9 +351,7 @@ export const legacyPlugin: ( logger: LoggerService; permissions: PermissionsService; scheduler: SchedulerService; - tokenManager: TokenManagerService; reader: UrlReaderService; - identity: IdentityService; }, { logger: (log: LoggerService) => Logger; @@ -345,10 +359,13 @@ export const legacyPlugin: ( getClient(options?: CacheServiceOptions | undefined): CacheService; }; } - > + > & { + tokenManager: TokenManager; + identity: LegacyIdentityService; + } >; }>, -) => BackendFeatureCompat; +) => BackendFeature; // Warning: (ae-forgotten-export) The symbol "LegacyRootDatabaseService_2" needs to be exported by the entry point index.d.ts // @@ -384,9 +401,14 @@ export function makeLegacyPlugin< ): ( name: string, createRouterImport: Promise<{ - default: LegacyCreateRouter>; + default: LegacyCreateRouter< + TransformedEnv & { + tokenManager: TokenManager; + identity: LegacyIdentityService; + } + >; }>, -) => BackendFeatureCompat; +) => BackendFeature; // @public @deprecated export function notFoundHandler(): RequestHandler; @@ -523,7 +545,12 @@ export interface StatusCheckHandlerOptions { } // @public @deprecated (undocumented) -export type TokenManager = TokenManagerService; +export interface TokenManager { + authenticate(token: string): Promise; + getToken(): Promise<{ + token: string; + }>; +} // @public @deprecated export function useHotCleanup( diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index ed253dd54d..6146adfe83 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { HumanDuration } from '@backstage/types'; + export interface Config { app: { baseUrl: string; // defined in core, but repeated here without doc @@ -180,7 +182,7 @@ export interface Config { | { store: 'memory'; /** An optional default TTL (in milliseconds). */ - defaultTtl?: number; + defaultTtl?: number | HumanDuration; } | { store: 'redis'; @@ -190,7 +192,7 @@ export interface Config { */ connection: string; /** An optional default TTL (in milliseconds). */ - defaultTtl?: number; + defaultTtl?: number | HumanDuration; /** * Whether or not [useRedisSets](https://github.com/jaredwray/keyv/tree/main/packages/redis#useredissets) should be configured to this redis cache. * Defaults to true if unspecified. @@ -205,9 +207,12 @@ export interface Config { */ connection: string; /** An optional default TTL (in milliseconds). */ - defaultTtl?: number; + defaultTtl?: number | HumanDuration; }; + /** + * Properties returned upon CORS requests to the backend, including the app-backend. + */ cors?: { origin?: string | string[]; methods?: string | string[]; @@ -219,6 +224,16 @@ export interface Config { optionsSuccessStatus?: number; }; + /** + * Content Security Policy options. + * + * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The + * values are on the format that the helmet library expects them, as an + * array of strings. There is also the special value false, which means to + * remove the default value that Backstage puts in place for that policy. + */ + csp?: { [policyId: string]: string[] | false }; + /** * Configuration related to URL reading, used for example for reading catalog info * files, scaffolder templates, and techdocs content. @@ -246,15 +261,5 @@ export interface Config { paths?: string[]; }>; }; - - /** - * Content Security Policy options. - * - * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The - * values are on the format that the helmet library expects them, as an - * array of strings. There is also the special value false, which means to - * remove the default value that Backstage puts in place for that policy. - */ - csp?: { [policyId: string]: string[] | false }; }; } diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index ce26958aaf..74fa058cc0 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-common", - "version": "0.24.0", + "version": "0.25.0-next.0", "description": "Common functionality library for Backstage backends", "backstage": { "role": "node-library" @@ -107,6 +107,7 @@ "p-limit": "^3.1.0", "path-to-regexp": "^6.2.1", "pg": "^8.11.3", + "pg-format": "^1.0.4", "raw-body": "^2.4.1", "selfsigned": "^2.0.0", "stoppable": "^1.1.0", diff --git a/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.test.ts b/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.test.ts index 354b4a57dc..49494761bf 100644 --- a/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.test.ts +++ b/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.test.ts @@ -14,18 +14,28 @@ * limitations under the License. */ -import { mockServices } from '@backstage/backend-test-utils'; import { createLegacyAuthAdapters } from './createLegacyAuthAdapters'; import { Request } from 'express'; +import { TokenManager } from '../../deprecated'; + +const mockTokenManager: TokenManager = { + async getToken(): Promise<{ token: string }> { + return { token: 'mock-token' }; + }, + async authenticate(token: string): Promise { + if (token !== 'mock-token') { + throw new Error('Invalid token'); + } + }, +}; describe('createLegacyAuthAdapters', () => { it('should pass through auth if only auth is provided', () => { const auth = {}; const ret = createLegacyAuthAdapters({ auth: auth as any, - tokenManager: mockServices.tokenManager(), + tokenManager: mockTokenManager, discovery: {} as any, - identity: mockServices.identity(), }); expect(ret.auth).toBe(auth); @@ -35,9 +45,8 @@ describe('createLegacyAuthAdapters', () => { const httpAuth = {}; const ret = createLegacyAuthAdapters({ httpAuth: httpAuth as any, - tokenManager: mockServices.tokenManager(), + tokenManager: mockTokenManager, discovery: {} as any, - identity: mockServices.identity(), }); expect(ret.httpAuth).toBe(httpAuth); @@ -49,9 +58,8 @@ describe('createLegacyAuthAdapters', () => { const ret = createLegacyAuthAdapters({ auth: auth as any, httpAuth: httpAuth as any, - tokenManager: mockServices.tokenManager(), + tokenManager: mockTokenManager, discovery: {} as any, - identity: mockServices.identity(), }); expect(ret.auth).toBe(auth); @@ -64,9 +72,8 @@ describe('createLegacyAuthAdapters', () => { const ret = createLegacyAuthAdapters({ auth: auth as any, userInfo: userInfo as any, - tokenManager: mockServices.tokenManager(), + tokenManager: mockTokenManager, discovery: {} as any, - identity: mockServices.identity(), }); expect(ret.auth).toBe(auth); @@ -77,9 +84,8 @@ describe('createLegacyAuthAdapters', () => { const ret = createLegacyAuthAdapters({ auth: undefined, httpAuth: undefined, - tokenManager: mockServices.tokenManager(), + tokenManager: mockTokenManager, discovery: {} as any, - identity: mockServices.identity(), }); expect(ret).toEqual({ @@ -94,7 +100,6 @@ describe('createLegacyAuthAdapters', () => { auth: undefined, httpAuth: undefined, discovery: {} as any, - identity: mockServices.identity(), }); const credentials = await httpAuth.credentials({ @@ -116,13 +121,12 @@ describe('createLegacyAuthAdapters', () => { auth: undefined, httpAuth: undefined, tokenManager: { - ...mockServices.tokenManager(), + ...mockTokenManager, async getToken() { return { token: 'new-token' }; }, }, discovery: {} as any, - identity: mockServices.identity(), }); const credentials = await httpAuth.credentials({ diff --git a/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.ts b/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.ts index 15cd881649..fc02d936d1 100644 --- a/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.ts +++ b/packages/backend-common/src/compat/auth/createLegacyAuthAdapters.ts @@ -23,8 +23,6 @@ import { BackstageUserInfo, BackstageUserPrincipal, HttpAuthService, - IdentityService, - TokenManagerService, UserInfoService, } from '@backstage/backend-plugin-api'; import { AuthenticationError, NotAllowedError } from '@backstage/errors'; @@ -44,11 +42,12 @@ import { import { decodeJwt } from 'jose'; import { TokenManager, PluginEndpointDiscovery } from '../../deprecated'; import { JsonObject } from '@backstage/types'; +import { LegacyIdentityService } from '../legacy'; class AuthCompat implements AuthService { constructor( - private readonly identity: IdentityService, - private readonly tokenManager?: TokenManagerService, + private readonly identity: LegacyIdentityService, + private readonly tokenManager?: TokenManager, ) {} isPrincipal( @@ -299,7 +298,7 @@ export function createLegacyAuthAdapters< auth?: AuthService; httpAuth?: HttpAuthService; userInfo?: UserInfoService; - identity?: IdentityService; + identity?: LegacyIdentityService; tokenManager?: TokenManager; discovery: PluginEndpointDiscovery; }, diff --git a/packages/backend-common/src/compat/legacy/index.ts b/packages/backend-common/src/compat/legacy/index.ts index 1172084cd7..7e0646342f 100644 --- a/packages/backend-common/src/compat/legacy/index.ts +++ b/packages/backend-common/src/compat/legacy/index.ts @@ -15,4 +15,4 @@ */ export { legacyPlugin, makeLegacyPlugin } from './legacy'; -export type { LegacyCreateRouter } from './legacy'; +export type { LegacyCreateRouter, LegacyIdentityService } from './legacy'; diff --git a/packages/backend-common/src/compat/legacy/legacy.test.ts b/packages/backend-common/src/compat/legacy/legacy.test.ts index 08a645f660..b7a5ff1a78 100644 --- a/packages/backend-common/src/compat/legacy/legacy.test.ts +++ b/packages/backend-common/src/compat/legacy/legacy.test.ts @@ -18,15 +18,18 @@ import { coreServices, createBackendPlugin, } from '@backstage/backend-plugin-api'; -import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { + mockCredentials, + mockServices, + startTestBackend, +} from '@backstage/backend-test-utils'; import { EventEmitter } from 'events'; import { Router } from 'express'; +import request from 'supertest'; import { createLegacyAuthAdapters } from '..'; import { legacyPlugin } from './legacy'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { authServiceFactory } from '../../../../backend-defaults/src/entrypoints/auth'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { tokenManagerServiceFactory } from '../../../../backend-app-api/src/services/implementations/tokenManager'; describe('legacyPlugin', () => { it('can auth across the new and old systems', async () => { @@ -41,7 +44,6 @@ describe('legacyPlugin', () => { await startTestBackend({ features: [ authServiceFactory, - tokenManagerServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -115,4 +117,54 @@ describe('legacyPlugin', () => { await done; }); + + it('can auth users with the identity service shim', async () => { + const backend = await startTestBackend({ + features: [ + mockServices.rootConfig.factory({ + data: { + backend: { + auth: { + keys: [ + { + secret: 'test', + }, + ], + }, + }, + }, + }), + legacyPlugin( + 'test', + Promise.resolve({ + async default({ identity }) { + const router = Router(); + + router.get('/', async (req, res) => { + const user = await identity.getIdentity({ request: req }); + res.json(user); + }); + + return router; + }, + }), + ), + ], + }); + + const res = await request(backend.server) + .get('/api/test') + .set('authorization', mockCredentials.user.header()); + + const mockUserRef = mockCredentials.user().principal.userEntityRef; + expect(res.status).toBe(200); + expect(res.body).toEqual({ + token: mockCredentials.user.token(), + identity: { + type: 'user', + userEntityRef: mockUserRef, + ownershipEntityRefs: [mockUserRef], + }, + }); + }); }); diff --git a/packages/backend-common/src/compat/legacy/legacy.ts b/packages/backend-common/src/compat/legacy/legacy.ts index 4dc7d1a09a..bf051f3040 100644 --- a/packages/backend-common/src/compat/legacy/legacy.ts +++ b/packages/backend-common/src/compat/legacy/legacy.ts @@ -18,13 +18,16 @@ import { AuthService, coreServices, createBackendPlugin, - HttpRouterService, + LoggerService, + RootConfigService, ServiceRef, + UserInfoService, } from '@backstage/backend-plugin-api'; import { RequestHandler } from 'express'; import { cacheToPluginCacheManager } from '../cache'; import { loggerToWinstonLogger } from '../logging'; -import { TokenManager } from '../../deprecated'; +import { ServerTokenManager, TokenManager } from '../../deprecated'; +import { Request } from 'express'; /** * @public @@ -46,8 +49,13 @@ type TransformedEnv< // new plugin tokens, which we'll also be signaling by supporting the JWKS endpoint through // the http router. // This makes sure that we accept the new plugin tokens as valid tokens, but otherwise fall -// back to whatever the token manager is doing. -function wrapTokenManager(tokenManager: TokenManager, auth: AuthService) { +// back to the legacy token manager. +function createTokenManagerShim( + auth: AuthService, + config: RootConfigService, + logger: LoggerService, +): TokenManager { + const tokenManager = ServerTokenManager.fromConfig(config, { logger }); return { async getToken() { return tokenManager.getToken(); @@ -64,7 +72,64 @@ function wrapTokenManager(tokenManager: TokenManager, auth: AuthService) { } await tokenManager.authenticate(token); }, - } satisfies TokenManager; + }; +} + +/** + * Originally IdentityApi from `@backstage/plugin-auth-node`, re-declared here for backwards compatibility + * @public + * @deprecated Only relevant for legacy plugins, which are deprecated. + */ +export interface LegacyIdentityService { + getIdentity(options: { request: Request }): Promise< + | { + expiresInSeconds?: number; + + token: string; + identity: { + type: 'user'; + userEntityRef: string; + ownershipEntityRefs: string[]; + }; + } + | undefined + >; +} + +// This doesn't use DefaultIdentityClient because we will be removing it and break support for ownershipEntityRefs +function createIdentityServiceShim( + auth: AuthService, + userInfo: UserInfoService, +): LegacyIdentityService { + return { + async getIdentity(options) { + const authHeader = options.request.headers.authorization; + if (typeof authHeader !== 'string') { + return undefined; + } + + const token = authHeader.match(/^Bearer[ ]+(\S+)$/i)?.[1]; + if (!token) { + return undefined; + } + + const credentials = await auth.authenticate(token); + if (!auth.isPrincipal(credentials, 'user')) { + return undefined; + } + + const info = await userInfo.getUserInfo(credentials); + + return { + token, + identity: { + type: 'user', + userEntityRef: info.userEntityRef, + ownershipEntityRefs: info.ownershipEntityRefs, + }, + }; + }, + }; } /** @@ -87,19 +152,34 @@ export function makeLegacyPlugin< return ( name: string, createRouterImport: Promise<{ - default: LegacyCreateRouter>; + default: LegacyCreateRouter< + TransformedEnv & { + tokenManager: TokenManager; + identity: LegacyIdentityService; + } + >; }>, ) => { - const compatPlugin = createBackendPlugin({ + return createBackendPlugin({ pluginId: name, register(env) { env.registerInit({ deps: { ...envMapping, - _router: coreServices.httpRouter, - _auth: coreServices.auth, + $$router: coreServices.httpRouter, + $$auth: coreServices.auth, + $$userInfo: coreServices.userInfo, + $$config: coreServices.rootConfig, + $$logger: coreServices.logger, }, - async init({ _router, _auth, ...envDeps }) { + async init({ + $$auth, + $$config, + $$logger, + $$router, + $$userInfo, + ...envDeps + }) { const { default: createRouter } = await createRouterImport; const pluginEnv = Object.fromEntries( Object.entries(envDeps).map(([key, dep]) => { @@ -107,25 +187,35 @@ export function makeLegacyPlugin< if (transform) { return [key, transform(dep)]; } - if (key === 'tokenManager') { - return [ - key, - wrapTokenManager(dep as TokenManager, _auth as AuthService), - ]; - } return [key, dep]; }), ); - const router = await createRouter( - pluginEnv as TransformedEnv, + + const auth = $$auth as typeof coreServices.auth.T; + const config = $$config as typeof coreServices.rootConfig.T; + const logger = $$logger as typeof coreServices.logger.T; + const router = $$router as typeof coreServices.httpRouter.T; + const userInfo = $$userInfo as typeof coreServices.userInfo.T; + + // Token manager and identity services are no longer supported in the new backend system, so we provide shims for them. + pluginEnv.tokenManager = createTokenManagerShim( + auth, + config, + logger, ); - (_router as HttpRouterService).use(router); + pluginEnv.identity = createIdentityServiceShim(auth, userInfo); + + const pluginRouter = await createRouter( + pluginEnv as TransformedEnv & { + tokenManager: TokenManager; + identity: LegacyIdentityService; + }, + ); + router.use(pluginRouter); }, }); }, }); - - return compatPlugin(); }; } @@ -155,9 +245,7 @@ export const legacyPlugin = makeLegacyPlugin( logger: coreServices.logger, permissions: coreServices.permissions, scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, reader: coreServices.urlReader, - identity: coreServices.identity, }, { logger: log => loggerToWinstonLogger(log), diff --git a/packages/backend-common/src/deprecated/tokens/types.ts b/packages/backend-common/src/deprecated/tokens/types.ts index 09dc0bb211..41a2000868 100644 --- a/packages/backend-common/src/deprecated/tokens/types.ts +++ b/packages/backend-common/src/deprecated/tokens/types.ts @@ -14,10 +14,25 @@ * limitations under the License. */ -import { TokenManagerService } from '@backstage/backend-plugin-api'; - /** * @public * @deprecated Please {@link https://backstage.io/docs/tutorials/auth-service-migration | migrate} to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead. */ -export type TokenManager = TokenManagerService; +export interface TokenManager { + /** + * Fetches a valid token. + * + * @remarks + * + * Tokens are valid for roughly one hour; the actual deadline is set in the + * payload `exp` claim. Never hold on to tokens for reuse; always ask for a + * new one for each outgoing request. This ensures that you always get a + * valid, fresh one. + */ + getToken(): Promise<{ token: string }>; + + /** + * Validates a given token. + */ + authenticate(token: string): Promise; +} diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 64d9b77fba..dfa2373019 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,116 @@ # @backstage/backend-defaults +## 0.5.0-next.0 + +### Minor Changes + +- 359fcd7: **BREAKING**: The backwards compatibility with plugins using legacy auth through the token manager service has been removed. This means that instead of falling back to using the old token manager, requests towards plugins that don't support the new auth system will simply fail. Please make sure that all plugins in your deployment are hosted within a backend instance from the new backend system. +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +- 19ff127: **BREAKING**: The default backend instance no longer provides implementations for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`. + + If you rely on plugins that still require these services, you can add them to your own backend by re-creating the service reference and factory. + + The following can be used to implement the identity service: + + ```ts + import { + coreServices, + createServiceFactory, + createServiceRef, + } from '@backstage/backend-plugin-api'; + import { + DefaultIdentityClient, + IdentityApi, + } from '@backstage/plugin-auth-node'; + + backend.add( + createServiceFactory({ + service: createServiceRef({ id: 'core.identity' }), + deps: { + discovery: coreServices.discovery, + }, + async factory({ discovery }) { + return DefaultIdentityClient.create({ discovery }); + }, + }), + ); + ``` + + The following can be used to implement the token manager service: + + ```ts + import { ServerTokenManager, TokenManager } from '@backstage/backend-common'; + import { createBackend } from '@backstage/backend-defaults'; + import { + coreServices, + createServiceFactory, + createServiceRef, + } from '@backstage/backend-plugin-api'; + + backend.add( + createServiceFactory({ + service: createServiceRef({ id: 'core.tokenManager' }), + deps: { + config: coreServices.rootConfig, + logger: coreServices.rootLogger, + }, + createRootContext({ config, logger }) { + return ServerTokenManager.fromConfig(config, { + logger, + allowDisabledTokenManager: true, + }); + }, + async factory(_deps, tokenManager) { + return tokenManager; + }, + }), + ); + ``` + +### Patch Changes + +- 7f779c7: `auth.externalAccess` should be optional in the config schema +- 7a72ec8: Exports the `discoveryFeatureLoader` as a replacement for the deprecated `featureDiscoveryService`. + The `discoveryFeatureLoader` is a new backend system [feature loader](https://backstage.io/docs/backend-system/architecture/feature-loaders/) that discovers backend features from the current `package.json` and its dependencies. + Here is an example using the `discoveryFeatureLoader` loader in a new backend instance: + + ```ts + import { createBackend } from '@backstage/backend-defaults'; + import { discoveryFeatureLoader } from '@backstage/backend-defaults'; + //... + + const backend = createBackend(); + //... + backend.add(discoveryFeatureLoader); + //... + backend.start(); + ``` + +- 66dbf0a: Allow the cache service to accept the human duration format for TTL +- 5a8fcb4: Added the option to skip database migrations by setting `skipMigrations: true` in config. This can be done globally in the database config or by plugin id. +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/backend-app-api@0.10.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + ## 0.4.2 ### Patch Changes diff --git a/packages/backend-defaults/api-report-auth.md b/packages/backend-defaults/api-report-auth.md index 5ceca7f72e..2f201e4573 100644 --- a/packages/backend-defaults/api-report-auth.md +++ b/packages/backend-defaults/api-report-auth.md @@ -4,14 +4,13 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const authServiceFactory: ServiceFactoryCompat< +export const authServiceFactory: ServiceFactory< AuthService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-cache.md b/packages/backend-defaults/api-report-cache.md index e8b6f075ab..58106430ab 100644 --- a/packages/backend-defaults/api-report-cache.md +++ b/packages/backend-defaults/api-report-cache.md @@ -7,7 +7,7 @@ import { CacheService } from '@backstage/backend-plugin-api'; import { CacheServiceOptions } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { LoggerService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public export class CacheManager { @@ -25,11 +25,10 @@ export type CacheManagerOptions = { }; // @public -export const cacheServiceFactory: ServiceFactoryCompat< +export const cacheServiceFactory: ServiceFactory< CacheService, 'plugin', - 'singleton', - undefined + 'singleton' >; // @public (undocumented) diff --git a/packages/backend-defaults/api-report-database.md b/packages/backend-defaults/api-report-database.md index 8d204db025..10de8df1ff 100644 --- a/packages/backend-defaults/api-report-database.md +++ b/packages/backend-defaults/api-report-database.md @@ -8,7 +8,7 @@ import { DatabaseService } from '@backstage/backend-plugin-api'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { PluginMetadataService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public export class DatabaseManager implements LegacyRootDatabaseService { @@ -32,11 +32,10 @@ export type DatabaseManagerOptions = { }; // @public -export const databaseServiceFactory: ServiceFactoryCompat< +export const databaseServiceFactory: ServiceFactory< DatabaseService, 'plugin', - 'singleton', - undefined + 'singleton' >; // @public @deprecated diff --git a/packages/backend-defaults/api-report-discovery.md b/packages/backend-defaults/api-report-discovery.md index 1b19a42aa5..de60256f02 100644 --- a/packages/backend-defaults/api-report-discovery.md +++ b/packages/backend-defaults/api-report-discovery.md @@ -5,14 +5,13 @@ ```ts import { Config } from '@backstage/config'; import { DiscoveryService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const discoveryServiceFactory: ServiceFactoryCompat< +export const discoveryServiceFactory: ServiceFactory< DiscoveryService, 'plugin', - 'singleton', - undefined + 'singleton' >; // @public diff --git a/packages/backend-defaults/api-report-httpAuth.md b/packages/backend-defaults/api-report-httpAuth.md index a65a6e157d..6c08a6f4d0 100644 --- a/packages/backend-defaults/api-report-httpAuth.md +++ b/packages/backend-defaults/api-report-httpAuth.md @@ -4,14 +4,13 @@ ```ts import { HttpAuthService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const httpAuthServiceFactory: ServiceFactoryCompat< +export const httpAuthServiceFactory: ServiceFactory< HttpAuthService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-httpRouter.md b/packages/backend-defaults/api-report-httpRouter.md index 300e75a44d..dc612377ae 100644 --- a/packages/backend-defaults/api-report-httpRouter.md +++ b/packages/backend-defaults/api-report-httpRouter.md @@ -7,7 +7,7 @@ import { HttpRouterService } from '@backstage/backend-plugin-api'; import { HumanDuration } from '@backstage/types'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { RequestHandler } from 'express'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public export function createLifecycleMiddleware( @@ -15,11 +15,10 @@ export function createLifecycleMiddleware( ): RequestHandler; // @public -export const httpRouterServiceFactory: ServiceFactoryCompat< +export const httpRouterServiceFactory: ServiceFactory< HttpRouterService, 'plugin', - 'singleton', - undefined + 'singleton' >; // @public diff --git a/packages/backend-defaults/api-report-lifecycle.md b/packages/backend-defaults/api-report-lifecycle.md index 54ffb77fed..0d909faa90 100644 --- a/packages/backend-defaults/api-report-lifecycle.md +++ b/packages/backend-defaults/api-report-lifecycle.md @@ -4,14 +4,13 @@ ```ts import { LifecycleService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const lifecycleServiceFactory: ServiceFactoryCompat< +export const lifecycleServiceFactory: ServiceFactory< LifecycleService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-logger.md b/packages/backend-defaults/api-report-logger.md index 40ca4e21b4..c3d2ab3359 100644 --- a/packages/backend-defaults/api-report-logger.md +++ b/packages/backend-defaults/api-report-logger.md @@ -4,14 +4,13 @@ ```ts import { LoggerService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const loggerServiceFactory: ServiceFactoryCompat< +export const loggerServiceFactory: ServiceFactory< LoggerService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-permissions.md b/packages/backend-defaults/api-report-permissions.md index eea11ed7c9..003bee9d25 100644 --- a/packages/backend-defaults/api-report-permissions.md +++ b/packages/backend-defaults/api-report-permissions.md @@ -4,14 +4,13 @@ ```ts import { PermissionsService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const permissionsServiceFactory: ServiceFactoryCompat< +export const permissionsServiceFactory: ServiceFactory< PermissionsService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-rootHealth.md b/packages/backend-defaults/api-report-rootHealth.md index c7580cacd6..b26de7cfda 100644 --- a/packages/backend-defaults/api-report-rootHealth.md +++ b/packages/backend-defaults/api-report-rootHealth.md @@ -4,14 +4,13 @@ ```ts import { RootHealthService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public (undocumented) -export const rootHealthServiceFactory: ServiceFactoryCompat< +export const rootHealthServiceFactory: ServiceFactory< RootHealthService, 'root', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-rootLifecycle.md b/packages/backend-defaults/api-report-rootLifecycle.md index e522471d04..7a7c4fd7c0 100644 --- a/packages/backend-defaults/api-report-rootLifecycle.md +++ b/packages/backend-defaults/api-report-rootLifecycle.md @@ -4,14 +4,13 @@ ```ts import { RootLifecycleService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export const rootLifecycleServiceFactory: ServiceFactoryCompat< +export const rootLifecycleServiceFactory: ServiceFactory< RootLifecycleService, 'root', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-rootLogger.md b/packages/backend-defaults/api-report-rootLogger.md index 3878420192..b353039c03 100644 --- a/packages/backend-defaults/api-report-rootLogger.md +++ b/packages/backend-defaults/api-report-rootLogger.md @@ -7,15 +7,14 @@ import { Format } from 'logform'; import { JsonObject } from '@backstage/types'; import { LoggerService } from '@backstage/backend-plugin-api'; import { RootLoggerService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; import { transport } from 'winston'; // @public -export const rootLoggerServiceFactory: ServiceFactoryCompat< +export const rootLoggerServiceFactory: ServiceFactory< RootLoggerService, 'root', - 'singleton', - undefined + 'singleton' >; // @public diff --git a/packages/backend-defaults/api-report-scheduler.md b/packages/backend-defaults/api-report-scheduler.md index 9953518e09..10c0c0a943 100644 --- a/packages/backend-defaults/api-report-scheduler.md +++ b/packages/backend-defaults/api-report-scheduler.md @@ -6,7 +6,7 @@ import { DatabaseService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public export class DefaultSchedulerService { @@ -18,11 +18,10 @@ export class DefaultSchedulerService { } // @public -export const schedulerServiceFactory: ServiceFactoryCompat< +export const schedulerServiceFactory: ServiceFactory< SchedulerService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report-urlReader.md b/packages/backend-defaults/api-report-urlReader.md index f561c2cbf5..73fbcd60ac 100644 --- a/packages/backend-defaults/api-report-urlReader.md +++ b/packages/backend-defaults/api-report-urlReader.md @@ -21,7 +21,7 @@ import { GitLabIntegration } from '@backstage/integration'; import { HarnessIntegration } from '@backstage/integration'; import { LoggerService } from '@backstage/backend-plugin-api'; import { Readable } from 'stream'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { UrlReaderServiceReadTreeOptions } from '@backstage/backend-plugin-api'; @@ -434,11 +434,10 @@ export class UrlReaders { } // @public -export const urlReaderServiceFactory: ServiceFactoryCompat< +export const urlReaderServiceFactory: ServiceFactory< UrlReaderService, 'plugin', - 'singleton', - undefined + 'singleton' >; // @public diff --git a/packages/backend-defaults/api-report-userInfo.md b/packages/backend-defaults/api-report-userInfo.md index 27f73d06e1..9ab4db1391 100644 --- a/packages/backend-defaults/api-report-userInfo.md +++ b/packages/backend-defaults/api-report-userInfo.md @@ -3,15 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; import { UserInfoService } from '@backstage/backend-plugin-api'; // @public -export const userInfoServiceFactory: ServiceFactoryCompat< +export const userInfoServiceFactory: ServiceFactory< UserInfoService, 'plugin', - 'singleton', - undefined + 'singleton' >; // (No @packageDocumentation comment for this package) diff --git a/packages/backend-defaults/api-report.md b/packages/backend-defaults/api-report.md index ab9cc5775c..def38beda7 100644 --- a/packages/backend-defaults/api-report.md +++ b/packages/backend-defaults/api-report.md @@ -4,7 +4,11 @@ ```ts import { Backend } from '@backstage/backend-app-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public (undocumented) export function createBackend(): Backend; + +// @public +export const discoveryFeatureLoader: BackendFeature; ``` diff --git a/packages/backend-defaults/config.d.ts b/packages/backend-defaults/config.d.ts index 1582cf1a39..a8c6e1c290 100644 --- a/packages/backend-defaults/config.d.ts +++ b/packages/backend-defaults/config.d.ts @@ -14,8 +14,51 @@ * limitations under the License. */ +import { HumanDuration } from '@backstage/types'; + export interface Config { + app: { + baseUrl: string; // defined in core, but repeated here without doc + }; + backend?: { + /** + * The full base URL of the backend, as seen from the browser's point of + * view as it makes calls to the backend. + */ + baseUrl: string; + + /** Address that the backend should listen to. */ + listen?: + | string + | { + /** Address of the interface that the backend should bind to. */ + host?: string; + /** Port that the backend should listen to. */ + port?: string | number; + }; + + /** + * HTTPS configuration for the backend. If omitted the backend will serve HTTP. + * + * Setting this to `true` will cause self-signed certificates to be generated, which + * can be useful for local development or other non-production scenarios. + */ + https?: + | true + | { + /** Certificate configuration */ + certificate?: { + /** PEM encoded certificate. Use $file to load in a file */ + cert: string; + /** + * PEM encoded certificate key. Use $file to load in a file. + * @visibility secret + */ + key: string; + }; + }; + /** * Options used by the default auth, httpAuth and userInfo services. */ @@ -330,6 +373,184 @@ export interface Config { } >; }; + + /** Database connection configuration, select base database type using the `client` field */ + database: { + /** Default database client to use */ + client: 'better-sqlite3' | 'sqlite3' | 'pg'; + /** + * Base database connection string, or object with individual connection properties + * @visibility secret + */ + connection: + | string + | { + /** + * Password that belongs to the client User + * @visibility secret + */ + password?: string; + /** + * Other connection settings + */ + [key: string]: unknown; + }; + /** Database name prefix override */ + prefix?: string; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to true if unspecified. + */ + ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; + /** + * How plugins databases are managed/divided in the provided database instance. + * + * `database` -> Plugins are each given their own database to manage their schemas/tables. + * + * `schema` -> Plugins will be given their own schema (in the specified/default database) + * to manage their tables. + * + * NOTE: Currently only supported by the `pg` client. + * + * @default database + */ + pluginDivisionMode?: 'database' | 'schema'; + /** Configures the ownership of newly created schemas in pg databases. */ + role?: string; + /** + * Arbitrary config object to pass to knex when initializing + * (https://knexjs.org/#Installation-client). Most notable is the debug + * and asyncStackTraces booleans + */ + knexConfig?: object; + /** Skip running database migrations. */ + skipMigrations?: boolean; + /** Plugin specific database configuration and client override */ + plugin?: { + [pluginId: string]: { + /** Database client override */ + client?: 'better-sqlite3' | 'sqlite3' | 'pg'; + /** + * Database connection string or Knex object override + * @visibility secret + */ + connection?: string | object; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to base config if unspecified. + */ + ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; + /** + * Arbitrary config object to pass to knex when initializing + * (https://knexjs.org/#Installation-client). Most notable is the + * debug and asyncStackTraces booleans. + * + * This is merged recursively into the base knexConfig + */ + knexConfig?: object; + /** Configures the ownership of newly created schemas in pg databases. */ + role?: string; + /** Skip running database migrations. */ + skipMigrations?: boolean; + }; + }; + }; + + /** Cache connection configuration, select cache type using the `store` field */ + cache?: + | { + store: 'memory'; + /** An optional default TTL (in milliseconds). */ + defaultTtl?: number | HumanDuration; + } + | { + store: 'redis'; + /** + * A redis connection string in the form `redis://user:pass@host:port`. + * @visibility secret + */ + connection: string; + /** An optional default TTL (in milliseconds). */ + defaultTtl?: number | HumanDuration; + /** + * Whether or not [useRedisSets](https://github.com/jaredwray/keyv/tree/main/packages/redis#useredissets) should be configured to this redis cache. + * Defaults to true if unspecified. + */ + useRedisSets?: boolean; + } + | { + store: 'memcache'; + /** + * A memcache connection string in the form `user:pass@host:port`. + * @visibility secret + */ + connection: string; + /** An optional default TTL (in milliseconds). */ + defaultTtl?: number | HumanDuration; + }; + + cors?: { + origin?: string | string[]; + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + preflightContinue?: boolean; + optionsSuccessStatus?: number; + }; + + /** + * Content Security Policy options. + * + * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The + * values are on the format that the helmet library expects them, as an + * array of strings. There is also the special value false, which means to + * remove the default value that Backstage puts in place for that policy. + */ + csp?: { [policyId: string]: string[] | false }; + + /** + * Configuration related to URL reading, used for example for reading catalog info + * files, scaffolder templates, and techdocs content. + */ + reading?: { + /** + * A list of targets to allow outgoing requests to. Users will be able to make + * requests on behalf of the backend to the targets that are allowed by this list. + */ + allow?: Array<{ + /** + * A host to allow outgoing requests to, being either a full host or + * a subdomain wildcard pattern with a leading `*`. For example `example.com` + * and `*.example.com` are valid values, `prod.*.example.com` is not. + * The host may also contain a port, for example `example.com:8080`. + */ + host: string; + + /** + * An optional list of paths. In case they are present only targets matching + * any of them will are allowed. You can use trailing slashes to make sure only + * subdirectories are allowed, for example `/mydir/` will allow targets with + * paths like `/mydir/a` but will block paths like `/mydir2`. + */ + paths?: string[]; + }>; + }; }; /** diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 3280a3cf9d..60a7ce155c 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.4.2", + "version": "0.5.0-next.0", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" @@ -124,6 +124,7 @@ "@backstage/backend-dev-utils": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/cli-common": "workspace:^", + "@backstage/cli-node": "workspace:^", "@backstage/config": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", diff --git a/packages/backend-defaults/src/CreateBackend.ts b/packages/backend-defaults/src/CreateBackend.ts index 47fa09da49..4b0f497980 100644 --- a/packages/backend-defaults/src/CreateBackend.ts +++ b/packages/backend-defaults/src/CreateBackend.ts @@ -14,12 +14,7 @@ * limitations under the License. */ -import { - Backend, - createSpecializedBackend, - identityServiceFactory, - tokenManagerServiceFactory, -} from '@backstage/backend-app-api'; +import { Backend, createSpecializedBackend } from '@backstage/backend-app-api'; import { authServiceFactory } from '@backstage/backend-defaults/auth'; import { cacheServiceFactory } from '@backstage/backend-defaults/cache'; import { databaseServiceFactory } from '@backstage/backend-defaults/database'; @@ -47,7 +42,6 @@ export const defaultServiceFactories = [ discoveryServiceFactory, httpAuthServiceFactory, httpRouterServiceFactory, - identityServiceFactory, lifecycleServiceFactory, loggerServiceFactory, permissionsServiceFactory, @@ -56,7 +50,6 @@ export const defaultServiceFactories = [ rootLifecycleServiceFactory, rootLoggerServiceFactory, schedulerServiceFactory, - tokenManagerServiceFactory, userInfoServiceFactory, urlReaderServiceFactory, eventsServiceFactory, diff --git a/packages/backend-defaults/src/PackageDiscoveryService.ts b/packages/backend-defaults/src/PackageDiscoveryService.ts new file mode 100644 index 0000000000..d7d127f4fa --- /dev/null +++ b/packages/backend-defaults/src/PackageDiscoveryService.ts @@ -0,0 +1,170 @@ +/* + * Copyright 2024 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 fs from 'fs-extra'; +import { resolve as resolvePath, dirname } from 'path'; + +import { + BackendFeature, + RootConfigService, + RootLoggerService, +} from '@backstage/backend-plugin-api'; +import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha'; +import { BackstagePackageJson } from '@backstage/cli-node'; + +const DETECTED_PACKAGE_ROLES = [ + 'node-library', + 'backend', + 'backend-plugin', + 'backend-plugin-module', +]; + +/** @internal */ +function isBackendFeature(value: unknown): value is BackendFeature { + return ( + !!value && + ['object', 'function'].includes(typeof value) && + (value as BackendFeature).$$type === '@backstage/BackendFeature' + ); +} + +/** @internal */ +function isBackendFeatureFactory( + value: unknown, +): value is () => BackendFeature { + return ( + !!value && + typeof value === 'function' && + (value as any).$$type === '@backstage/BackendFeatureFactory' + ); +} + +/** @internal */ +async function findClosestPackageDir( + searchDir: string, +): Promise { + let path = searchDir; + + // Some confidence check to avoid infinite loop + for (let i = 0; i < 1000; i++) { + const packagePath = resolvePath(path, 'package.json'); + const exists = await fs.pathExists(packagePath); + if (exists) { + return path; + } + + const newPath = dirname(path); + if (newPath === path) { + return undefined; + } + path = newPath; + } + + throw new Error( + `Iteration limit reached when searching for root package.json at ${searchDir}`, + ); +} + +/** @internal */ +export class PackageDiscoveryService implements FeatureDiscoveryService { + constructor( + private readonly config: RootConfigService, + private readonly logger: RootLoggerService, + ) {} + + getDependencyNames(path: string) { + const { dependencies } = require(path) as BackstagePackageJson; + const packagesConfig = this.config.getOptional('backend.packages'); + + const dependencyNames = Object.keys(dependencies || {}); + + if (packagesConfig === 'all') { + return dependencyNames; + } + + const includedPackagesConfig = this.config.getOptionalStringArray( + 'backend.packages.include', + ); + + const includedPackages = includedPackagesConfig + ? new Set(includedPackagesConfig) + : dependencyNames; + const excludedPackagesSet = new Set( + this.config.getOptionalStringArray('backend.packages.exclude'), + ); + + return [...includedPackages].filter(name => !excludedPackagesSet.has(name)); + } + + async getBackendFeatures(): Promise<{ features: Array }> { + const packagesConfig = this.config.getOptional('backend.packages'); + if (!packagesConfig || Object.keys(packagesConfig).length === 0) { + return { features: [] }; + } + + const packageDir = await findClosestPackageDir(process.argv[1]); + if (!packageDir) { + throw new Error('Package discovery failed to find package.json'); + } + const dependencyNames = this.getDependencyNames( + resolvePath(packageDir, 'package.json'), + ); + + const features: BackendFeature[] = []; + + for (const name of dependencyNames) { + const depPkg = require(require.resolve(`${name}/package.json`, { + paths: [packageDir], + })) as BackstagePackageJson; + if ( + !depPkg?.backstage?.role || + !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role) + ) { + continue; // Not a backstage backend package, ignore + } + + const exportedModulePaths = [ + require.resolve(name, { + paths: [packageDir], + }), + ]; + + // Find modules exported as alpha + try { + exportedModulePaths.push( + require.resolve(`${name}/alpha`, { paths: [packageDir] }), + ); + } catch { + /* ignore */ + } + + for (const modulePath of exportedModulePaths) { + const mod = require(modulePath); + + if (isBackendFeature(mod.default)) { + this.logger.info(`Detected: ${name}`); + features.push(mod.default); + } + if (isBackendFeatureFactory(mod.default)) { + this.logger.info(`Detected: ${name}`); + features.push(mod.default()); + } + } + } + + return { features }; + } +} diff --git a/packages/backend-defaults/src/discoveryFeatureLoader.ts b/packages/backend-defaults/src/discoveryFeatureLoader.ts new file mode 100644 index 0000000000..dc3c9c1d90 --- /dev/null +++ b/packages/backend-defaults/src/discoveryFeatureLoader.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2024 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 { + coreServices, + createBackendFeatureLoader, +} from '@backstage/backend-plugin-api'; +import { PackageDiscoveryService } from './PackageDiscoveryService'; + +/** + * A loader that discovers backend features from the current package.json and its dependencies. + * + * @public + * + * @example + * Using the `discoveryFeatureLoader` loader in a backend instance: + * ```ts + * //... + * import { createBackend } from '@backstage/backend-defaults'; + * import { discoveryFeatureLoader } from '@backstage/backend-defaults'; + * + * const backend = createBackend(); + * backend.add(discoveryFeatureLoader); + * //... + * backend.start(); + * ``` + */ +export const discoveryFeatureLoader = createBackendFeatureLoader({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.rootLogger, + }, + async loader({ config, logger }) { + const service = new PackageDiscoveryService(config, logger); + const { features } = await service.getBackendFeatures(); + return features; + }, +}); diff --git a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts index 08cfc147f8..05d62a54a2 100644 --- a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts +++ b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { TokenManager } from '@backstage/backend-common'; import { AuthService, BackstageCredentials, @@ -22,9 +21,8 @@ import { BackstagePrincipalTypes, BackstageServicePrincipal, BackstageUserPrincipal, - LoggerService, } from '@backstage/backend-plugin-api'; -import { AuthenticationError, ForwardedError } from '@backstage/errors'; +import { AuthenticationError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import { decodeJwt } from 'jose'; import { ExternalTokenHandler } from './external/ExternalTokenHandler'; @@ -44,11 +42,9 @@ export class DefaultAuthService implements AuthService { private readonly userTokenHandler: UserTokenHandler, private readonly pluginTokenHandler: PluginTokenHandler, private readonly externalTokenHandler: ExternalTokenHandler, - private readonly tokenManager: TokenManager, private readonly pluginId: string, private readonly disableDefaultAuthPolicy: boolean, private readonly pluginKeySource: PluginKeySource, - private readonly logger: LoggerService, ) {} async authenticate( @@ -153,55 +149,28 @@ export class DefaultAuthService implements AuthService { return { token: '' }; } - const targetSupportsNewAuth = - await this.pluginTokenHandler.isTargetPluginSupported(targetPluginId); - // check whether a plugin support the new auth system // by checking the public keys endpoint existance. switch (type) { // TODO: Check whether the principal is ourselves case 'service': - if (targetSupportsNewAuth) { - return this.pluginTokenHandler.issueToken({ - pluginId: this.pluginId, - targetPluginId, - }); - } - // If the target plugin does not support the new auth service, fall back to using old token format - this.logger.warn( - `DEPRECATION WARNING: A call to the '${targetPluginId}' plugin had to fall back to using deprecated auth via the token manager service. Please migrate all plugins to the new auth service, see https://backstage.io/docs/tutorials/auth-service-migration for more information`, - ); - return this.tokenManager.getToken().catch(error => { - throw new ForwardedError( - `Unable to generate legacy token for communication with the '${targetPluginId}' plugin. ` + - `You will typically encounter this error when attempting to call a plugin that does not exist, or is deployed with an old version of Backstage`, - error, - ); + return this.pluginTokenHandler.issueToken({ + pluginId: this.pluginId, + targetPluginId, }); case 'user': { const { token } = internalForward; if (!token) { throw new Error('User credentials is unexpectedly missing token'); } - // If the target plugin supports the new auth service we issue a service - // on-behalf-of token rather than forwarding the user token - if (targetSupportsNewAuth) { - const onBehalfOf = await this.userTokenHandler.createLimitedUserToken( - token, - ); - return this.pluginTokenHandler.issueToken({ - pluginId: this.pluginId, - targetPluginId, - onBehalfOf, - }); - } - - if (this.userTokenHandler.isLimitedUserToken(token)) { - throw new AuthenticationError( - `Unable to call '${targetPluginId}' plugin on behalf of user, because the target plugin does not support on-behalf-of tokens or the plugin doesn't exist`, - ); - } - return { token }; + const onBehalfOf = await this.userTokenHandler.createLimitedUserToken( + token, + ); + return this.pluginTokenHandler.issueToken({ + pluginId: this.pluginId, + targetPluginId, + onBehalfOf, + }); } default: throw new AuthenticationError( diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index f9ef4c9762..cb672ed7f1 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -19,25 +19,18 @@ import { mockServices, registerMswTestHooks, } from '@backstage/backend-test-utils'; -import { tokenManagerServiceFactory } from '@backstage/backend-app-api'; import { authServiceFactory } from './authServiceFactory'; import { base64url, decodeJwt } from 'jose'; import { discoveryServiceFactory } from '../discovery'; -import { - BackstageServicePrincipal, - BackstageUserPrincipal, -} from '@backstage/backend-plugin-api'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; -import { InternalBackstageCredentials } from './types'; import { toInternalBackstageCredentials } from './helpers'; const server = setupServer(); // TODO: Ship discovery mock service in the service factory tester const mockDeps = [ - discoveryServiceFactory(), - tokenManagerServiceFactory, + discoveryServiceFactory, mockServices.rootConfig.factory({ data: { backend: { @@ -74,12 +67,16 @@ describe('authServiceFactory', () => { jest.useRealTimers(); }); - it('should authenticate issued tokens with legacy auth', async () => { + it('should not support tokens issued with legacy auth', async () => { server.use( rest.get( 'http://localhost:7007/api/catalog/.backstage/auth/v1/jwks.json', (_req, res, ctx) => res(ctx.status(404)), ), + rest.get( + 'http://localhost:7007/api/search/.backstage/auth/v1/jwks.json', + (_req, res, ctx) => res(ctx.status(404)), + ), ); const tester = ServiceFactoryTester.from(authServiceFactory, { @@ -94,21 +91,15 @@ describe('authServiceFactory', () => { targetPluginId: 'catalog', }); - await expect(searchAuth.authenticate(searchToken)).resolves.toEqual( - expect.objectContaining({ - principal: { - type: 'service', - subject: 'external:backstage-plugin', - }, - }), + await expect( + searchAuth.authenticate(searchToken), + ).rejects.toMatchInlineSnapshot( + `[AuthenticationError: Received a plugin token where the source 'search' plugin unexpectedly does not have a JWKS endpoint. The target plugin needs to be migrated to be installed in an app using the new backend system.]`, ); - await expect(catalogAuth.authenticate(searchToken)).resolves.toEqual( - expect.objectContaining({ - principal: { - type: 'service', - subject: 'external:backstage-plugin', - }, - }), + await expect( + catalogAuth.authenticate(searchToken), + ).rejects.toMatchInlineSnapshot( + `[AuthenticationError: Received a plugin token where the source 'search' plugin unexpectedly does not have a JWKS endpoint. The target plugin needs to be migrated to be installed in an app using the new backend system.]`, ); }); @@ -151,38 +142,7 @@ describe('authServiceFactory', () => { ); }); - it('should forward user token if target plugin does not support new auth service', async () => { - server.use( - rest.get( - 'http://localhost:7007/api/permission/.backstage/auth/v1/jwks.json', - (_req, res, ctx) => res(ctx.status(404)), - ), - ); - - const tester = ServiceFactoryTester.from(authServiceFactory, { - dependencies: mockDeps, - }); - - const catalogAuth = await tester.getSubject('catalog'); - - await expect( - catalogAuth.getPluginRequestToken({ - onBehalfOf: { - $$type: '@backstage/BackstageCredentials', - version: 'v1', - authMethod: 'token', - token: 'alice-token', - principal: { - type: 'user', - userEntityRef: 'user:default/alice', - }, - } as InternalBackstageCredentials, - targetPluginId: 'permission', - }), - ).resolves.toEqual({ token: 'alice-token' }); - }); - - it('should issue a new service token with token manager if target plugin does not support new auth service', async () => { + it('should issue a service token for the new system even if the target plugin does not support it', async () => { server.use( rest.get( 'http://localhost:7007/api/permission/.backstage/auth/v1/jwks.json', @@ -197,22 +157,14 @@ describe('authServiceFactory', () => { const catalogAuth = await tester.getSubject('catalog'); const { token } = await catalogAuth.getPluginRequestToken({ - onBehalfOf: { - $$type: '@backstage/BackstageCredentials', - version: 'v1', - authMethod: 'token', - token: 'some-upstream-service-token', - principal: { - type: 'service', - subject: 'external:upstream-service', - }, - } as InternalBackstageCredentials, + onBehalfOf: await catalogAuth.getOwnServiceCredentials(), targetPluginId: 'permission', }); expect(decodeJwt(token)).toEqual( expect.objectContaining({ - sub: 'backstage-server', + sub: 'catalog', + aud: 'permission', }), ); }); @@ -415,15 +367,6 @@ describe('authServiceFactory', () => { targetPluginId: 'permission', }); expect(decodeJwt(oboToken2).obo).toBe(limitedToken); - - await expect( - catalogAuth.getPluginRequestToken({ - onBehalfOf: oboCredentials, - targetPluginId: 'kubernetes', - }), - ).rejects.toThrow( - "Unable to call 'kubernetes' plugin on behalf of user, because the target plugin does not support on-behalf-of tokens or the plugin doesn't exist", - ); }); it('should eagerly reject access to external access tokens based on plugin id', async () => { diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index b66ba7227f..34071ca541 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -41,13 +41,8 @@ export const authServiceFactory = createServiceFactory({ discovery: coreServices.discovery, plugin: coreServices.pluginMetadata, database: coreServices.database, - // Re-using the token manager makes sure that we use the same generated keys for - // development as plugins that have not yet been migrated. It's important that this - // keeps working as long as there are plugins that have not been migrated to the - // new auth services in the new backend system. - tokenManager: coreServices.tokenManager, }, - async factory({ config, discovery, plugin, tokenManager, logger, database }) { + async factory({ config, discovery, plugin, logger, database }) { const disableDefaultAuthPolicy = config.getOptionalBoolean( 'backend.auth.dangerouslyDisableDefaultAuthPolicy', @@ -84,11 +79,9 @@ export const authServiceFactory = createServiceFactory({ userTokens, pluginTokens, externalTokens, - tokenManager, plugin.getId(), disableDefaultAuthPolicy, keySource, - logger, ); }, }); diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts index a401469fdc..ff594ecbd4 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts @@ -146,7 +146,9 @@ export class PluginTokenHandler { return { token }; } - async isTargetPluginSupported(targetPluginId: string): Promise { + private async isTargetPluginSupported( + targetPluginId: string, + ): Promise { if (this.supportedTargetPlugins.has(targetPluginId)) { return true; } @@ -199,7 +201,8 @@ export class PluginTokenHandler { // Double check that the target plugin has a valid JWKS endpoint, otherwise avoid creating a remote key set if (!(await this.isTargetPluginSupported(pluginId))) { throw new AuthenticationError( - `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint`, + `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint. ` + + 'The target plugin needs to be migrated to be installed in an app using the new backend system.', ); } diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheClient.ts b/packages/backend-defaults/src/entrypoints/cache/CacheClient.ts index aba62558d4..f519daa6df 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheClient.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheClient.ts @@ -22,6 +22,7 @@ import { import { JsonValue } from '@backstage/types'; import { createHash } from 'crypto'; import Keyv from 'keyv'; +import { ttlToMilliseconds } from './types'; export type CacheClientFactory = (options: CacheServiceOptions) => Keyv; @@ -58,7 +59,9 @@ export class DefaultCacheClient implements CacheService { opts: CacheServiceSetOptions = {}, ): Promise { const k = this.getNormalizedKey(key); - await this.#client.set(k, value, opts.ttl); + const ttl = + opts.ttl !== undefined ? ttlToMilliseconds(opts.ttl) : undefined; + await this.#client.set(k, value, ttl); } async delete(key: string): Promise { diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts index 6eb907764d..6a1dad2c91 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts @@ -93,4 +93,96 @@ describe('CacheManager integration', () => { await expect(plugin2b.get('a')).resolves.toBe('plugin2b'); }, ); + + it.each(caches.eachSupportedId())( + 'supports both milliseconds and human durations throughout, %p', + async cacheId => { + const { store, connection } = await caches.init(cacheId); + + for (const defaultTtl of [200, { milliseconds: 200 }]) { + const manager = CacheManager.fromConfig( + mockServices.rootConfig({ + data: { + backend: { + cache: { + store, + connection, + defaultTtl, + }, + }, + }, + }), + ).forPlugin('p'); + + const defaultClient = manager.getClient(); + const numberOverrideClient = manager.getClient({ defaultTtl: 400 }); + const durationOverrideClient = manager.getClient({ + defaultTtl: { milliseconds: 400 }, + }); + + await defaultClient.set('a', 'x'); + await defaultClient.set('b', 'x'); + await numberOverrideClient.set('c', 'x'); + await durationOverrideClient.set('d', 'x'); + await defaultClient.set('e', 'x', { ttl: 400 }); + await defaultClient.set('f', 'x', { ttl: { milliseconds: 400 } }); + + await expect(defaultClient.get('a')).resolves.toBe('x'); + await expect(defaultClient.get('b')).resolves.toBe('x'); + await expect(defaultClient.get('c')).resolves.toBe('x'); + await expect(defaultClient.get('d')).resolves.toBe('x'); + await expect(defaultClient.get('e')).resolves.toBe('x'); + await expect(defaultClient.get('f')).resolves.toBe('x'); + + await new Promise(resolve => setTimeout(resolve, 50 + 200)); + + await expect(defaultClient.get('a')).resolves.toBeUndefined(); + await expect(defaultClient.get('b')).resolves.toBeUndefined(); + await expect(defaultClient.get('c')).resolves.toBe('x'); + await expect(defaultClient.get('d')).resolves.toBe('x'); + await expect(defaultClient.get('e')).resolves.toBe('x'); + await expect(defaultClient.get('f')).resolves.toBe('x'); + + await new Promise(resolve => setTimeout(resolve, 200)); + + await expect(defaultClient.get('a')).resolves.toBeUndefined(); + await expect(defaultClient.get('b')).resolves.toBeUndefined(); + await expect(defaultClient.get('c')).resolves.toBeUndefined(); + await expect(defaultClient.get('d')).resolves.toBeUndefined(); + await expect(defaultClient.get('e')).resolves.toBeUndefined(); + await expect(defaultClient.get('f')).resolves.toBeUndefined(); + } + }, + ); + + it('rejects invalid defaultTtl', () => { + expect(() => + CacheManager.fromConfig( + mockServices.rootConfig({ + data: { + backend: { + cache: { + store: 'memory', + }, + }, + }, + }), + ), + ).not.toThrow(); + + expect(() => + CacheManager.fromConfig( + mockServices.rootConfig({ + data: { + backend: { + cache: { + store: 'memory', + defaultTtl: 'hello', + }, + }, + }, + }), + ), + ).toThrow(/Invalid configuration backend.cache.defaultTtl/); + }); }); diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts index 873bcf9648..9dd42862c9 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts @@ -21,7 +21,12 @@ import { import { Config } from '@backstage/config'; import Keyv from 'keyv'; import { DefaultCacheClient } from './CacheClient'; -import { CacheManagerOptions, PluginCacheManager } from './types'; +import { + CacheManagerOptions, + PluginCacheManager, + ttlToMilliseconds, +} from './types'; +import { durationToMilliseconds } from '@backstage/types'; type StoreFactory = (pluginId: string, defaultTtl: number | undefined) => Keyv; @@ -63,7 +68,7 @@ export class CacheManager { // If no `backend.cache` config is provided, instantiate the CacheManager // with an in-memory cache client. const store = config.getOptionalString('backend.cache.store') || 'memory'; - const defaultTtl = config.getOptionalNumber('backend.cache.defaultTtl'); + const defaultTtlConfig = config.getOptional('backend.cache.defaultTtl'); const connectionString = config.getOptionalString('backend.cache.connection') || ''; const useRedisSets = @@ -71,6 +76,23 @@ export class CacheManager { const logger = options.logger?.child({ type: 'cacheManager', }); + + let defaultTtl: number | undefined; + if (defaultTtlConfig !== undefined && defaultTtlConfig !== null) { + if (typeof defaultTtlConfig === 'number') { + defaultTtl = defaultTtlConfig; + } else if ( + typeof defaultTtlConfig === 'object' && + !Array.isArray(defaultTtlConfig) + ) { + defaultTtl = durationToMilliseconds(defaultTtlConfig); + } else { + throw new Error( + `Invalid configuration backend.cache.defaultTtl: ${defaultTtlConfig}, expected milliseconds number or HumanDuration object`, + ); + } + } + return new CacheManager( store, connectionString, @@ -111,9 +133,10 @@ export class CacheManager { return { getClient: (defaultOptions = {}) => { const clientFactory = (options: CacheServiceOptions) => { + const ttl = options.defaultTtl ?? this.defaultTtl; return this.getClientWithTtl( pluginId, - options.defaultTtl ?? this.defaultTtl, + ttl !== undefined ? ttlToMilliseconds(ttl) : undefined, ); }; diff --git a/packages/backend-defaults/src/entrypoints/cache/types.ts b/packages/backend-defaults/src/entrypoints/cache/types.ts index ccb3ed5f6d..23c8bcaf3d 100644 --- a/packages/backend-defaults/src/entrypoints/cache/types.ts +++ b/packages/backend-defaults/src/entrypoints/cache/types.ts @@ -19,6 +19,7 @@ import { CacheService, CacheServiceOptions, } from '@backstage/backend-plugin-api'; +import { HumanDuration, durationToMilliseconds } from '@backstage/types'; /** * Options given when constructing a {@link CacheManager}. @@ -44,3 +45,7 @@ export type CacheManagerOptions = { export interface PluginCacheManager { getClient(options?: CacheServiceOptions): CacheService; } + +export function ttlToMilliseconds(ttl: number | HumanDuration): number { + return typeof ttl === 'number' ? ttl : durationToMilliseconds(ttl); +} diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts index 347ec742c0..0d52ff16ff 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts @@ -96,27 +96,83 @@ describe('DatabaseManagerImpl', () => { expect(connector2.getClient).toHaveBeenLastCalledWith('plugin2', undefined); }); - it('retains the migration skip info', async () => { + it('migration skip options take precedence over config', async () => { const connector = { getClient: jest.fn(), dropDatabase: jest.fn(), } satisfies Connector; + const impl = new DatabaseManagerImpl( + new ConfigReader({ + client: 'pg', + backend: { + database: { + skipMigrations: true, + plugin: { plugin1: { skipMigrations: true } }, + }, + }, + }), + { + pg: connector, + }, + { migrations: { skip: false } }, + ); + expect((await impl.forPlugin('plugin1')).migrations).toEqual({ + skip: false, + }); + const impl1 = new DatabaseManagerImpl(new ConfigReader({ client: 'pg' }), { pg: connector, }); - const impl2 = new DatabaseManagerImpl( - new ConfigReader({ client: 'pg' }), - { pg: connector }, - { migrations: { skip: true } }, - ); - expect((await impl1.forPlugin('plugin1')).migrations).toEqual({ skip: false, }); + }); + it('plugin can skip migrations using config', async () => { + const connector = { + getClient: jest.fn(), + dropDatabase: jest.fn(), + } satisfies Connector; + + const impl = new DatabaseManagerImpl( + new ConfigReader({ + client: 'pg', + backend: { + database: { plugin: { plugin1: { skipMigrations: true } } }, + }, + }), + { + pg: connector, + }, + ); + + expect((await impl.forPlugin('plugin1')).migrations).toEqual({ + skip: true, + }); + expect((await impl.forPlugin('plugin2')).migrations).toEqual({ + skip: false, + }); + + const impl2 = new DatabaseManagerImpl( + new ConfigReader({ + client: 'pg', + backend: { + database: { + skipMigrations: true, + plugin: { plugin1: { skipMigrations: false } }, + }, + }, + }), + { + pg: connector, + }, + ); expect((await impl2.forPlugin('plugin1')).migrations).toEqual({ + skip: false, + }); + expect((await impl2.forPlugin('plugin2')).migrations).toEqual({ skip: true, }); }); diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts index 7e3adf17df..b3e69f0ae2 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts @@ -86,8 +86,16 @@ export class DatabaseManagerImpl implements LegacyRootDatabaseService { ); } const getClient = () => this.getDatabase(pluginId, connector, deps); - const migrations = { skip: false, ...this.options?.migrations }; - return { getClient, migrations }; + + const skip = + this.options?.migrations?.skip ?? + this.config.getOptionalBoolean( + `backend.database.plugin.${pluginId}.skipMigrations`, + ) ?? + this.config.getOptionalBoolean('backend.database.skipMigrations') ?? + false; + + return { getClient, migrations: { skip } }; } /** diff --git a/packages/backend-defaults/src/entrypoints/httpRouter/createCredentialsBarrier.test.ts b/packages/backend-defaults/src/entrypoints/httpRouter/createCredentialsBarrier.test.ts index 124acf277b..22c6c90f05 100644 --- a/packages/backend-defaults/src/entrypoints/httpRouter/createCredentialsBarrier.test.ts +++ b/packages/backend-defaults/src/entrypoints/httpRouter/createCredentialsBarrier.test.ts @@ -19,13 +19,11 @@ import express from 'express'; import request from 'supertest'; import { createCredentialsBarrier } from './createCredentialsBarrier'; -import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; -import { MiddlewareFactory } from '../rootHttpRouter/http'; - -const errorMiddleware = MiddlewareFactory.create({ - config: mockServices.rootConfig(), - logger: mockServices.rootLogger(), -}).error(); +import { + mockCredentials, + mockErrorHandler, + mockServices, +} from '@backstage/backend-test-utils'; function setup() { const barrier = createCredentialsBarrier({ @@ -37,7 +35,7 @@ function setup() { const app = express(); app.use(barrier.middleware); - app.use(errorMiddleware); + app.use(mockErrorHandler()); app.get('*', (_req, res) => res.status(200).end()); return { app, barrier }; diff --git a/packages/backend-defaults/src/entrypoints/httpRouter/httpRouterServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/httpRouter/httpRouterServiceFactory.test.ts index a8b749939b..3c721c9ed9 100644 --- a/packages/backend-defaults/src/entrypoints/httpRouter/httpRouterServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/httpRouter/httpRouterServiceFactory.test.ts @@ -110,7 +110,7 @@ describe('httpRouterFactory', () => { }); const defaultServices = [ - httpRouterServiceFactory(), + httpRouterServiceFactory, mockServices.httpAuth.factory({ defaultCredentials: mockCredentials.none(), }), diff --git a/packages/backend-defaults/src/entrypoints/permissions/permissionsServiceFactory.ts b/packages/backend-defaults/src/entrypoints/permissions/permissionsServiceFactory.ts index 873d6a4fa0..000d71965b 100644 --- a/packages/backend-defaults/src/entrypoints/permissions/permissionsServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/permissions/permissionsServiceFactory.ts @@ -35,13 +35,11 @@ export const permissionsServiceFactory = createServiceFactory({ auth: coreServices.auth, config: coreServices.rootConfig, discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, }, - async factory({ auth, config, discovery, tokenManager }) { + async factory({ auth, config, discovery }) { return ServerPermissionClient.fromConfig(config, { auth, discovery, - tokenManager, }); }, }); diff --git a/packages/backend-defaults/src/entrypoints/rootConfig/rootConfigServiceFactory.ts b/packages/backend-defaults/src/entrypoints/rootConfig/rootConfigServiceFactory.ts index 63a7353241..ab31ec4850 100644 --- a/packages/backend-defaults/src/entrypoints/rootConfig/rootConfigServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/rootConfig/rootConfigServiceFactory.ts @@ -60,7 +60,7 @@ export const rootConfigServiceFactoryWithOptions = ( console.log(`Loading config from ${source}`); return await ConfigSources.toConfig(source); }, - })(); + }); /** * @public diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts index c3a97737e4..a18ddbe096 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts @@ -123,7 +123,7 @@ const rootHttpRouterServiceFactoryWithOptions = ( return router; }, - })(); + }); /** @public */ export const rootHttpRouterServiceFactory = Object.assign( diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts index f8d06baf56..5c3af24ab1 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts @@ -23,13 +23,15 @@ import { SchedulerServiceTaskRunner, SchedulerServiceTaskScheduleDefinition, } from '@backstage/backend-plugin-api'; -import { Counter, Histogram, metrics } from '@opentelemetry/api'; +import { Counter, Histogram, metrics, trace } from '@opentelemetry/api'; import { Knex } from 'knex'; import { Duration } from 'luxon'; import { LocalTaskWorker } from './LocalTaskWorker'; import { TaskWorker } from './TaskWorker'; import { TaskSettingsV2 } from './types'; -import { validateId } from './util'; +import { TRACER_ID, validateId } from './util'; + +const tracer = trace.getTracer(TRACER_ID); /** * Implements the actual task management. @@ -85,7 +87,7 @@ export class PluginTaskSchedulerImpl implements SchedulerService { const knex = await this.databaseFactory(); const worker = new TaskWorker( task.id, - this.wrapInMetrics(task.fn, { labels: { taskId: task.id, scope } }), + this.instrumentedFunction(task, scope), knex, this.logger.child({ task: task.id }), ); @@ -93,7 +95,7 @@ export class PluginTaskSchedulerImpl implements SchedulerService { } else { const worker = new LocalTaskWorker( task.id, - this.wrapInMetrics(task.fn, { labels: { taskId: task.id, scope } }), + this.instrumentedFunction(task, scope), this.logger.child({ task: task.id }), ); worker.start(settings, { signal: task.signal }); @@ -121,20 +123,33 @@ export class PluginTaskSchedulerImpl implements SchedulerService { return this.allScheduledTasks; } - private wrapInMetrics( - fn: SchedulerServiceTaskFunction, - opts: { labels: Record }, + private instrumentedFunction( + task: SchedulerServiceTaskInvocationDefinition, + scope: string, ): SchedulerServiceTaskFunction { return async abort => { - const labels = { - ...opts.labels, + const labels: Record = { + taskId: task.id, + scope, }; this.counter.add(1, { ...labels, result: 'started' }); const startTime = process.hrtime(); try { - await fn(abort); + await tracer.startActiveSpan(`task ${task.id}`, async span => { + try { + span.setAttributes(labels); + await task.fn(abort); + } catch (error) { + if (error instanceof Error) { + span.recordException(error); + } + throw error; + } finally { + span.end(); + } + }); labels.result = 'completed'; } catch (ex) { labels.result = 'failed'; diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/util.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/util.ts index 70d67a9fbe..c475a20b80 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/util.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/util.ts @@ -18,6 +18,8 @@ import { InputError } from '@backstage/errors'; import { Knex } from 'knex'; import { DateTime, Duration } from 'luxon'; +export const TRACER_ID = 'backstage-service-scheduler'; + // Keep the IDs compatible with e.g. Prometheus labels export function validateId(id: string) { if (typeof id !== 'string' || !id.trim()) { diff --git a/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.test.ts index b16509cf96..2d84f6ce87 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.test.ts @@ -20,7 +20,7 @@ import { schedulerServiceFactory } from './schedulerServiceFactory'; describe('schedulerFactory', () => { it('creates sidecar database features', async () => { - const tester = ServiceFactoryTester.from(schedulerServiceFactory()); + const tester = ServiceFactoryTester.from(schedulerServiceFactory); const scheduler = await tester.getSubject(); await scheduler.scheduleTask({ diff --git a/packages/backend-defaults/src/index.ts b/packages/backend-defaults/src/index.ts index b00c865524..5f26df8921 100644 --- a/packages/backend-defaults/src/index.ts +++ b/packages/backend-defaults/src/index.ts @@ -21,3 +21,4 @@ */ export { createBackend } from './CreateBackend'; +export { discoveryFeatureLoader } from './discoveryFeatureLoader'; diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index 5828cebe32..902f805fbd 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,72 @@ # @backstage/backend-dynamic-feature-service +## 0.4.0-next.0 + +### Minor Changes + +- 9080f57: **BREAKING**: `dynamicPluginsServiceFactory` is no longer callable as a function. If you need to provide options to make a custom factory, use `dynamicPluginsSchemasServiceFactoryWithOptions` instead. + +### Patch Changes + +- cd38da8: Deprecate the `dynamicPluginsServiceRef`, `dynamicPluginsServiceFactory` and `dynamicPluginsServiceFactoryWithOptions` in favor of using the `dynamicPluginsFeatureDiscoveryLoader` to discover dynamic features in a new backend system. + + See usage examples below: + + Example using the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + + ```ts + import { createBackend } from '@backstage/backend-defaults'; + import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; + //... + + const backend = createBackend(); + backend.add(dynamicPluginsFeatureDiscoveryLoader); + //... + backend.start(); + ``` + + Passing options to the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + + ```ts + import { createBackend } from '@backstage/backend-defaults'; + import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; + import { myCustomModuleLoader } from './myCustomModuleLoader'; + //... + + const backend = createBackend(); + backend.add( + dynamicPluginsFeatureDiscoveryLoader({ + moduleLoader: myCustomModuleLoader, + }), + ); + //... + backend.start(); + ``` + +- e27f889: Relax type check for a plugin's default export to also accept a BackendFeature defined as a function instead of an object +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-app-api@0.10.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-app-node@0.1.25-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-backend@0.3.12-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.3.0 ### Minor Changes diff --git a/packages/backend-dynamic-feature-service/api-report.md b/packages/backend-dynamic-feature-service/api-report.md index d7a4b8b75d..f6972da50b 100644 --- a/packages/backend-dynamic-feature-service/api-report.md +++ b/packages/backend-dynamic-feature-service/api-report.md @@ -4,7 +4,6 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; import { BackstagePackageJson } from '@backstage/cli-node'; import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; @@ -29,7 +28,7 @@ import { RootLoggerService } from '@backstage/backend-plugin-api'; import { Router } from 'express'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { TokenManager } from '@backstage/backend-common'; @@ -113,23 +112,27 @@ export interface DynamicPluginsFactoryOptions { moduleLoader?(logger: LoggerService): ModuleLoader; } -// @public (undocumented) -export const dynamicPluginsFeatureDiscoveryServiceFactory: ServiceFactoryCompat< +// @public +export const dynamicPluginsFeatureDiscoveryLoader: (( + options?: DynamicPluginsFactoryOptions, +) => BackendFeature) & + BackendFeature; + +// @public @deprecated (undocumented) +export const dynamicPluginsFeatureDiscoveryServiceFactory: ServiceFactory< FeatureDiscoveryService, 'root', - 'singleton', - undefined + 'singleton' >; // @public (undocumented) -export const dynamicPluginsFrontendSchemas: BackendFeatureCompat; +export const dynamicPluginsFrontendSchemas: BackendFeature; // @public (undocumented) -export const dynamicPluginsRootLoggerServiceFactory: ServiceFactoryCompat< +export const dynamicPluginsRootLoggerServiceFactory: ServiceFactory< RootLoggerService, 'root', - 'singleton', - undefined + 'singleton' >; // @public (undocumented) @@ -146,22 +149,30 @@ export interface DynamicPluginsSchemasService { } // @public (undocumented) -export const dynamicPluginsSchemasServiceFactory: ServiceFactoryCompat< +export const dynamicPluginsSchemasServiceFactory: ServiceFactory< DynamicPluginsSchemasService, 'root', - 'singleton', - DynamicPluginsSchemasOptions + 'singleton' >; // @public (undocumented) -export const dynamicPluginsServiceFactory: ServiceFactoryCompat< +export const dynamicPluginsSchemasServiceFactoryWithOptions: ( + options?: DynamicPluginsSchemasOptions, +) => ServiceFactory; + +// @public @deprecated (undocumented) +export const dynamicPluginsServiceFactory: ServiceFactory< DynamicPluginProvider, 'root', - 'singleton', - DynamicPluginsFactoryOptions + 'singleton' >; -// @public (undocumented) +// @public @deprecated (undocumented) +export const dynamicPluginsServiceFactoryWithOptions: ( + options?: DynamicPluginsFactoryOptions, +) => ServiceFactory; + +// @public @deprecated (undocumented) export const dynamicPluginsServiceRef: ServiceRef< DynamicPluginProvider, 'root', diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index ac06fcd8d0..20ef7790f6 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.3.0", + "version": "0.4.0-next.0", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/src/manager/index.ts b/packages/backend-dynamic-feature-service/src/manager/index.ts index dd6aaacfe9..d24a41a7a3 100644 --- a/packages/backend-dynamic-feature-service/src/manager/index.ts +++ b/packages/backend-dynamic-feature-service/src/manager/index.ts @@ -34,7 +34,9 @@ export { DynamicPluginManager, dynamicPluginsFeatureDiscoveryServiceFactory, dynamicPluginsServiceFactory, + dynamicPluginsServiceFactoryWithOptions, dynamicPluginsServiceRef, + dynamicPluginsFeatureDiscoveryLoader, } from './plugin-manager'; export type { diff --git a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts index 9c45cc53f6..5c01a3dfe5 100644 --- a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts +++ b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts @@ -16,7 +16,7 @@ import { DynamicPluginManager, - dynamicPluginsServiceFactory, + dynamicPluginsServiceFactoryWithOptions, } from './plugin-manager'; import { BackendFeature, @@ -246,6 +246,59 @@ describe('backend-dynamic-feature-service', () => { >([]); }, }, + { + name: 'should successfully load a backend plugin wrapped in a BackendFeatureCompatWrapper function', + packageManifest: { + name: 'backend-dynamic-plugin-test', + version: '0.0.0', + backstage: { + role: 'backend-plugin', + }, + main: 'dist/index.cjs.js', + }, + indexFile: { + relativePath: ['dist', 'index.cjs.js'], + content: ` + function backendFeatureCompatWrapper() { + return backendFeatureCompatWrapper; + } + Object.assign(backendFeatureCompatWrapper, { + $$type: "@backstage/BackendFeature", + version: "v1", + }); + const alpha = backendFeatureCompatWrapper; + exports.default = alpha; + `, + }, + expectedLogs(location) { + return { + infos: [ + { + message: `loaded dynamic backend plugin 'backend-dynamic-plugin-test' from '${location}'`, + }, + ], + }; + }, + checkLoadedPlugins(plugins) { + expect(plugins).toMatchObject([ + { + name: 'backend-dynamic-plugin-test', + version: '0.0.0', + role: 'backend-plugin', + platform: 'node', + installer: { + kind: 'new', + }, + }, + ]); + const installer: NewBackendPluginInstaller = ( + plugins[0] as BackendDynamicPlugin + ).installer as NewBackendPluginInstaller; + expect((installer.install() as BackendFeature).$$type).toEqual( + '@backstage/BackendFeature', + ); + }, + }, { name: 'should fail when no index file', packageManifest: { @@ -637,7 +690,7 @@ describe('backend-dynamic-feature-service', () => { const backend = createSpecializedBackend({ defaultServiceFactories: [ - rootLifecycleServiceFactory(), + rootLifecycleServiceFactory, createServiceFactory({ service: coreServices.rootConfig, deps: {}, @@ -670,7 +723,7 @@ describe('backend-dynamic-feature-service', () => { return rootLogger; }, }), - dynamicPluginsServiceFactory({ + dynamicPluginsServiceFactoryWithOptions({ moduleLoader: _ => mockedModuleLoader, }), ], diff --git a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts index 95b0c6592c..fef0b6000d 100644 --- a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts +++ b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts @@ -30,6 +30,7 @@ import { BackendFeature, LoggerService, coreServices, + createBackendFeatureLoader, createServiceFactory, createServiceRef, } from '@backstage/backend-plugin-api'; @@ -214,6 +215,7 @@ export class DynamicPluginManager implements DynamicPluginProvider { /** * @public + * @deprecated The `featureDiscoveryService` is deprecated in favor of using {@link dynamicPluginsFeatureDiscoveryLoader} instead. */ export const dynamicPluginsServiceRef = createServiceRef( { @@ -231,9 +233,12 @@ export interface DynamicPluginsFactoryOptions { /** * @public + * @deprecated Use {@link dynamicPluginsFeatureDiscoveryLoader} instead. */ -export const dynamicPluginsServiceFactory = createServiceFactory( - (options?: DynamicPluginsFactoryOptions) => ({ +export const dynamicPluginsServiceFactoryWithOptions = ( + options?: DynamicPluginsFactoryOptions, +) => + createServiceFactory({ service: dynamicPluginsServiceRef, deps: { config: coreServices.rootConfig, @@ -247,8 +252,14 @@ export const dynamicPluginsServiceFactory = createServiceFactory( moduleLoader: options?.moduleLoader?.(logger), }); }, - }), -); + }); + +/** + * @public + * @deprecated Use {@link dynamicPluginsFeatureDiscoveryLoader} instead. + */ +export const dynamicPluginsServiceFactory = + dynamicPluginsServiceFactoryWithOptions(); class DynamicPluginsEnabledFeatureDiscoveryService implements FeatureDiscoveryService @@ -285,6 +296,7 @@ class DynamicPluginsEnabledFeatureDiscoveryService /** * @public + * @deprecated The `featureDiscoveryService` is deprecated in favor of using {@link dynamicPluginsFeatureDiscoveryLoader} instead. */ export const dynamicPluginsFeatureDiscoveryServiceFactory = createServiceFactory({ @@ -298,10 +310,70 @@ export const dynamicPluginsFeatureDiscoveryServiceFactory = }, }); +const dynamicPluginsFeatureDiscoveryLoaderWithOptions = ( + options?: DynamicPluginsFactoryOptions, +) => + createBackendFeatureLoader({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.rootLogger, + }, + async loader({ config, logger }) { + const manager = await DynamicPluginManager.create({ + config, + logger, + preferAlpha: true, + moduleLoader: options?.moduleLoader?.(logger), + }); + const service = new DynamicPluginsEnabledFeatureDiscoveryService(manager); + const { features } = await service.getBackendFeatures(); + return features; + }, + }); + +/** + * A backend feature loader that uses the dynamic plugins system to discover features. + * + * @public + * + * @example + * Using the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + * ```ts + * //... + * import { createBackend } from '@backstage/backend-defaults'; + * import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; + * + * const backend = createBackend(); + * backend.add(dynamicPluginsFeatureDiscoveryLoader); + * //... + * backend.start(); + * ``` + * + * @example + * Passing options to the `dynamicPluginsFeatureDiscoveryLoader` loader in a backend instance: + * ```ts + * //... + * import { createBackend } from '@backstage/backend-defaults'; + * import { dynamicPluginsFeatureDiscoveryLoader } from '@backstage/backend-dynamic-feature-service'; + * import { myCustomModuleLoader } from './myCustomModuleLoader'; + * + * const backend = createBackend(); + * backend.add(dynamicPluginsFeatureDiscoveryLoader({ + * moduleLoader: myCustomModuleLoader + * })); + * //... + * backend.start(); + * ``` + */ +export const dynamicPluginsFeatureDiscoveryLoader = Object.assign( + dynamicPluginsFeatureDiscoveryLoaderWithOptions, + dynamicPluginsFeatureDiscoveryLoaderWithOptions(), +); + function isBackendFeature(value: unknown): value is BackendFeature { return ( !!value && - typeof value === 'object' && + (typeof value === 'object' || typeof value === 'function') && (value as BackendFeature).$$type === '@backstage/BackendFeature' ); } diff --git a/packages/backend-dynamic-feature-service/src/schemas/index.ts b/packages/backend-dynamic-feature-service/src/schemas/index.ts index 94c167679b..14c6734b6b 100644 --- a/packages/backend-dynamic-feature-service/src/schemas/index.ts +++ b/packages/backend-dynamic-feature-service/src/schemas/index.ts @@ -16,6 +16,7 @@ export { dynamicPluginsSchemasServiceFactory, + dynamicPluginsSchemasServiceFactoryWithOptions, type DynamicPluginsSchemasService, type DynamicPluginsSchemasOptions, } from './schemas'; diff --git a/packages/backend-dynamic-feature-service/src/schemas/schemas.ts b/packages/backend-dynamic-feature-service/src/schemas/schemas.ts index 0c03e93ada..1cfb42a093 100644 --- a/packages/backend-dynamic-feature-service/src/schemas/schemas.ts +++ b/packages/backend-dynamic-feature-service/src/schemas/schemas.ts @@ -71,8 +71,10 @@ export interface DynamicPluginsSchemasOptions { /** * @public */ -export const dynamicPluginsSchemasServiceFactory = createServiceFactory( - (options?: DynamicPluginsSchemasOptions) => ({ +export const dynamicPluginsSchemasServiceFactoryWithOptions = ( + options?: DynamicPluginsSchemasOptions, +) => + createServiceFactory({ service: dynamicPluginsSchemasServiceRef, deps: { config: coreServices.rootConfig, @@ -137,8 +139,13 @@ export const dynamicPluginsSchemasServiceFactory = createServiceFactory( }, }; }, - }), -); + }); + +/** + * @public + */ +export const dynamicPluginsSchemasServiceFactory = + dynamicPluginsSchemasServiceFactoryWithOptions(); /** @internal */ async function gatherDynamicPluginsSchemas( diff --git a/packages/backend-legacy/CHANGELOG.md b/packages/backend-legacy/CHANGELOG.md index 3b6a1af6f4..df3dce4cf6 100644 --- a/packages/backend-legacy/CHANGELOG.md +++ b/packages/backend-legacy/CHANGELOG.md @@ -1,5 +1,47 @@ # example-backend-legacy +## 0.2.102-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-backend@1.10.13-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-search-backend@1.5.17-next.0 + - @backstage/plugin-kubernetes-backend@0.18.6-next.0 + - @backstage/plugin-scaffolder-backend@1.25.0-next.0 + - @backstage/plugin-app-backend@0.3.74-next.0 + - @backstage/plugin-signals-backend@0.2.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.0 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-backend@0.3.12-next.0 + - @backstage/plugin-permission-backend@0.5.49-next.0 + - @backstage/plugin-proxy-backend@0.5.6-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.5.6-next.0 + - @backstage/plugin-search-backend-module-pg@0.5.35-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.0 + - @backstage/plugin-devtools-backend@0.4.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-permission-common@0.8.1 + ## 0.2.101 ### Patch Changes diff --git a/packages/backend-legacy/package.json b/packages/backend-legacy/package.json index 0bdfa350e9..8d2eb61566 100644 --- a/packages/backend-legacy/package.json +++ b/packages/backend-legacy/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-legacy", - "version": "0.2.101", + "version": "0.2.102-next.0", "backstage": { "role": "backend" }, diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 0d58c6ad9c..1393b93790 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-openapi-utils +## 0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/errors@1.2.4 + ## 0.1.16 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index a64ef674a9..2ae545af9f 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-openapi-utils", "description": "OpenAPI typescript support.", - "version": "0.1.16", + "version": "0.1.18-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index cb830feb33..2356b1714d 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/backend-plugin-api +## 0.9.0-next.0 + +### Minor Changes + +- 19ff127: **BREAKING**: The deprecated identity and token manager services have been removed. This means that `coreServices.identity` and `coreServices.tokenManager` are gone, along with related types and utilities in other packages. +- f687050: Removed the following deprecated exports + + - `BackendPluginConfig` use `CreateBackendPluginOptions` + - `BackendModuleConfig` use `CreateBackendModuleOptions` + - `ExtensionPointConfig` use `CreateExtensionPointOptions` + +- 4d82481: Removed deprecated `ServiceFactoryOrFunction` type. +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- cd38da8: Deprecate the `featureDiscoveryServiceRef` in favor of using the new `discoveryFeatureLoader` instead. +- 66dbf0a: Allow the cache service to accept the human duration format for TTL +- 0b2a402: Updates to the config schema to match reality +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + ## 0.8.0 ### Minor Changes diff --git a/packages/backend-plugin-api/api-report-alpha.md b/packages/backend-plugin-api/api-report-alpha.md index 0f4348336a..dea357502e 100644 --- a/packages/backend-plugin-api/api-report-alpha.md +++ b/packages/backend-plugin-api/api-report-alpha.md @@ -14,7 +14,7 @@ export interface FeatureDiscoveryService { }>; } -// @alpha +// @alpha @deprecated export const featureDiscoveryServiceRef: ServiceRef< FeatureDiscoveryService, 'root', diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 5ea07ead7a..89dfe40dd9 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -12,7 +12,6 @@ import { Duration } from 'luxon'; import { EvaluatorRequestOptions } from '@backstage/plugin-permission-common'; import { Handler } from 'express'; import { HumanDuration } from '@backstage/types'; -import { IdentityApi } from '@backstage/plugin-auth-node'; import { isChildPath } from '@backstage/cli-common'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; @@ -64,15 +63,6 @@ export interface BackendFeature { $$type: '@backstage/BackendFeature'; } -// @public @deprecated (undocumented) -export interface BackendFeatureCompat extends BackendFeature { - // @deprecated (undocumented) - (): this; -} - -// @public @deprecated (undocumented) -export type BackendModuleConfig = CreateBackendModuleOptions; - // @public export interface BackendModuleRegistrationPoints { // (undocumented) @@ -91,9 +81,6 @@ export interface BackendModuleRegistrationPoints { }): void; } -// @public @deprecated (undocumented) -export type BackendPluginConfig = CreateBackendPluginOptions; - // @public export interface BackendPluginRegistrationPoints { // (undocumented) @@ -175,12 +162,12 @@ export interface CacheService { // @public export type CacheServiceOptions = { - defaultTtl?: number; + defaultTtl?: number | HumanDuration; }; // @public export type CacheServiceSetOptions = { - ttl?: number; + ttl?: number | HumanDuration; }; // @public @@ -206,11 +193,7 @@ export namespace coreServices { const rootLifecycle: ServiceRef; const rootLogger: ServiceRef; const scheduler: ServiceRef; - const // @deprecated - tokenManager: ServiceRef; const urlReader: ServiceRef; - const // @deprecated - identity: ServiceRef; } // @public @@ -257,7 +240,7 @@ export interface CreateBackendFeatureLoaderOptions< // @public export function createBackendModule( options: CreateBackendModuleOptions, -): BackendFeatureCompat; +): BackendFeature; // @public export interface CreateBackendModuleOptions { @@ -270,7 +253,7 @@ export interface CreateBackendModuleOptions { // @public export function createBackendPlugin( options: CreateBackendPluginOptions, -): BackendFeatureCompat; +): BackendFeature; // @public export interface CreateBackendPluginOptions { @@ -297,25 +280,9 @@ export function createServiceFactory< TDeps extends { [name in string]: ServiceRef; }, - TOpts extends object | undefined = undefined, >( options: RootServiceFactoryOptions, -): ServiceFactoryCompat; - -// @public @deprecated -export function createServiceFactory< - TService, - TInstances extends 'singleton' | 'multiton', - TImpl extends TService, - TDeps extends { - [name in string]: ServiceRef; - }, - TOpts extends object | undefined = undefined, ->( - options: ( - options?: TOpts, - ) => RootServiceFactoryOptions, -): ServiceFactoryCompat; +): ServiceFactory; // @public export function createServiceFactory< @@ -326,7 +293,6 @@ export function createServiceFactory< [name in string]: ServiceRef; }, TContext = undefined, - TOpts extends object | undefined = undefined, >( options: PluginServiceFactoryOptions< TService, @@ -335,29 +301,7 @@ export function createServiceFactory< TImpl, TDeps >, -): ServiceFactoryCompat; - -// @public @deprecated -export function createServiceFactory< - TService, - TInstances extends 'singleton' | 'multiton', - TImpl extends TService, - TDeps extends { - [name in string]: ServiceRef; - }, - TContext = undefined, - TOpts extends object | undefined = undefined, ->( - options: ( - options?: TOpts, - ) => PluginServiceFactoryOptions< - TService, - TInstances, - TContext, - TImpl, - TDeps - >, -): ServiceFactoryCompat; +): ServiceFactory; // @public export function createServiceRef( @@ -401,9 +345,6 @@ export type ExtensionPoint = { $$type: '@backstage/ExtensionPoint'; }; -// @public @deprecated (undocumented) -export type ExtensionPointConfig = CreateExtensionPointOptions; - // @public export interface HttpAuthService { credentials( @@ -437,9 +378,6 @@ export interface HttpRouterServiceAuthPolicy { path: string; } -// @public @deprecated -export interface IdentityService extends IdentityApi {} - export { isChildPath }; // @public @@ -576,7 +514,7 @@ export interface RootLoggerService extends LoggerService {} // @public (undocumented) export interface RootServiceFactoryOptions< - TService, // TODO(Rugvip): Can we forward the entire service ref type here instead of forwarding each type arg once the callback form is gone? + TService, TInstances extends 'singleton' | 'multiton', TImpl extends TService, TDeps extends { @@ -677,22 +615,6 @@ export interface ServiceFactory< service: ServiceRef; } -// @public @deprecated (undocumented) -export interface ServiceFactoryCompat< - TService = unknown, - TScope extends 'plugin' | 'root' = 'plugin' | 'root', - TInstances extends 'singleton' | 'multiton' = 'singleton' | 'multiton', - TOpts extends object | undefined = undefined, -> extends ServiceFactory { - // @deprecated (undocumented) - ( - ...options: undefined extends TOpts ? [] : [options?: TOpts] - ): ServiceFactory; -} - -// @public @deprecated -export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory); - // @public export type ServiceRef< TService, @@ -716,10 +638,6 @@ export interface ServiceRefOptions< defaultFactory?( service: ServiceRef, ): Promise; - // @deprecated (undocumented) - defaultFactory?( - service: ServiceRef, - ): Promise<() => ServiceFactory>; // (undocumented) id: string; // (undocumented) @@ -728,14 +646,6 @@ export interface ServiceRefOptions< scope?: TScope; } -// @public @deprecated -export interface TokenManagerService { - authenticate(token: string): Promise; - getToken(): Promise<{ - token: string; - }>; -} - // @public export interface UrlReaderService { readTree( diff --git a/packages/backend-plugin-api/config.d.ts b/packages/backend-plugin-api/config.d.ts new file mode 100644 index 0000000000..21122d7238 --- /dev/null +++ b/packages/backend-plugin-api/config.d.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 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 interface Config { + backend?: { + /** + * An absolute path to a directory that can be used as a working dir, for + * example as scratch space for large operations. + * + * @remarks + * + * Note that this must be an absolute path. + * + * If not set, the operating system's designated temporary directory is + * commonly used, but that is implementation defined per plugin. + * + * Plugins are encouraged to heed this config setting if present, to allow + * deployment in severely locked-down or limited environments. + */ + workingDirectory?: string; + }; +} diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 94793e1e49..6a3392529d 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-plugin-api", - "version": "0.8.0", + "version": "0.9.0-next.0", "description": "Core API used by Backstage backend plugins", "backstage": { "role": "node-library" @@ -40,7 +40,8 @@ } }, "files": [ - "dist" + "dist", + "config.d.ts" ], "scripts": { "build": "backstage-cli package build", @@ -67,5 +68,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^" - } + }, + "configSchema": "config.d.ts" } diff --git a/packages/backend-plugin-api/src/alpha.ts b/packages/backend-plugin-api/src/alpha.ts index baee739f49..3f26c66fe9 100644 --- a/packages/backend-plugin-api/src/alpha.ts +++ b/packages/backend-plugin-api/src/alpha.ts @@ -27,6 +27,7 @@ export interface FeatureDiscoveryService { /** * An optional service that can be used to dynamically load in additional BackendFeatures at runtime. * @alpha + * @deprecated The `featureDiscoveryServiceRef` is deprecated in favor of using {@link @backstage/backend-defaults#discoveryFeatureLoader} instead. */ export const featureDiscoveryServiceRef = createServiceRef({ diff --git a/packages/backend-plugin-api/src/index.ts b/packages/backend-plugin-api/src/index.ts index e6e57db1dc..c2e2a13a89 100644 --- a/packages/backend-plugin-api/src/index.ts +++ b/packages/backend-plugin-api/src/index.ts @@ -21,6 +21,6 @@ */ export * from './services'; -export type { BackendFeature, BackendFeatureCompat } from './types'; +export type { BackendFeature } from './types'; export * from './paths'; export * from './wiring'; diff --git a/packages/backend-plugin-api/src/services/definitions/CacheService.ts b/packages/backend-plugin-api/src/services/definitions/CacheService.ts index 3b13292a58..4c5cfd74b3 100644 --- a/packages/backend-plugin-api/src/services/definitions/CacheService.ts +++ b/packages/backend-plugin-api/src/services/definitions/CacheService.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { JsonValue } from '@backstage/types'; +import { HumanDuration, JsonValue } from '@backstage/types'; /** * Options passed to {@link CacheService.set}. @@ -23,10 +23,10 @@ import { JsonValue } from '@backstage/types'; */ export type CacheServiceSetOptions = { /** - * Optional TTL in milliseconds. Defaults to the TTL provided when the client + * Optional TTL (in milliseconds if given as a number). Defaults to the TTL provided when the client * was set up (or no TTL if none are provided). */ - ttl?: number; + ttl?: number | HumanDuration; }; /** @@ -36,11 +36,11 @@ export type CacheServiceSetOptions = { */ export type CacheServiceOptions = { /** - * An optional default TTL (in milliseconds) to be set when getting a client + * An optional default TTL (in milliseconds if given as a number) to be set when getting a client * instance. If not provided, data will persist indefinitely by default (or * can be configured per entry at set-time). */ - defaultTtl?: number; + defaultTtl?: number | HumanDuration; }; /** diff --git a/packages/backend-plugin-api/src/services/definitions/IdentityService.ts b/packages/backend-plugin-api/src/services/definitions/IdentityService.ts deleted file mode 100644 index daa45b8200..0000000000 --- a/packages/backend-plugin-api/src/services/definitions/IdentityService.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { IdentityApi } from '@backstage/plugin-auth-node'; - -/** - * This is the legacy service for identity handling in Backstage. Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead. - * - * See the {@link https://backstage.io/docs/backend-system/core-services/identity | service documentation} for more details. - * - * @public - * @deprecated Please {@link https://backstage.io/docs/tutorials/auth-service-migration | migrate} to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead. - */ -export interface IdentityService extends IdentityApi {} diff --git a/packages/backend-plugin-api/src/services/definitions/TokenManagerService.ts b/packages/backend-plugin-api/src/services/definitions/TokenManagerService.ts deleted file mode 100644 index 52d5d1ff9f..0000000000 --- a/packages/backend-plugin-api/src/services/definitions/TokenManagerService.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ - -/** - * This is the legacy service for creating and validating tokens. Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead. - * - * See the {@link https://backstage.io/docs/backend-system/core-services/token-manager | service documentation} for more details. - * - * @public - * @deprecated Please {@link https://backstage.io/docs/tutorials/auth-service-migration | migrate} to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead. - */ -export interface TokenManagerService { - /** - * Fetches a valid token. - * - * @remarks - * - * Tokens are valid for roughly one hour; the actual deadline is set in the - * payload `exp` claim. Never hold on to tokens for reuse; always ask for a - * new one for each outgoing request. This ensures that you always get a - * valid, fresh one. - */ - getToken(): Promise<{ token: string }>; - - /** - * Validates a given token. - */ - authenticate(token: string): Promise; -} diff --git a/packages/backend-plugin-api/src/services/definitions/coreServices.ts b/packages/backend-plugin-api/src/services/definitions/coreServices.ts index 33d1da2903..f87bff023c 100644 --- a/packages/backend-plugin-api/src/services/definitions/coreServices.ts +++ b/packages/backend-plugin-api/src/services/definitions/coreServices.ts @@ -239,20 +239,6 @@ export namespace coreServices { import('./SchedulerService').SchedulerService >({ id: 'core.scheduler' }); - /** - * Deprecated service authentication service, use the `auth` service instead. - * - * See {@link TokenManagerService} - * and {@link https://backstage.io/docs/backend-system/core-services/token-manager | the service docs} - * for more information. - * - * @public - * @deprecated Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead - */ - export const tokenManager = createServiceRef< - import('./TokenManagerService').TokenManagerService - >({ id: 'core.tokenManager' }); - /** * Reading content from external systems. * @@ -265,18 +251,4 @@ export namespace coreServices { export const urlReader = createServiceRef< import('./UrlReaderService').UrlReaderService >({ id: 'core.urlReader' }); - - /** - * Deprecated user authentication service, use the `auth` service instead. - * - * See {@link IdentityService} - * and {@link https://backstage.io/docs/backend-system/core-services/identity | the service docs} - * for more information. - * - * @public - * @deprecated Please migrate to the new `coreServices.auth`, `coreServices.httpAuth`, and `coreServices.userInfo` services as needed instead - */ - export const identity = createServiceRef< - import('./IdentityService').IdentityService - >({ id: 'core.identity' }); } diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index 194658dc0e..1df00bdead 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -64,7 +64,6 @@ export type { SchedulerServiceTaskScheduleDefinition, SchedulerServiceTaskScheduleDefinitionConfig, } from './SchedulerService'; -export type { TokenManagerService } from './TokenManagerService'; export type { UrlReaderServiceReadTreeOptions, UrlReaderServiceReadTreeResponse, @@ -78,4 +77,3 @@ export type { UrlReaderService, } from './UrlReaderService'; export type { BackstageUserInfo, UserInfoService } from './UserInfoService'; -export type { IdentityService } from './IdentityService'; diff --git a/packages/backend-plugin-api/src/services/system/index.ts b/packages/backend-plugin-api/src/services/system/index.ts index c2d3b9c6fc..82afa904fe 100644 --- a/packages/backend-plugin-api/src/services/system/index.ts +++ b/packages/backend-plugin-api/src/services/system/index.ts @@ -20,7 +20,5 @@ export type { ServiceFactory, PluginServiceFactoryOptions, RootServiceFactoryOptions, - ServiceFactoryOrFunction, - ServiceFactoryCompat, } from './types'; export { createServiceRef, createServiceFactory } from './types'; diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index 52322f998b..a98b8c44ee 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -23,15 +23,11 @@ import { const ref = createServiceRef({ id: 'x' }); const rootDep = createServiceRef({ id: 'y', scope: 'root' }); const pluginDep = createServiceRef({ id: 'z' }); - -interface TestOptions { - x: number; -} function unused(..._any: any[]) {} describe('createServiceFactory', () => { - it('should create a sync factory', () => { - const metaFactory = createServiceFactory({ + it('should create a plugin scoped factory', () => { + const factory = createServiceFactory({ service: ref, deps: {}, createRootContext() {}, @@ -39,52 +35,26 @@ describe('createServiceFactory', () => { return 'x'; }, }); - expect(metaFactory).toEqual(expect.any(Function)); - expect(metaFactory().$$type).toBe('@backstage/BackendFeature'); - expect((metaFactory() as InternalServiceFactory).featureType).toBe( - 'service', - ); - expect(metaFactory().service).toBe(ref); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - // @ts-expect-error - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory(null); - // @ts-expect-error - metaFactory(undefined); - metaFactory(); + expect(factory.$$type).toBe('@backstage/BackendFeature'); + expect((factory as InternalServiceFactory).featureType).toBe('service'); + expect(factory.service).toBe(ref); }); - it('should create a sync root factory', () => { - const metaFactory = createServiceFactory({ + it('should create a root scoped factory', () => { + const factory = createServiceFactory({ service: rootDep, deps: {}, factory(_deps) { return 0; }, }); - expect(metaFactory).toEqual(expect.any(Function)); - expect(metaFactory().service).toBe(rootDep); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - // @ts-expect-error - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory(null); - // @ts-expect-error - metaFactory(undefined); - metaFactory(); + expect(factory.$$type).toBe('@backstage/BackendFeature'); + expect((factory as InternalServiceFactory).featureType).toBe('service'); + expect(factory.service).toBe(rootDep); }); - it('should create a factory', () => { - const metaFactory = createServiceFactory({ + it('should create a plugin scoped factory with a root context', () => { + const factory = createServiceFactory({ service: ref, deps: {}, async createRootContext() {}, @@ -92,85 +62,13 @@ describe('createServiceFactory', () => { return 'x'; }, }); - expect(metaFactory).toEqual(expect.any(Function)); - expect(metaFactory().service).toBe(ref); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - // @ts-expect-error - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory(null); - // @ts-expect-error - metaFactory(undefined); - metaFactory(); - }); - - it('should create a factory with options', () => { - const metaFactory = createServiceFactory((_opts?: { x: number }) => ({ - service: ref, - deps: {}, - async createRootContext() {}, - async factory() { - return 'x'; - }, - })); - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory({ x: 1, y: 2 }); - // @ts-expect-error - metaFactory(null); - metaFactory(undefined); - metaFactory(); - }); - - it('should not be allowed to require options', () => { - // @ts-expect-error - const metaFactory = createServiceFactory((_opts: { x: number }) => ({ - service: ref, - deps: {}, - async createRootContext() {}, - async factory() { - return 'x'; - }, - })); - expect(metaFactory).toEqual(expect.any(Function)); - }); - - it('should create a factory with options as interface', () => { - const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ - service: ref, - deps: {}, - async createRootContext() {}, - async factory() { - return 'x'; - }, - })); - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory({ x: 1, y: 2 }); - // @ts-expect-error - metaFactory(null); - metaFactory(undefined); - metaFactory(); + expect(factory.$$type).toBe('@backstage/BackendFeature'); + expect((factory as InternalServiceFactory).featureType).toBe('service'); + expect(factory.service).toBe(ref); }); it('should create root scoped factory with dependencies', () => { - const metaFactory = createServiceFactory({ + const factory = createServiceFactory({ service: createServiceRef({ id: 'foo', scope: 'root' }), deps: { root: rootDep, @@ -183,48 +81,11 @@ describe('createServiceFactory', () => { return 0; }, }); - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory({}); - // @ts-expect-error - metaFactory(null); - // @ts-expect-error - metaFactory(undefined); - metaFactory(); + expect(factory.$$type).toBe('@backstage/BackendFeature'); }); - it('should create root scoped factory with dependencies and options', () => { - const metaFactory = createServiceFactory((_options?: TestOptions) => ({ - service: createServiceRef({ id: 'foo', scope: 'root' }), - deps: { - root: rootDep, - }, - async factory({ root }) { - const root1: number = root; - // @ts-expect-error - const root2: string = root; - unused(root1, root2); - return 0; - }, - })); - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory({ x: 1, y: 2 }); - // @ts-expect-error - metaFactory(null); - metaFactory(undefined); - metaFactory(); - }); - - it('should create factory with dependencies', () => { - const metaFactory = createServiceFactory({ + it('should create a plugin scoped factory with dependencies', () => { + const factory = createServiceFactory({ service: createServiceRef({ id: 'derp' }), deps: { root: rootDep, @@ -251,19 +112,11 @@ describe('createServiceFactory', () => { return 'x'; }, }); - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory({}); - // @ts-expect-error - metaFactory(null); - // @ts-expect-error - metaFactory(undefined); - metaFactory(); + expect(factory.$$type).toBe('@backstage/BackendFeature'); }); it('should create factory with dependencies with optional derpFactory', () => { - const metaFactory = createServiceFactory({ + const factory = createServiceFactory({ service: createServiceRef({ id: 'derp' }), deps: { root: rootDep, @@ -280,61 +133,12 @@ describe('createServiceFactory', () => { return 'x'; }, }); - - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory({}); - // @ts-expect-error - metaFactory(null); - // @ts-expect-error - metaFactory(undefined); - metaFactory(); - }); - - it('should create factory with options and dependencies', () => { - const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ - service: ref, - deps: { - root: rootDep, - plugin: pluginDep, - }, - async createRootContext({ root }) { - const root1: number = root; - // @ts-expect-error - const root2: string = root; - unused(root1, root2); - return { root }; - }, - async factory({ plugin }, { root }) { - const root1: number = root; - // @ts-expect-error - const root2: string = root; - const plugin3: boolean = plugin; - // @ts-expect-error - const plugin4: number = plugin; - unused(root1, root2, plugin3, plugin4); - return 'x'; - }, - })); - expect(metaFactory).toEqual(expect.any(Function)); - - // @ts-expect-error - metaFactory('string'); - // @ts-expect-error - metaFactory({}); - metaFactory({ x: 1 }); - // @ts-expect-error - metaFactory({ x: 1, y: 2 }); - // @ts-expect-error - metaFactory(null); - metaFactory(undefined); - metaFactory(); + expect(factory.$$type).toBe('@backstage/BackendFeature'); }); it('should support old service refs without a multiton field', () => { const oldPluginDep = pluginDep as Omit; // Old refs don't have a multiton field - const metaFactory = createServiceFactory({ + const factory = createServiceFactory({ service: ref, deps: { plugin: oldPluginDep, @@ -347,82 +151,6 @@ describe('createServiceFactory', () => { return 'x'; }, }); - expect(metaFactory).toEqual(expect.any(Function)); - }); - - it('should only allow objects as options', () => { - // @ts-expect-error - const metaFactory = createServiceFactory((_opts: string) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - expect(metaFactory).toEqual(expect.any(Function)); - // @ts-expect-error - createServiceFactory((_opts: number) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: symbol) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: bigint) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: 'string') => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: Array) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: Map) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: Set) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); - // @ts-expect-error - createServiceFactory((_opts: null) => ({ - service: ref, - deps: {}, - async factory() { - return async () => 'x'; - }, - })); + expect(factory.$$type).toBe('@backstage/BackendFeature'); }); }); diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 5bca592768..a14e388bd0 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -59,24 +59,6 @@ export interface ServiceFactory< service: ServiceRef; } -/** - * @public - * @deprecated This type exists only as a helper for old code that relied on `createServiceFactory` to return `() => ServiceFactory` instead of `ServiceFactory`. You should remove the `()` parentheses at the end of your usages. This type will be removed in a future release. - */ -export interface ServiceFactoryCompat< - TService = unknown, - TScope extends 'plugin' | 'root' = 'plugin' | 'root', - TInstances extends 'singleton' | 'multiton' = 'singleton' | 'multiton', - TOpts extends object | undefined = undefined, -> extends ServiceFactory { - /** - * @deprecated Callable service factories will be removed in a future release, please re-implement the service factory using the available APIs instead. If no options are being passed, you can simply remove the trailing `()`. - */ - ( - ...options: undefined extends TOpts ? [] : [options?: TOpts] - ): ServiceFactory; -} - /** @internal */ export interface InternalServiceFactory< TService = unknown, @@ -94,14 +76,6 @@ export interface InternalServiceFactory< ): Promise; } -/** - * Represents either a {@link ServiceFactory} or a function that returns one. - * - * @deprecated The support for service factory functions is deprecated and will be removed. - * @public - */ -export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory); - /** @public */ export interface ServiceRefOptions< TService, @@ -114,12 +88,6 @@ export interface ServiceRefOptions< defaultFactory?( service: ServiceRef, ): Promise; - /** - * @deprecated The defaultFactory must return a plain `ServiceFactory` object, support for returning a function will be removed. - */ - defaultFactory?( - service: ServiceRef, - ): Promise<() => ServiceFactory>; } /** @@ -179,7 +147,7 @@ export function createServiceRef< } as ServiceRef & { __defaultFactory?: ( service: ServiceRef, - ) => Promise | (() => ServiceFactory)>; + ) => Promise>; }; } @@ -197,7 +165,7 @@ type ServiceRefsToInstances< /** @public */ export interface RootServiceFactoryOptions< - TService, // TODO(Rugvip): Can we forward the entire service ref type here instead of forwarding each type arg once the callback form is gone? + TService, TInstances extends 'singleton' | 'multiton', TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, @@ -259,31 +227,9 @@ export function createServiceFactory< TInstances extends 'singleton' | 'multiton', TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, - TOpts extends object | undefined = undefined, >( options: RootServiceFactoryOptions, -): ServiceFactoryCompat; -/** - * Creates a root scoped service factory with optional options. - * - * @deprecated The ability to define options for service factories is deprecated - * and will be removed. Please use the non-callback form of createServiceFactory - * and provide an API that allows for a simple re-implementation of the service - * factory instead. - * @public - * @param options - The service factory configuration. - */ -export function createServiceFactory< - TService, - TInstances extends 'singleton' | 'multiton', - TImpl extends TService, - TDeps extends { [name in string]: ServiceRef }, - TOpts extends object | undefined = undefined, ->( - options: ( - options?: TOpts, - ) => RootServiceFactoryOptions, -): ServiceFactoryCompat; +): ServiceFactory; /** * Creates a plugin scoped service factory without options. * @@ -296,7 +242,6 @@ export function createServiceFactory< TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, TContext = undefined, - TOpts extends object | undefined = undefined, >( options: PluginServiceFactoryOptions< TService, @@ -305,100 +250,22 @@ export function createServiceFactory< TImpl, TDeps >, -): ServiceFactoryCompat; -/** - * Creates a plugin scoped service factory with optional options. - * - * @deprecated The ability to define options for service factories is deprecated - * and will be removed. Please use the non-callback form of createServiceFactory - * and provide an API that allows for a simple re-implementation of the service - * factory instead. - * @public - * @param options - The service factory configuration. - */ -export function createServiceFactory< - TService, - TInstances extends 'singleton' | 'multiton', - TImpl extends TService, - TDeps extends { [name in string]: ServiceRef }, - TContext = undefined, - TOpts extends object | undefined = undefined, ->( - options: ( - options?: TOpts, - ) => PluginServiceFactoryOptions< - TService, - TInstances, - TContext, - TImpl, - TDeps - >, -): ServiceFactoryCompat; +): ServiceFactory; export function createServiceFactory< TService, TInstances extends 'singleton' | 'multiton', TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, TContext, - TOpts extends object | undefined = undefined, >( options: | RootServiceFactoryOptions - | PluginServiceFactoryOptions - | (( - options: TOpts, - ) => RootServiceFactoryOptions) - | (( - options: TOpts, - ) => PluginServiceFactoryOptions< - TService, - TInstances, - TContext, - TImpl, - TDeps - >) - | (() => RootServiceFactoryOptions) - | (() => PluginServiceFactoryOptions< - TService, - TInstances, - TContext, - TImpl, - TDeps - >), -): ServiceFactoryCompat< - TService, - 'root' | 'plugin', - 'singleton' | 'multiton', - TOpts -> { - const configCallback = - typeof options === 'function' ? options : () => options; - const factory = ( - o?: TOpts, - ): InternalServiceFactory => { - const anyConf = configCallback(o!); - if (anyConf.service.scope === 'root') { - const c = anyConf as RootServiceFactoryOptions< - TService, - TInstances, - TImpl, - TDeps - >; - return { - $$type: '@backstage/BackendFeature', - version: 'v1', - featureType: 'service', - service: c.service, - initialization: c.initialization, - deps: c.deps, - factory: async (deps: ServiceRefsToInstances) => - c.factory(deps), - }; - } - const c = anyConf as PluginServiceFactoryOptions< + | PluginServiceFactoryOptions, +): ServiceFactory { + if (options.service.scope === 'root') { + const c = options as RootServiceFactoryOptions< TService, TInstances, - TContext, TImpl, TDeps >; @@ -408,23 +275,33 @@ export function createServiceFactory< featureType: 'service', service: c.service, initialization: c.initialization, - ...('createRootContext' in c - ? { - createRootContext: async ( - deps: ServiceRefsToInstances, - ) => c?.createRootContext?.(deps), - } - : {}), - deps: c.deps, - factory: async (deps: ServiceRefsToInstances, ctx: TContext) => - c.factory(deps, ctx), - }; - }; - - // This constructs the `ServiceFactoryCompat` type, which is both a plain - // factory object as well as a function that can be called to construct a - // factory, potentially with options. In the future only the plain factory - // form will be supported, but for now we need to allow callers to call the - // factory too. - return Object.assign(factory, factory(undefined as TOpts)); + deps: options.deps, + factory: async (deps: ServiceRefsToInstances) => + c.factory(deps), + } as InternalServiceFactory; + } + const c = options as PluginServiceFactoryOptions< + TService, + TInstances, + TContext, + TImpl, + TDeps + >; + return { + $$type: '@backstage/BackendFeature', + version: 'v1', + featureType: 'service', + service: c.service, + initialization: c.initialization, + ...('createRootContext' in options + ? { + createRootContext: async ( + deps: ServiceRefsToInstances, + ) => c?.createRootContext?.(deps), + } + : {}), + deps: options.deps, + factory: async (deps: ServiceRefsToInstances, ctx: TContext) => + c.factory(deps, ctx), + } as InternalServiceFactory; } diff --git a/packages/backend-plugin-api/src/types.ts b/packages/backend-plugin-api/src/types.ts index 31684b6a38..59f1106fab 100644 --- a/packages/backend-plugin-api/src/types.ts +++ b/packages/backend-plugin-api/src/types.ts @@ -27,14 +27,3 @@ export interface BackendFeature { // NOTE: This type is opaque in order to simplify future API evolution. $$type: '@backstage/BackendFeature'; } - -/** - * @public - * @deprecated This type exists only as a helper for old code that relied on `createBackendFeature` and `createBackendPlugin` to return `() => BackendFeature` instead of `BackendFeature`. You should remove the `()` parentheses at the end of your usages. This type will be removed in a future release. - */ -export interface BackendFeatureCompat extends BackendFeature { - /** - * @deprecated You do not need to use this call signature; use the type directly instead by removing the `()` parentheses at the end. This call signature will be removed in a future release. - */ - (): this; -} diff --git a/packages/backend-plugin-api/src/wiring/createBackendFeatureLoader.test.ts b/packages/backend-plugin-api/src/wiring/createBackendFeatureLoader.test.ts index 37fc079551..cf7ab18ace 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendFeatureLoader.test.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendFeatureLoader.test.ts @@ -46,18 +46,18 @@ describe('createBackendFeatureLoader', () => { createBackendPlugin({ pluginId: 'x', register() {}, - })(), + }), createServiceFactory({ service: coreServices.pluginMetadata, deps: {}, factory: () => ({ getId: () => 'fake-id' }), - })(), + }), // Dynamic import format Promise.resolve({ default: createBackendPlugin({ pluginId: 'y', register() {}, - })(), + }), }), ]; }, @@ -79,7 +79,7 @@ describe('createBackendFeatureLoader', () => { }); it('should support multiple output formats', async () => { - const feature = createBackendPlugin({ pluginId: 'x', register() {} })(); + const feature = createBackendPlugin({ pluginId: 'x', register() {} }); const dynamicFeature = Promise.resolve({ default: feature }); async function extractResult(f: BackendFeature) { diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts index 3426d5a90c..4d927c86f6 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts @@ -29,13 +29,6 @@ describe('createBackendModule', () => { }, }); - // legacy form - const legacy = result() as unknown as InternalBackendRegistrations; - expect(legacy.$$type).toEqual('@backstage/BackendFeature'); - expect(legacy.version).toEqual('v1'); - expect(legacy.getRegistrations).toEqual(expect.any(Function)); - - // new form const module = result as unknown as InternalBackendRegistrations; expect(module.$$type).toEqual('@backstage/BackendFeature'); expect(module.version).toEqual('v1'); @@ -52,9 +45,6 @@ describe('createBackendModule', () => { }, }, ]); - - // @ts-expect-error - expect(module({ a: 'a' })).toBeDefined(); }); it('should be able to depend on all types of dependencies', () => { diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.ts index 56f041606e..e2357a65d0 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.ts @@ -14,11 +14,12 @@ * limitations under the License. */ -import { BackendFeatureCompat } from '../types'; +import { BackendFeature } from '../types'; import { BackendModuleRegistrationPoints, InternalBackendModuleRegistration, InternalBackendPluginRegistration, + InternalBackendRegistrations, } from './types'; /** @@ -52,7 +53,7 @@ export interface CreateBackendModuleOptions { */ export function createBackendModule( options: CreateBackendModuleOptions, -): BackendFeatureCompat { +): BackendFeature { function getRegistrations() { const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; @@ -93,15 +94,10 @@ export function createBackendModule( ]; } - function backendFeatureCompatWrapper() { - return backendFeatureCompatWrapper; - } - - Object.assign(backendFeatureCompatWrapper, { + return { $$type: '@backstage/BackendFeature' as const, + featureType: 'registrations', version: 'v1', getRegistrations, - }); - - return backendFeatureCompatWrapper as BackendFeatureCompat; + } as InternalBackendRegistrations; } diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts index f4a0877b85..5acd8e46d7 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts @@ -28,13 +28,6 @@ describe('createBackendPlugin', () => { }, }); - // legacy form - const legacy = result() as unknown as InternalBackendRegistrations; - expect(legacy.$$type).toEqual('@backstage/BackendFeature'); - expect(legacy.version).toEqual('v1'); - expect(legacy.getRegistrations).toEqual(expect.any(Function)); - - // new form const plugin = result as unknown as InternalBackendRegistrations; expect(plugin.$$type).toEqual('@backstage/BackendFeature'); expect(plugin.version).toEqual('v1'); @@ -50,9 +43,6 @@ describe('createBackendPlugin', () => { }, }, ]); - - // @ts-expect-error - expect(plugin({ a: 'a' })).toBeDefined(); }); it('should be able to depend on all compatible dependencies', () => { diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts index f5984ef24b..185500c495 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts @@ -14,10 +14,11 @@ * limitations under the License. */ -import { BackendFeatureCompat } from '../types'; +import { BackendFeature } from '../types'; import { BackendPluginRegistrationPoints, InternalBackendPluginRegistration, + InternalBackendRegistrations, } from './types'; /** @@ -46,7 +47,7 @@ export interface CreateBackendPluginOptions { */ export function createBackendPlugin( options: CreateBackendPluginOptions, -): BackendFeatureCompat { +): BackendFeature { function getRegistrations() { const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; @@ -86,15 +87,10 @@ export function createBackendPlugin( ]; } - function backendFeatureCompatWrapper() { - return backendFeatureCompatWrapper; - } - - Object.assign(backendFeatureCompatWrapper, { + return { $$type: '@backstage/BackendFeature' as const, version: 'v1', + featureType: 'registrations', getRegistrations, - }); - - return backendFeatureCompatWrapper as BackendFeatureCompat; + } as InternalBackendRegistrations; } diff --git a/packages/backend-plugin-api/src/wiring/index.ts b/packages/backend-plugin-api/src/wiring/index.ts index 95eea6c3d4..73b099978e 100644 --- a/packages/backend-plugin-api/src/wiring/index.ts +++ b/packages/backend-plugin-api/src/wiring/index.ts @@ -37,21 +37,3 @@ export type { CreateBackendModuleOptions, CreateExtensionPointOptions, }; - -/** - * @public - * @deprecated Use {@link CreateBackendPluginOptions} instead. - */ -export type BackendPluginConfig = CreateBackendPluginOptions; - -/** - * @public - * @deprecated Use {@link CreateBackendModuleOptions} instead. - */ -export type BackendModuleConfig = CreateBackendModuleOptions; - -/** - * @public - * @deprecated Use {@link CreateExtensionPointOptions} instead. - */ -export type ExtensionPointConfig = CreateExtensionPointOptions; diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md deleted file mode 100644 index fbdbff1a8a..0000000000 --- a/packages/backend-tasks/CHANGELOG.md +++ /dev/null @@ -1,1438 +0,0 @@ -# @backstage/backend-tasks - -## 0.6.0 - -### Minor Changes - -- fc24d9e: This package is deprecated and will be removed in a near future, follow the instructions below to stop using it: - - - `TaskScheduler`: Please migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from `@backstage/backend-defaults; - - `TaskRunner`: Please import `SchedulerServiceTaskRunner` from `@backstage/backend-plugin-api` instead; - - `TaskFunction`: Please import `SchedulerServiceTaskFunction` from `@backstage/backend-plugin-api` instead; - - `TaskDescriptor`: Please import `SchedulerServiceTaskDescriptor` from `@backstage/backend-plugin-api` instead; - - `TaskInvocationDefinition`: Please import `SchedulerServiceTaskInvocationDefinition` from `@backstage/backend-plugin-api` instead; - - `TaskScheduleDefinition`: Please import `SchedulerServiceTaskFunction` from `@backstage/backend-plugin-api` instead; - - `TaskScheduleDefinitionConfig`: Please import `SchedulerServiceTaskScheduleDefinitionConfig` from `@backstage/backend-plugin-api` instead; - - `PluginTaskScheduler`: Please use `SchedulerService` from `@backstage/backend-plugin-api` instead (most likely via `coreServices.scheduler`); - - `readTaskScheduleDefinitionFromConfig`: Please import `readSchedulerServiceTaskScheduleDefinitionFromConfig` from `@backstage/backend-plugin-api` instead; - - `HumanDuration`: Import `TypesHumanDuration` from `@backstage/types` instead. - -### Patch Changes - -- ba9abf4: The `PluginTaskScheduler` now allows tasks with `frequency: { trigger: 'manual' }`. This means that the task will not be scheduled, but rather run only when manually triggered with `PluginTaskScheduler.triggerTask`. -- Updated dependencies - - @backstage/backend-plugin-api@0.8.0 - - @backstage/backend-common@0.24.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.28-next.3 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.8.0-next.3 - - @backstage/backend-common@0.23.4-next.3 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.28-next.2 - -### Patch Changes - -- ba9abf4: The `PluginTaskScheduler` now allows tasks with `frequency: { trigger: 'manual' }`. This means that the task will not be scheduled, but rather run only when manually triggered with `PluginTaskScheduler.triggerTask`. -- Updated dependencies - - @backstage/backend-plugin-api@0.8.0-next.2 - - @backstage/backend-common@0.23.4-next.2 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.28-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.7.1-next.1 - - @backstage/backend-common@0.23.4-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.28-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.23.4-next.0 - - @backstage/backend-plugin-api@0.7.1-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.27 - -### Patch Changes - -- 083eaf9: Fix bug where ISO durations could no longer be used for schedules -- Updated dependencies - - @backstage/backend-plugin-api@0.7.0 - - @backstage/backend-common@0.23.3 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.27-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.23.3-next.1 - - @backstage/backend-plugin-api@0.6.22-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.26-next.0 - -### Patch Changes - -- 083eaf9: Fix bug where ISO durations could no longer be used for schedules -- Updated dependencies - - @backstage/backend-plugin-api@0.6.21-next.0 - - @backstage/backend-common@0.23.2-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.24 - -### Patch Changes - -- 736bc3c: Marked all exports as deprecated and pointed at `@backstage/backend-plugin-api` and `@backstage/backend-defaults` -- ed473cd: Updated the `TaskScheduleDefinitionConfig` deprecated comment to point to `SchedulerServiceTaskScheduleDefinitionConfig` -- 6a576dc: Deprecate the legacy `TaskScheduler.fromConfig` method and stop using the `getVoidlogger` in tests files to reduce the dependency on the soon-to-deprecate `backstage-common` package. -- 1897169: More detailed deprecation messages -- Updated dependencies - - @backstage/backend-common@0.23.0 - - @backstage/backend-plugin-api@0.6.19 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.24-next.3 - -### Patch Changes - -- 1897169: More detailed deprecation messages -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19-next.3 - - @backstage/backend-common@0.23.0-next.3 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.24-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19-next.2 - - @backstage/backend-common@0.23.0-next.2 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.24-next.1 - -### Patch Changes - -- ed473cd: Updated the `TaskScheduleDefinitionConfig` deprecated comment to point to `SchedulerServiceTaskScheduleDefinitionConfig` -- Updated dependencies - - @backstage/backend-plugin-api@0.6.19-next.1 - - @backstage/backend-common@0.23.0-next.1 - -## 0.5.24-next.0 - -### Patch Changes - -- 736bc3c: Marked all exports as deprecated and pointed at `@backstage/backend-plugin-api` and `@backstage/backend-defaults` -- 6a576dc: Deprecate the legacy `TaskScheduler.fromConfig` method and stop using the `getVoidlogger` in tests files to reduce the dependency on the soon-to-deprecate `backstage-common` package. -- Updated dependencies - - @backstage/backend-common@0.22.1-next.0 - - @backstage/backend-plugin-api@0.6.19-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.23 - -### Patch Changes - -- d229dc4: Move path utilities from `backend-common` to the `backend-plugin-api` package. -- Updated dependencies - - @backstage/backend-common@0.22.0 - - @backstage/backend-plugin-api@0.6.18 - -## 0.5.23-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.22.0-next.1 - - @backstage/backend-plugin-api@0.6.18-next.1 - -## 0.5.23-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.8-next.0 - - @backstage/backend-plugin-api@0.6.18-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.22 - -### Patch Changes - -- d5a1fe1: Replaced winston logger with `LoggerService` -- Updated dependencies - - @backstage/backend-common@0.21.7 - - @backstage/backend-plugin-api@0.6.17 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.22-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.7-next.1 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.22-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.7-next.0 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.21 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.6 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.20 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.5 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.19 - -### Patch Changes - -- 0fb419b: Updated dependency `uuid` to `^9.0.0`. - Updated dependency `@types/uuid` to `^9.0.0`. -- Updated dependencies - - @backstage/backend-common@0.21.4 - - @backstage/config@1.2.0 - - @backstage/errors@1.2.4 - - @backstage/types@1.1.1 - -## 0.5.19-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.4-next.2 - - @backstage/config@1.2.0-next.1 - - @backstage/errors@1.2.4-next.0 - - @backstage/types@1.1.1 - -## 0.5.19-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.2.0-next.1 - - @backstage/backend-common@0.21.4-next.1 - - @backstage/errors@1.2.4-next.0 - - @backstage/types@1.1.1 - -## 0.5.18-next.0 - -### Patch Changes - -- 0fb419b: Updated dependency `uuid` to `^9.0.0`. - Updated dependency `@types/uuid` to `^9.0.0`. -- Updated dependencies - - @backstage/backend-common@0.21.3-next.0 - - @backstage/errors@1.2.4-next.0 - - @backstage/config@1.1.2-next.0 - - @backstage/types@1.1.1 - -## 0.5.15 - -### Patch Changes - -- 9aac2b0: Use `--cwd` as the first `yarn` argument -- 6707216: The `TaskScheduler.fromConfig` method now accepts the `LegacyRootDatabaseService` interface rather than the full `DatabaseManager` implementation. -- b68248b: Updated dependency `cron` to `^3.0.0`. -- Updated dependencies - - @backstage/backend-common@0.21.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.15-next.3 - -### Patch Changes - -- 6707216: The `TaskScheduler.fromConfig` method now accepts the `LegacyRootDatabaseService` interface rather than the full `DatabaseManager` implementation. -- b68248b: Updated dependency `cron` to `^3.0.0`. -- Updated dependencies - - @backstage/backend-common@0.21.0-next.3 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.15-next.2 - -### Patch Changes - -- 9aac2b0: Use `--cwd` as the first `yarn` argument -- Updated dependencies - - @backstage/backend-common@0.21.0-next.2 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.15-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.0-next.1 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.15-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.21.0-next.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.14 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.14-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1-next.2 - -## 0.5.14-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1-next.1 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.14-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.1-next.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.13 - -### Patch Changes - -- d8f488a: Allow tasks to run more often that the default work check interval, which is 5 seconds. -- 0cbb03b: Fixing regular expression ReDoS with zod packages. Upgrading to latest. ref: https://security.snyk.io/vuln/SNYK-JS-ZOD-5925617 -- Updated dependencies - - @backstage/backend-common@0.20.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.13-next.3 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.0-next.3 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.13-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.0-next.2 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.13-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.20.0-next.1 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.13-next.0 - -### Patch Changes - -- d8f488aaa8: Allow tasks to run more often that the default work check interval, which is 5 seconds. -- Updated dependencies - - @backstage/backend-common@0.20.0-next.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.12 - -### Patch Changes - -- 013611b42e: `knex` has been bumped to major version 3 and `better-sqlite3` to major version 9, which deprecate node 16 support. -- Updated dependencies - - @backstage/backend-common@0.19.9 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.12-next.2 - -### Patch Changes - -- [#20570](https://github.com/backstage/backstage/pull/20570) [`013611b42e`](https://github.com/backstage/backstage/commit/013611b42ed457fefa9bb85fddf416cf5e0c1f76) Thanks [@freben](https://github.com/freben)! - `knex` has been bumped to major version 3 and `better-sqlite3` to major version 9, which deprecate node 16 support. - -- Updated dependencies - - @backstage/backend-common@0.19.9-next.2 - -## 0.5.12-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.9-next.1 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.12-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.9-next.0 - - @backstage/config@1.1.1 - - @backstage/errors@1.2.3 - - @backstage/types@1.1.1 - -## 0.5.11 - -### Patch Changes - -- 5db102bfdf: Instrument `backend-tasks` with some counters and histograms for duration. - - `backend_tasks.task.runs.count`: Counter with the total number of times a task has been run. - `backend_tasks.task.runs.duration`: Histogram with the run durations for each task. - - Both these metrics have come with `result` `taskId` and `scope` labels for finer grained grouping. - -- ddd76ac98d: Fix bug where backend tasks that are defined with HumanDuration are immediately triggered on application startup -- Updated dependencies - - @backstage/backend-common@0.19.8 - - @backstage/errors@1.2.3 - - @backstage/config@1.1.1 - - @backstage/types@1.1.1 - -## 0.5.11-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.8-next.2 - - @backstage/errors@1.2.3-next.0 - - @backstage/config@1.1.1-next.0 - - @backstage/types@1.1.1 - -## 0.5.10-next.1 - -### Patch Changes - -- 5db102bfdf: Instrument `backend-tasks` with some counters and histograms for duration. - - `backend_tasks.task.runs.count`: Counter with the total number of times a task has been run. - `backend_tasks.task.runs.duration`: Histogram with the run durations for each task. - - Both these metrics have come with `result` `taskId` and `scope` labels for finer grained grouping. - -- ddd76ac98d: Fix bug where backend tasks that are defined with HumanDuration are immediately triggered on application startup -- Updated dependencies - - @backstage/backend-common@0.19.7-next.1 - - @backstage/config@1.1.0 - - @backstage/errors@1.2.2 - - @backstage/types@1.1.1 - -## 0.5.10-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.7-next.0 - - @backstage/config@1.1.0 - - @backstage/errors@1.2.2 - - @backstage/types@1.1.1 - -## 0.5.8 - -### Patch Changes - -- 8fd91547cd0b: When starting a task that existed before, with a faster schedule than it - previously had, the task will now correctly obey the faster schedule - immediately. Before this fix, the new schedule was only obeyed after the next - pending (according to the old schedule) run had completed. -- 62f448edb0b5: Use `readDurationFromConfig` from the config package -- 74604806aae8: Avoid starting task janitor in tests. -- cfc3ca6ce060: Changes needed to support MySQL -- 814feeed7343: Update to handle invalid luxon values -- Updated dependencies - - @backstage/backend-common@0.19.5 - - @backstage/config@1.1.0 - - @backstage/errors@1.2.2 - - @backstage/types@1.1.1 - -## 0.5.8-next.3 - -### Patch Changes - -- Updated dependencies - - @backstage/config@1.1.0-next.2 - - @backstage/errors@1.2.2-next.0 - - @backstage/types@1.1.1-next.0 - - @backstage/backend-common@0.19.5-next.3 - -## 0.5.8-next.2 - -### Patch Changes - -- 814feeed7343: Update to handle invalid luxon values -- Updated dependencies - - @backstage/config@1.1.0-next.1 - - @backstage/backend-common@0.19.5-next.2 - - @backstage/errors@1.2.1 - - @backstage/types@1.1.0 - -## 0.5.8-next.1 - -### Patch Changes - -- 62f448edb0b5: Use `readDurationFromConfig` from the config package -- Updated dependencies - - @backstage/config@1.1.0-next.0 - - @backstage/backend-common@0.19.5-next.1 - - @backstage/errors@1.2.1 - - @backstage/types@1.1.0 - -## 0.5.7-next.0 - -### Patch Changes - -- cfc3ca6ce060: Changes needed to support MySQL -- Updated dependencies - - @backstage/backend-common@0.19.4-next.0 - - @backstage/config@1.0.8 - - @backstage/errors@1.2.1 - - @backstage/types@1.1.0 - -## 0.5.5 - -### Patch Changes - -- dfd1b6b2fc33: Make `readTaskScheduleDefinitionFromConfig` properly handle bad inputs -- Updated dependencies - - @backstage/backend-common@0.19.2 - - @backstage/config@1.0.8 - - @backstage/errors@1.2.1 - - @backstage/types@1.1.0 - -## 0.5.5-next.2 - -### Patch Changes - -- dfd1b6b2fc33: Make `readTaskScheduleDefinitionFromConfig` properly handle bad inputs -- Updated dependencies - - @backstage/backend-common@0.19.2-next.2 - -## 0.5.5-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.2-next.1 - - @backstage/config@1.0.8 - - @backstage/errors@1.2.1 - - @backstage/types@1.1.0 - -## 0.5.5-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.2-next.0 - - @backstage/config@1.0.8 - - @backstage/errors@1.2.1 - - @backstage/types@1.1.0 - -## 0.5.4 - -### Patch Changes - -- Updated dependencies - - @backstage/errors@1.2.1 - - @backstage/backend-common@0.19.1 - - @backstage/config@1.0.8 - - @backstage/types@1.1.0 - -## 0.5.4-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/errors@1.2.1-next.0 - - @backstage/backend-common@0.19.1-next.0 - - @backstage/config@1.0.8 - - @backstage/types@1.1.0 - -## 0.5.3 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.0 - - @backstage/types@1.1.0 - - @backstage/errors@1.2.0 - - @backstage/config@1.0.8 - -## 0.5.3-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.0-next.2 - - @backstage/config@1.0.7 - - @backstage/errors@1.2.0-next.0 - - @backstage/types@1.0.2 - -## 0.5.3-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.19.0-next.1 - - @backstage/errors@1.2.0-next.0 - - @backstage/config@1.0.7 - - @backstage/types@1.0.2 - -## 0.5.3-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.6-next.0 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.5 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.2-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.5-next.1 - - @backstage/config@1.0.7 - -## 0.5.2-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.5-next.0 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.1 - -### Patch Changes - -- 1e4f5e91b8e: Bump `zod` and `zod-to-json-schema` dependencies. -- Updated dependencies - - @backstage/backend-common@0.18.4 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.1-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.4-next.2 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.1-next.1 - -### Patch Changes - -- 1e4f5e91b8e: Bump `zod` and `zod-to-json-schema` dependencies. -- Updated dependencies - - @backstage/backend-common@0.18.4-next.1 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.1-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.4-next.0 - - @backstage/config@1.0.7 - - @backstage/errors@1.1.5 - - @backstage/types@1.0.2 - -## 0.5.0 - -### Minor Changes - -- 1578276708a: add functionality to get descriptions from the scheduler for triggering - -### Patch Changes - -- f0685193efa: Added the adapted query to mysql and sqlite3 databases to not returning warning on logs -- 482dae5de1c: Updated link to docs. -- Updated dependencies - - @backstage/backend-common@0.18.3 - - @backstage/errors@1.1.5 - - @backstage/config@1.0.7 - - @backstage/types@1.0.2 - -## 0.5.0-next.2 - -### Minor Changes - -- 1578276708a: add functionality to get descriptions from the scheduler for triggering - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.3-next.2 - - @backstage/config@1.0.7-next.0 - -## 0.4.4-next.1 - -### Patch Changes - -- 482dae5de1c: Updated link to docs. -- Updated dependencies - - @backstage/errors@1.1.5-next.0 - - @backstage/backend-common@0.18.3-next.1 - - @backstage/config@1.0.7-next.0 - - @backstage/types@1.0.2 - -## 0.4.4-next.0 - -### Patch Changes - -- f0685193ef: Added the adapted query to mysql and sqlite3 databases to not returning warning on logs -- Updated dependencies - - @backstage/backend-common@0.18.3-next.0 - - @backstage/config@1.0.6 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.3 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.2 - - @backstage/config@1.0.6 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.3-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.2-next.2 - - @backstage/config@1.0.6 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.3-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.2-next.1 - - @backstage/config@1.0.6 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.3-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.2-next.0 - -## 0.4.1 - -### Patch Changes - -- 3fad4ed40a: Added a new static `TaskScheduler.forPlugin` method. -- b99c030f1b: Minor internal refactor to avoid import cycle issue. -- Updated dependencies - - @backstage/backend-common@0.18.0 - - @backstage/config@1.0.6 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.1-next.1 - -### Patch Changes - -- b99c030f1b: Minor internal refactor to avoid import cycle issue. -- Updated dependencies - - @backstage/backend-common@0.18.0-next.1 - - @backstage/config@1.0.6-next.0 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.1-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.18.0-next.0 - - @backstage/config@1.0.6-next.0 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - -## 0.4.0 - -### Minor Changes - -- de8a975911: Changed to use native `AbortController` and `AbortSignal` from Node.js, instead - of the one from `node-abort-controller`. This is possible now that the minimum - supported Node.js version of the project is 16. - - Note that their interfaces are very slightly different, but typically not in a - way that matters to consumers. If you see any typescript errors as a direct - result from this, they are compatible with each other in the ways that we - interact with them, and should be possible to type-cast across without ill - effects. - -### Patch Changes - -- b05dcd5530: Move the `zod` dependency to a version that does not collide with other libraries -- Updated dependencies - - @backstage/backend-common@0.17.0 - - @backstage/errors@1.1.4 - - @backstage/types@1.0.2 - - @backstage/config@1.0.5 - -## 0.4.0-next.3 - -### Patch Changes - -- b05dcd5530: Move the `zod` dependency to a version that does not collide with other libraries -- Updated dependencies - - @backstage/backend-common@0.17.0-next.3 - - @backstage/config@1.0.5-next.1 - - @backstage/errors@1.1.4-next.1 - - @backstage/types@1.0.2-next.1 - -## 0.4.0-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.17.0-next.2 - - @backstage/config@1.0.5-next.1 - - @backstage/errors@1.1.4-next.1 - - @backstage/types@1.0.2-next.1 - -## 0.4.0-next.1 - -### Minor Changes - -- de8a975911: Changed to use native `AbortController` and `AbortSignal` from Node.js, instead - of the one from `node-abort-controller`. This is possible now that the minimum - supported Node.js version of the project is 16. - - Note that their interfaces are very slightly different, but typically not in a - way that matters to consumers. If you see any typescript errors as a direct - result from this, they are compatible with each other in the ways that we - interact with them, and should be possible to type-cast across without ill - effects. - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.17.0-next.1 - - @backstage/types@1.0.2-next.1 - - @backstage/config@1.0.5-next.1 - - @backstage/errors@1.1.4-next.1 - -## 0.3.8-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.16.1-next.0 - - @backstage/types@1.0.2-next.0 - - @backstage/config@1.0.5-next.0 - - @backstage/errors@1.1.4-next.0 - -## 0.3.7 - -### Patch Changes - -- 30e43717c7: Deprecated the `HumanDuration` type, which should now instead be imported from `@backstage/types`. -- Updated dependencies - - @backstage/backend-common@0.16.0 - - @backstage/types@1.0.1 - - @backstage/config@1.0.4 - - @backstage/errors@1.1.3 - -## 0.3.7-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.16.0-next.1 - - @backstage/config@1.0.4-next.0 - - @backstage/errors@1.1.3-next.0 - - @backstage/types@1.0.1-next.0 - -## 0.3.7-next.0 - -### Patch Changes - -- 30e43717c7: Deprecated the `HumanDuration` type, which should now instead be imported from `@backstage/types`. -- Updated dependencies - - @backstage/backend-common@0.16.0-next.0 - - @backstage/types@1.0.1-next.0 - - @backstage/config@1.0.4-next.0 - - @backstage/errors@1.1.3-next.0 - -## 0.3.6 - -### Patch Changes - -- d4fea86ea3: Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. -- Updated dependencies - - @backstage/backend-common@0.15.2 - - @backstage/config@1.0.3 - - @backstage/errors@1.1.2 - - @backstage/types@1.0.0 - -## 0.3.6-next.2 - -### Patch Changes - -- d4fea86ea3: Added new function `readTaskScheduleDefinitionFromConfig` to read `TaskScheduleDefinition` (aka. schedule) from the `Config`. -- Updated dependencies - - @backstage/backend-common@0.15.2-next.2 - - @backstage/config@1.0.3-next.2 - - @backstage/errors@1.1.2-next.2 - - @backstage/types@1.0.0 - -## 0.3.6-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.15.2-next.1 - - @backstage/config@1.0.3-next.1 - - @backstage/errors@1.1.2-next.1 - - @backstage/types@1.0.0 - -## 0.3.6-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.15.2-next.0 - - @backstage/config@1.0.3-next.0 - - @backstage/errors@1.1.2-next.0 - - @backstage/types@1.0.0 - -## 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 - -- Updated dependencies - - @backstage/config@1.0.2-next.0 - - @backstage/errors@1.1.1-next.0 - - @backstage/backend-common@0.15.1-next.3 - -## 0.3.5-next.0 - -### 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-next.0 - -## 0.3.4 - -### Patch Changes - -- 29f782eb37: Updated dependency `@types/luxon` to `^3.0.0`. -- Updated dependencies - - @backstage/backend-common@0.15.0 - -## 0.3.4-next.0 - -### Patch Changes - -- 29f782eb37: Updated dependency `@types/luxon` to `^3.0.0`. -- Updated dependencies - - @backstage/backend-common@0.15.0-next.0 - -## 0.3.3 - -### Patch Changes - -- 4e9a90e307: Updated dependency `luxon` to `^3.0.0`. -- 679b32172e: Updated dependency `knex` to `^2.0.0`. -- Updated dependencies - - @backstage/backend-common@0.14.1 - - @backstage/errors@1.1.0 - -## 0.3.3-next.3 - -### Patch Changes - -- 4e9a90e307: Updated dependency `luxon` to `^3.0.0`. -- Updated dependencies - - @backstage/backend-common@0.14.1-next.3 - -## 0.3.3-next.2 - -### Patch Changes - -- 679b32172e: Updated dependency `knex` to `^2.0.0`. -- Updated dependencies - - @backstage/backend-common@0.14.1-next.2 - -## 0.3.3-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.14.1-next.1 - - @backstage/errors@1.1.0-next.0 - -## 0.3.3-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.14.1-next.0 - -## 0.3.2 - -### Patch Changes - -- fde10d24f6: Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has -- f7146b516f: Updated dependency `cron` to `^2.0.0`. - Updated dependency `@types/cron` to `^2.0.0`. -- 7f108513b8: Add error logging when a background task throws an error rather than silently swallowing it. -- Updated dependencies - - @backstage/backend-common@0.14.0 - -## 0.3.2-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.14.0-next.2 - -## 0.3.2-next.1 - -### Patch Changes - -- f7146b516f: Updated dependency `cron` to `^2.0.0`. - Updated dependency `@types/cron` to `^2.0.0`. -- 7f108513b8: Add error logging when a background task throws an error rather than silently swallowing it. -- Updated dependencies - - @backstage/backend-common@0.13.6-next.1 - -## 0.3.2-next.0 - -### Patch Changes - -- fde10d24f6: Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has -- Updated dependencies - - @backstage/backend-common@0.13.6-next.0 - -## 0.3.1 - -### Patch Changes - -- 73480846dd: `TaskScheduleDefinition` has been updated to also accept an options object containing duration information in the form of days, hours, seconds and so on. This allows for scheduling without importing `luxon`. - - ```diff - -import { Duration } from 'luxon'; - // omitted other code - - const schedule = env.scheduler.createScheduledTaskRunner({ - - frequency: Duration.fromObject({ minutes: 10 }), - - timeout: Duration.fromObject({ minutes: 15 }), - + frequency: { minutes: 10 }, - + timeout: { minutes: 15 }, - // omitted other code - }); - ``` - -- cfd779a9bc: Scheduled tasks now have an optional `scope` field. If unset, or having the - value `'global'`, the old behavior with cross-worker locking is retained. If - having the value `'local'`, there is no coordination across workers and the - behavior is more like `setInterval`. This can be used to replace usages of - `runPeriodically` helpers. -- ebbec677e1: Correctly set next run time for tasks -- Updated dependencies - - @backstage/backend-common@0.13.3 - - @backstage/config@1.0.1 - -## 0.3.1-next.1 - -### Patch Changes - -- 73480846dd: `TaskScheduleDefinition` has been updated to also accept an options object containing duration information in the form of days, hours, seconds and so on. This allows for scheduling without importing `luxon`. - - ```diff - -import { Duration } from 'luxon'; - // omitted other code - - const schedule = env.scheduler.createScheduledTaskRunner({ - - frequency: Duration.fromObject({ minutes: 10 }), - - timeout: Duration.fromObject({ minutes: 15 }), - + frequency: { minutes: 10 }, - + timeout: { minutes: 15 }, - // omitted other code - }); - ``` - -- ebbec677e1: Correctly set next run time for tasks -- Updated dependencies - - @backstage/backend-common@0.13.3-next.2 - - @backstage/config@1.0.1-next.0 - -## 0.3.1-next.0 - -### Patch Changes - -- cfd779a9bc: Scheduled tasks now have an optional `scope` field. If unset, or having the - value `'global'`, the old behavior with cross-worker locking is retained. If - having the value `'local'`, there is no coordination across workers and the - behavior is more like `setInterval`. This can be used to replace usages of - `runPeriodically` helpers. -- Updated dependencies - - @backstage/backend-common@0.13.3-next.0 - -## 0.3.0 - -### Minor Changes - -- ab008a0988: Adds the ability to manually trigger tasks which are registered - -### Patch Changes - -- bdd2773202: Refactored the internal `TaskWorker` class to make it easier to test. -- a83babdd63: Fixed the `initialDelay` parameter of tasks to properly make task workers - _always_ wait before the first invocations on startup, not just the very first - time that the task is ever created. This behavior is more in line with - expectations. Callers to not need to update their code. - - Also clarified in the doc comment for the field that this wait applies only on - an individual worker level. That is, if you have a cluster of workers then each - individual machine may postpone its first task invocation by the given amount of - time to leave room for the service to settle, but _other_ workers may still - continue to invoke the task on the regular cadence in the meantime. - -- Updated dependencies - - @backstage/backend-common@0.13.2 - -## 0.3.0-next.2 - -### Patch Changes - -- a83babdd63: Fixed the `initialDelay` parameter of tasks to properly make task workers - _always_ wait before the first invocations on startup, not just the very first - time that the task is ever created. This behavior is more in line with - expectations. Callers to not need to update their code. - - Also clarified in the doc comment for the field that this wait applies only on - an individual worker level. That is, if you have a cluster of workers then each - individual machine may postpone its first task invocation by the given amount of - time to leave room for the service to settle, but _other_ workers may still - continue to invoke the task on the regular cadence in the meantime. - -## 0.3.0-next.1 - -### Minor Changes - -- ab008a0988: Adds the ability to manually trigger tasks which are registered - -### Patch Changes - -- bdd2773202: Refactored the internal `TaskWorker` class to make it easier to test. -- Updated dependencies - - @backstage/backend-common@0.13.2-next.1 - -## 0.2.2-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.13.2-next.0 - -## 0.2.1 - -### Patch Changes - -- efc73db10c: Use `better-sqlite3` instead of `@vscode/sqlite3` -- Updated dependencies - - @backstage/backend-common@0.13.1 - - @backstage/config@1.0.0 - - @backstage/errors@1.0.0 - - @backstage/types@1.0.0 - -## 0.2.0 - -### Minor Changes - -- 9461f73643: **BREAKING**: The `TaskDefinition` type has been removed, and replaced by the equal pair `TaskScheduleDefinition` and `TaskInvocationDefinition`. The interface for `PluginTaskScheduler.scheduleTask` stays effectively unchanged, so this only affects you if you use the actual types directly. - - Added the method `PluginTaskScheduler.createTaskSchedule`, which returns a `TaskSchedule` wrapper that is convenient to pass down into classes that want to control their task invocations while the caller wants to retain control of the actual schedule chosen. - -### Patch Changes - -- ab7cd7d70e: Do some groundwork for supporting the `better-sqlite3` driver, to maybe eventually replace `@vscode/sqlite3` (#9912) -- 7290dda9d4: Relaxed the task ID requirement to now support any non-empty string -- ae2ed04076: Add support for cron syntax to configure task frequency - `TaskScheduleDefinition.frequency` can now be both a `Duration` and an object on the form `{ cron: string }`, where the latter is expected to be on standard crontab format (e.g. `'0 */2 * * *'`). -- Updated dependencies - - @backstage/backend-common@0.13.0 - -## 0.2.0-next.0 - -### Minor Changes - -- 9461f73643: **BREAKING**: The `TaskDefinition` type has been removed, and replaced by the equal pair `TaskScheduleDefinition` and `TaskInvocationDefinition`. The interface for `PluginTaskScheduler.scheduleTask` stays effectively unchanged, so this only affects you if you use the actual types directly. - - Added the method `PluginTaskScheduler.createTaskSchedule`, which returns a `TaskSchedule` wrapper that is convenient to pass down into classes that want to control their task invocations while the caller wants to retain control of the actual schedule chosen. - -### Patch Changes - -- ab7cd7d70e: Do some groundwork for supporting the `better-sqlite3` driver, to maybe eventually replace `@vscode/sqlite3` (#9912) -- 7290dda9d4: Relaxed the task ID requirement to now support any non-empty string -- Updated dependencies - - @backstage/backend-common@0.13.0-next.0 - -## 0.1.10 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.12.0 - -## 0.1.9 - -### Patch Changes - -- dc97845422: Only output janitor logs when actually timing out tasks -- Updated dependencies - - @backstage/backend-common@0.11.0 - -## 0.1.8 - -### Patch Changes - -- Fix for the previous release with missing type declarations. -- Updated dependencies - - @backstage/backend-common@0.10.9 - - @backstage/config@0.1.15 - - @backstage/errors@0.2.2 - - @backstage/types@0.1.3 - -## 0.1.7 - -### Patch Changes - -- c77c5c7eb6: Added `backstage.role` to `package.json` -- Updated dependencies - - @backstage/backend-common@0.10.8 - - @backstage/errors@0.2.1 - - @backstage/config@0.1.14 - - @backstage/types@0.1.2 - -## 0.1.6 - -### Patch Changes - -- 2441d1cf59: chore(deps): bump `knex` from 0.95.6 to 1.0.2 - - This also replaces `sqlite3` with `@vscode/sqlite3` 5.0.7 - -- Updated dependencies - - @backstage/backend-common@0.10.7 - -## 0.1.6-next.0 - -### Patch Changes - -- 2441d1cf59: chore(deps): bump `knex` from 0.95.6 to 1.0.2 - - This also replaces `sqlite3` with `@vscode/sqlite3` 5.0.7 - -- Updated dependencies - - @backstage/backend-common@0.10.7-next.0 - -## 0.1.5 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.6 - -## 0.1.5-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.6-next.0 - -## 0.1.4 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.4 - - @backstage/config@0.1.13 - -## 0.1.4-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.4-next.0 - - @backstage/config@0.1.13-next.0 - -## 0.1.3 - -### Patch Changes - -- Updated dependencies - - @backstage/config@0.1.12 - - @backstage/backend-common@0.10.3 - - @backstage/errors@0.2.0 - -## 0.1.2 - -### Patch Changes - -- e188b37024: Updated README to clarify the following: - - - Dashes cannot be used in the `id`, changed to an underscore in the example - - The `timeout` is required, this was also added to the example - - Added a note about tasks not running as expected for local development using persistent database - -- Updated dependencies - - @backstage/backend-common@0.10.2 - -## 0.1.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.0 diff --git a/packages/backend-tasks/README.md b/packages/backend-tasks/README.md deleted file mode 100644 index bdb151dea6..0000000000 --- a/packages/backend-tasks/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# @backstage/backend-tasks - -> [!CAUTION] -> This package is deprecated and will be removed in a near future. - -Common distributed task management for Backstage backends. - -## Usage - -> [!CAUTION] -> Please note that the documentation below is only valid for versions equal to or below `0.5.28-next.3`. -> As this package will be deleted soon, we recommend that you migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from `@backstage/backend-defaults`. Here are the [backend](https://backstage.io/docs/backend-system/building-backends/migrating) and [plugin](https://backstage.io/docs/backend-system/building-plugins-and-modules/migrating) migration guides. - -Add the library to your backend package: - -```bash -# From your Backstage root directory -yarn --cwd packages/backend add @backstage/backend-tasks -``` - -then make use of its facilities as necessary: - -```typescript -import { TaskScheduler } from '@backstage/backend-tasks'; - -const scheduler = TaskScheduler.fromConfig(rootConfig).forPlugin('my-plugin'); - -await scheduler.scheduleTask({ - id: 'refresh_things', - frequency: { cron: '*/5 * * * *' }, // every 5 minutes, also supports Duration - timeout: { minutes: 15 }, - fn: async () => { - await entityProvider.run(); - }, -}); -``` diff --git a/packages/backend-tasks/api-report.md b/packages/backend-tasks/api-report.md deleted file mode 100644 index c0f9da3a01..0000000000 --- a/packages/backend-tasks/api-report.md +++ /dev/null @@ -1,114 +0,0 @@ -## API Report File for "@backstage/backend-tasks" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { Config } from '@backstage/config'; -import { Duration } from 'luxon'; -import { HumanDuration as HumanDuration_2 } from '@backstage/types'; -import { JsonObject } from '@backstage/types'; -import { LegacyRootDatabaseService } from '@backstage/backend-common'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import { PluginDatabaseManager } from '@backstage/backend-common'; - -// @public @deprecated -export type HumanDuration = HumanDuration_2; - -// @public @deprecated -export interface PluginTaskScheduler { - createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner; - getScheduledTasks(): Promise; - scheduleTask( - task: TaskScheduleDefinition & TaskInvocationDefinition, - ): Promise; - triggerTask(id: string): Promise; -} - -// @public @deprecated -export function readTaskScheduleDefinitionFromConfig( - config: Config, -): TaskScheduleDefinition; - -// @public @deprecated -export type TaskDescriptor = { - id: string; - scope: 'global' | 'local'; - settings: { - version: number; - } & JsonObject; -}; - -// @public @deprecated -export type TaskFunction = - | ((abortSignal: AbortSignal) => void | Promise) - | (() => void | Promise); - -// @public @deprecated -export interface TaskInvocationDefinition { - fn: TaskFunction; - id: string; - signal?: AbortSignal; -} - -// @public @deprecated -export interface TaskRunner { - run(task: TaskInvocationDefinition): Promise; -} - -// @public @deprecated -export interface TaskScheduleDefinition { - frequency: - | { - cron: string; - } - | Duration - | HumanDuration_2 - /** - * This task will only run when manually triggered with the `triggerTask` method; no automatic - * scheduling. This is useful for locking of global tasks that should not be run concurrently. - */ - | { - trigger: 'manual'; - }; - initialDelay?: Duration | HumanDuration_2; - scope?: 'global' | 'local'; - timeout: Duration | HumanDuration_2; -} - -// @public @deprecated -export interface TaskScheduleDefinitionConfig { - frequency: - | { - cron: string; - } - | string - | HumanDuration_2; - initialDelay?: string | HumanDuration_2; - scope?: 'global' | 'local'; - timeout: string | HumanDuration_2; -} - -// @public @deprecated -export class TaskScheduler { - constructor( - databaseManager: LegacyRootDatabaseService, - logger: LoggerService, - ); - // @deprecated - forPlugin(pluginId: string): PluginTaskScheduler; - // @deprecated (undocumented) - static forPlugin(opts: { - pluginId: string; - databaseManager: PluginDatabaseManager; - logger: LoggerService; - }): PluginTaskScheduler; - // @deprecated (undocumented) - static fromConfig( - config: Config, - options?: { - databaseManager?: LegacyRootDatabaseService; - logger?: LoggerService; - }, - ): TaskScheduler; -} -``` diff --git a/packages/backend-tasks/catalog-info.yaml b/packages/backend-tasks/catalog-info.yaml deleted file mode 100644 index f9b81066e6..0000000000 --- a/packages/backend-tasks/catalog-info.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: backstage-backend-tasks - title: '@backstage/backend-tasks' - description: Common distributed task management library for Backstage backends -spec: - lifecycle: experimental - type: backstage-node-library - owner: maintainers diff --git a/packages/backend-tasks/knip-report.md b/packages/backend-tasks/knip-report.md deleted file mode 100644 index 2661c35327..0000000000 --- a/packages/backend-tasks/knip-report.md +++ /dev/null @@ -1,2 +0,0 @@ -# Knip report - diff --git a/packages/backend-tasks/migrations/20210928160613_init.js b/packages/backend-tasks/migrations/20210928160613_init.js deleted file mode 100644 index f9900cab11..0000000000 --- a/packages/backend-tasks/migrations/20210928160613_init.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// @ts-check - -/** - * @param {import('knex').Knex} knex - */ -exports.up = async function up(knex) { - // - // tasks - // - await knex.schema.createTable('backstage_backend_tasks__tasks', table => { - table.comment('Tasks used for scheduling work on multiple workers'); - table - .string('id') - .primary() - .notNullable() - .comment('The unique ID of this particular task'); - table - .text('settings_json') - .notNullable() - .comment('JSON serialized object with properties for this task'); - table - .dateTime('next_run_start_at') - .notNullable() - .comment('The next time that the task should be started'); - table - .text('current_run_ticket') - .nullable() - .comment('A unique ticket for the current task run'); - table - .dateTime('current_run_started_at') - .nullable() - .comment('The time that the current task run started'); - table - .dateTime('current_run_expires_at') - .nullable() - .comment('The time that the current task run will time out'); - }); -}; - -/** - * @param {import('knex').Knex} knex - */ -exports.down = async function down(knex) { - // - // tasks - // - await knex.schema.dropTable('backstage_backend_tasks__tasks'); -}; diff --git a/packages/backend-tasks/migrations/20240712211735_nullable_next_run.js b/packages/backend-tasks/migrations/20240712211735_nullable_next_run.js deleted file mode 100644 index efbef1c76f..0000000000 --- a/packages/backend-tasks/migrations/20240712211735_nullable_next_run.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2024 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. - */ - -// @ts-check - -/** - * @param { import("knex").Knex } knex - * @returns { Promise } - */ -exports.up = async function up(knex) { - await knex.schema.alterTable('backstage_backend_tasks__tasks', table => { - table.setNullable('next_run_start_at'); - }); -}; - -/** - * @param { import("knex").Knex } knex - * @returns { Promise } - */ -exports.down = async function down(knex) { - await knex - .delete() - .from('backstage_backend_tasks__tasks') - .where({ next_run_start_at: null }); - await knex.schema.alterTable('backstage_backend_tasks__tasks', table => { - table.dropNullable('next_run_start_at'); - }); -}; diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json deleted file mode 100644 index f0209c1fa9..0000000000 --- a/packages/backend-tasks/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@backstage/backend-tasks", - "version": "0.6.0", - "description": "Common distributed task management library for Backstage backends", - "backstage": { - "role": "node-library" - }, - "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" - }, - "keywords": [ - "backstage" - ], - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/backend-tasks" - }, - "license": "Apache-2.0", - "main": "src/index.ts", - "types": "src/index.ts", - "files": [ - "dist", - "migrations/**/*.{js,d.ts}" - ], - "scripts": { - "build": "backstage-cli package build", - "clean": "backstage-cli package clean", - "lint": "backstage-cli package lint", - "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack", - "start": "backstage-cli package start", - "test": "backstage-cli package test" - }, - "dependencies": { - "@backstage/backend-common": "workspace:^", - "@backstage/backend-plugin-api": "workspace:^", - "@backstage/config": "workspace:^", - "@backstage/errors": "workspace:^", - "@backstage/types": "workspace:^", - "@opentelemetry/api": "^1.3.0", - "@types/luxon": "^3.0.0", - "cron": "^3.0.0", - "knex": "^3.0.0", - "lodash": "^4.17.21", - "luxon": "^3.0.0", - "uuid": "^9.0.0", - "zod": "^3.22.4" - }, - "devDependencies": { - "@backstage/backend-test-utils": "workspace:^", - "@backstage/cli": "workspace:^", - "wait-for-expect": "^3.0.2" - } -} diff --git a/packages/backend-tasks/src/database/migrateBackendTasks.ts b/packages/backend-tasks/src/database/migrateBackendTasks.ts deleted file mode 100644 index ae5048fbf7..0000000000 --- a/packages/backend-tasks/src/database/migrateBackendTasks.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 { resolvePackagePath } from '@backstage/backend-plugin-api'; -import { Knex } from 'knex'; -import { DB_MIGRATIONS_TABLE } from './tables'; - -export async function migrateBackendTasks(knex: Knex): Promise { - const migrationsDir = resolvePackagePath( - '@backstage/backend-tasks', - 'migrations', - ); - - await knex.migrate.latest({ - directory: migrationsDir, - tableName: DB_MIGRATIONS_TABLE, - }); -} diff --git a/packages/backend-tasks/src/database/tables.ts b/packages/backend-tasks/src/database/tables.ts deleted file mode 100644 index 63aad6e42a..0000000000 --- a/packages/backend-tasks/src/database/tables.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const DB_MIGRATIONS_TABLE = 'backstage_backend_tasks__knex_migrations'; -export const DB_TASKS_TABLE = 'backstage_backend_tasks__tasks'; - -export type DbTasksRow = { - id: string; - settings_json: string; - next_run_start_at: Date; - current_run_ticket?: string; - current_run_started_at?: Date | string; - current_run_expires_at?: Date | string; -}; diff --git a/packages/backend-tasks/src/index.ts b/packages/backend-tasks/src/index.ts deleted file mode 100644 index 83c7606700..0000000000 --- a/packages/backend-tasks/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Common distributed task management library for Backstage backends - * - * @remarks - * This package is deprecated and will be removed in a near future. - * Please migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from `@backstage/backend-defaults`. - * - * @packageDocumentation - */ - -export * from './deprecated'; diff --git a/packages/backend-tasks/src/migrations.test.ts b/packages/backend-tasks/src/migrations.test.ts deleted file mode 100644 index 7583948f71..0000000000 --- a/packages/backend-tasks/src/migrations.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 { Knex } from 'knex'; -import { TestDatabases } from '@backstage/backend-test-utils'; -import fs from 'fs'; - -const migrationsDir = `${__dirname}/../migrations`; -const migrationsFiles = fs.readdirSync(migrationsDir).sort(); - -async function migrateUpOnce(knex: Knex): Promise { - await knex.migrate.up({ directory: migrationsDir }); -} - -async function migrateDownOnce(knex: Knex): Promise { - await knex.migrate.down({ directory: migrationsDir }); -} - -async function migrateUntilBefore(knex: Knex, target: string): Promise { - const index = migrationsFiles.indexOf(target); - if (index === -1) { - throw new Error(`Migration ${target} not found`); - } - for (let i = 0; i < index; i++) { - await migrateUpOnce(knex); - } -} - -jest.setTimeout(60_000); - -describe('migrations', () => { - const databases = TestDatabases.create(); - - it.each(databases.eachSupportedId())( - '20210928160613_init.js, %p', - async databaseId => { - const knex = await databases.init(databaseId); - - await migrateUntilBefore(knex, '20210928160613_init.js'); - await migrateUpOnce(knex); - - await knex('backstage_backend_tasks__tasks').insert({ - id: 'test', - settings_json: '{}', - next_run_start_at: knex.fn.now(), - }); - - await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([ - { - id: 'test', - settings_json: '{}', - next_run_start_at: expect.anything(), - current_run_ticket: null, - current_run_started_at: null, - current_run_expires_at: null, - }, - ]); - - await migrateDownOnce(knex); - - // This looks odd - you might expect a .toThrow at the end but that - // actually is flaky for some reason specifically on sqlite when - // performing multiple runs in sequence - await expect(knex('backstage_backend_tasks__tasks')).rejects.toEqual( - expect.anything(), - ); - - await knex.destroy(); - }, - ); - - it.each(databases.eachSupportedId())( - '20240712211735_nullable_next_run.js, %p', - async databaseId => { - const knex = await databases.init(databaseId); - - await migrateUntilBefore(knex, '20240712211735_nullable_next_run.js'); - await migrateUpOnce(knex); - - await knex('backstage_backend_tasks__tasks').insert({ - id: 'test', - settings_json: '{}', - next_run_start_at: knex.raw('null'), - }); - - await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([ - { - id: 'test', - settings_json: '{}', - next_run_start_at: null, - current_run_ticket: null, - current_run_started_at: null, - current_run_expires_at: null, - }, - ]); - - await migrateDownOnce(knex); - - await expect( - knex('backstage_backend_tasks__tasks').insert({ - id: 'test', - settings_json: '{}', - next_run_start_at: knex.raw('null'), - }), - ).rejects.toEqual(expect.anything()); - - await knex.destroy(); - }, - ); -}); diff --git a/packages/backend-tasks/src/setupTests.ts b/packages/backend-tasks/src/setupTests.ts deleted file mode 100644 index 76619a2542..0000000000 --- a/packages/backend-tasks/src/setupTests.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { TestDatabases } from '@backstage/backend-test-utils'; -import { Settings } from 'luxon'; - -// TS still thinks that methods can return null / placeholders, but we still want to throw as soon as possible when things go wrong -Settings.throwOnInvalid = true; - -TestDatabases.setDefaults({ - ids: ['MYSQL_8', 'POSTGRES_16', 'POSTGRES_12', 'SQLITE_3'], -}); diff --git a/packages/backend-tasks/src/tasks/LocalTaskWorker.test.ts b/packages/backend-tasks/src/tasks/LocalTaskWorker.test.ts deleted file mode 100644 index d4d3d16067..0000000000 --- a/packages/backend-tasks/src/tasks/LocalTaskWorker.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 { LocalTaskWorker } from './LocalTaskWorker'; -import { mockServices } from '@backstage/backend-test-utils'; - -describe('LocalTaskWorker', () => { - const logger = mockServices.logger.mock(); - - it('runs the happy path (with iso duration) and handles cancellation', async () => { - const fn = jest.fn(); - const controller = new AbortController(); - - const worker = new LocalTaskWorker('a', fn, logger); - worker.start( - { - version: 2, - initialDelayDuration: 'PT0.2S', - cadence: 'PT0.2S', - timeoutAfterDuration: 'PT1S', - }, - { signal: controller.signal }, - ); - - // TODO(freben): Rewrite to fake timers - tried, but it wouldn't work - expect(fn).toHaveBeenCalledTimes(0); - await new Promise(r => setTimeout(r, 100)); - expect(fn).toHaveBeenCalledTimes(0); - await new Promise(r => setTimeout(r, 200)); - expect(fn).toHaveBeenCalledTimes(1); - await new Promise(r => setTimeout(r, 200)); - expect(fn).toHaveBeenCalledTimes(2); - controller.abort(); - await new Promise(r => setTimeout(r, 200)); - expect(fn).toHaveBeenCalledTimes(2); - }); - - it('runs the happy path (with a cron expression) and handles cancellation', async () => { - const fn = jest.fn(); - const controller = new AbortController(); - - // Await until system time is just past a second boundary (since cron is - // wall clock based) - await new Promise(r => setTimeout(r, 1000 - (Date.now() % 1000) + 10)); - - const worker = new LocalTaskWorker('a', fn, logger); - worker.start( - { - version: 2, - initialDelayDuration: 'PT0.2S', - cadence: '* * * * * *', - timeoutAfterDuration: 'PT1S', - }, - { signal: controller.signal }, - ); - - // TODO(freben): Rewrite to fake timers - tried, but it wouldn't work - expect(fn).toHaveBeenCalledTimes(0); - await new Promise(r => setTimeout(r, 100)); - expect(fn).toHaveBeenCalledTimes(0); - await new Promise(r => setTimeout(r, 200)); - expect(fn).toHaveBeenCalledTimes(1); - await new Promise(r => setTimeout(r, 1000)); - expect(fn).toHaveBeenCalledTimes(2); - controller.abort(); - await new Promise(r => setTimeout(r, 1000)); - expect(fn).toHaveBeenCalledTimes(2); - }); - - it('can trigger to abort wait', async () => { - const fn = jest.fn(); - const controller = new AbortController(); - - const worker = new LocalTaskWorker('a', fn, logger); - worker.start( - { - version: 2, - initialDelayDuration: 'PT0.2S', - cadence: 'PT0.2S', - timeoutAfterDuration: 'PT1S', - }, - { signal: controller.signal }, - ); - - // TODO(freben): Rewrite to fake timers - tried, but it wouldn't work - expect(fn).toHaveBeenCalledTimes(0); - await new Promise(r => setTimeout(r, 100)); - expect(fn).toHaveBeenCalledTimes(0); - await new Promise(r => setTimeout(r, 200)); - expect(fn).toHaveBeenCalledTimes(1); - worker.trigger(); - await new Promise(r => setTimeout(r, 10)); - expect(fn).toHaveBeenCalledTimes(2); - controller.abort(); - }); -}); diff --git a/packages/backend-tasks/src/tasks/LocalTaskWorker.ts b/packages/backend-tasks/src/tasks/LocalTaskWorker.ts deleted file mode 100644 index e79acb35f0..0000000000 --- a/packages/backend-tasks/src/tasks/LocalTaskWorker.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 { ConflictError } from '@backstage/errors'; -import { CronTime } from 'cron'; -import { DateTime, Duration } from 'luxon'; -import { TaskFunction, TaskSettingsV2 } from './types'; -import { delegateAbortController, sleep } from './util'; -import { LoggerService } from '@backstage/backend-plugin-api'; - -/** - * Implements tasks that run locally without cross-host collaboration. - * - * @private - */ -export class LocalTaskWorker { - private abortWait: AbortController | undefined; - - constructor( - private readonly taskId: string, - private readonly fn: TaskFunction, - private readonly logger: LoggerService, - ) {} - - start(settings: TaskSettingsV2, options?: { signal?: AbortSignal }) { - this.logger.info( - `Task worker starting: ${this.taskId}, ${JSON.stringify(settings)}`, - ); - - (async () => { - let attemptNum = 1; - for (;;) { - try { - if (settings.initialDelayDuration) { - await this.sleep( - Duration.fromISO(settings.initialDelayDuration), - options?.signal, - ); - } - - while (!options?.signal?.aborted) { - const startTime = process.hrtime(); - await this.runOnce(settings, options?.signal); - const timeTaken = process.hrtime(startTime); - await this.waitUntilNext( - settings, - (timeTaken[0] + timeTaken[1] / 1e9) * 1000, - options?.signal, - ); - } - - this.logger.info(`Task worker finished: ${this.taskId}`); - attemptNum = 0; - break; - } catch (e) { - attemptNum += 1; - this.logger.warn( - `Task worker failed unexpectedly, attempt number ${attemptNum}, ${e}`, - ); - await sleep(Duration.fromObject({ seconds: 1 })); - } - } - })(); - } - - trigger(): void { - if (!this.abortWait) { - throw new ConflictError(`Task ${this.taskId} is currently running`); - } - this.abortWait.abort(); - } - - /** - * Makes a single attempt at running the task to completion. - */ - private async runOnce( - settings: TaskSettingsV2, - signal?: AbortSignal, - ): Promise { - // Abort the task execution either if the worker is stopped, or if the - // task timeout is hit - const taskAbortController = delegateAbortController(signal); - const timeoutHandle = setTimeout(() => { - taskAbortController.abort(); - }, Duration.fromISO(settings.timeoutAfterDuration).as('milliseconds')); - - try { - await this.fn(taskAbortController.signal); - } catch (e) { - // ignore intentionally - } - - // release resources - clearTimeout(timeoutHandle); - taskAbortController.abort(); - } - - /** - * Sleeps until it's time to run the task again. - */ - private async waitUntilNext( - settings: TaskSettingsV2, - lastRunMillis: number, - signal?: AbortSignal, - ) { - if (signal?.aborted) { - return; - } - - const isCron = !settings.cadence.startsWith('P'); - let dt: number; - - if (isCron) { - const nextRun = +new CronTime(settings.cadence).sendAt().toJSDate(); - dt = nextRun - Date.now(); - } else { - dt = - Duration.fromISO(settings.cadence).as('milliseconds') - lastRunMillis; - } - - dt = Math.max(dt, 0); - - this.logger.debug( - `task: ${this.taskId} will next occur around ${DateTime.now().plus( - Duration.fromMillis(dt), - )}`, - ); - - await this.sleep(Duration.fromMillis(dt), signal); - } - - private async sleep( - duration: Duration, - abortSignal?: AbortSignal, - ): Promise { - this.abortWait = delegateAbortController(abortSignal); - await sleep(duration, this.abortWait.signal); - this.abortWait.abort(); // cleans up resources - this.abortWait = undefined; - } -} diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts deleted file mode 100644 index eca899098f..0000000000 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts +++ /dev/null @@ -1,351 +0,0 @@ -/* - * 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 { - TestDatabaseId, - TestDatabases, - mockServices, -} from '@backstage/backend-test-utils'; -import { ConflictError, NotFoundError } from '@backstage/errors'; -import { Duration } from 'luxon'; -import { migrateBackendTasks } from '../database/migrateBackendTasks'; -import { - parseDuration, - PluginTaskSchedulerImpl, -} from './PluginTaskSchedulerImpl'; - -function defer() { - let resolve = () => {}; - const promise = new Promise(_resolve => { - resolve = _resolve; - }); - return { promise, resolve }; -} - -jest.setTimeout(60_000); - -describe('PluginTaskManagerImpl', () => { - const databases = TestDatabases.create({ - ids: ['POSTGRES_16', 'POSTGRES_12', '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); - const logger = mockServices.logger.mock(); - const manager = new PluginTaskSchedulerImpl(async () => knex, logger); - return { knex, manager }; - } - - // This is just to test the wrapper code; most of the actual tests are in - // TaskWorker.test.ts - describe('scheduleTask with global scope', () => { - it.each(databases.eachSupportedId())( - 'can run the v1 happy path, %p', - async databaseId => { - const { manager } = await init(databaseId); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromMillis(5000), - fn, - scope: 'global', - }); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, - ); - - it.each(databases.eachSupportedId())( - 'can run the v2 happy path, %p', - async databaseId => { - const { manager } = await init(databaseId); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager.scheduleTask({ - id: 'task2', - timeout: Duration.fromMillis(5000), - frequency: { cron: '* * * * * *' }, - fn, - scope: 'global', - }); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, - ); - }); - - describe('triggerTask with global scope', () => { - it.each(databases.eachSupportedId())( - 'can manually trigger a task, %p', - async databaseId => { - const { manager } = await init(databaseId); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromObject({ years: 1 }), - initialDelay: Duration.fromObject({ years: 1 }), - fn, - scope: 'global', - }); - - await manager.triggerTask('task1'); - jest.advanceTimersByTime(5000); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, - ); - - it.each(databases.eachSupportedId())( - 'cant trigger a non-existent task, %p', - async databaseId => { - const { manager } = await init(databaseId); - - const fn = jest.fn(); - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromObject({ years: 1 }), - fn, - scope: 'global', - }); - - await expect(() => manager.triggerTask('task2')).rejects.toThrow( - NotFoundError, - ); - }, - ); - - it.each(databases.eachSupportedId())( - 'cant trigger a running task, %p', - async databaseId => { - const { manager } = await init(databaseId); - - const { promise, resolve } = defer(); - - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromObject({ years: 1 }), - fn: async () => { - resolve(); - await new Promise(r => setTimeout(r, 20000)); - }, - scope: 'global', - }); - - await promise; - await expect(() => manager.triggerTask('task1')).rejects.toThrow( - ConflictError, - ); - }, - ); - }); - - // This is just to test the wrapper code; most of the actual tests are in - // TaskWorker.test.ts - describe('scheduleTask with local scope', () => { - it('can run the v1 happy path', async () => { - const { manager } = await init('SQLITE_3'); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager.scheduleTask({ - id: 'task1', - timeout: { milliseconds: 5000 }, - frequency: { milliseconds: 5000 }, - fn, - scope: 'local', - }); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, 60_000); - - it('can run the v2 happy path', async () => { - const { manager } = await init('SQLITE_3'); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager.scheduleTask({ - id: 'task2', - timeout: Duration.fromMillis(5000), - frequency: { cron: '* * * * * *' }, - fn, - scope: 'local', - }); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, 60_000); - }); - - describe('triggerTask with local scope', () => { - it('can manually trigger a task', async () => { - const { manager } = await init('SQLITE_3'); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromObject({ years: 1 }), - initialDelay: Duration.fromObject({ years: 1 }), - fn, - scope: 'local', - }); - - await manager.triggerTask('task1'); - jest.advanceTimersByTime(5000); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, 60_000); - - it('cant trigger a non-existent task', async () => { - const { manager } = await init('SQLITE_3'); - - const fn = jest.fn(); - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromObject({ years: 1 }), - fn, - scope: 'local', - }); - - await expect(() => manager.triggerTask('task2')).rejects.toThrow( - NotFoundError, - ); - }, 60_000); - - it('cant trigger a running task', async () => { - const { manager } = await init('SQLITE_3'); - - const { promise, resolve } = defer(); - - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromObject({ years: 1 }), - fn: async () => { - resolve(); - await new Promise(r => setTimeout(r, 20000)); - }, - scope: 'local', - }); - - await promise; - await expect(() => manager.triggerTask('task1')).rejects.toThrow( - ConflictError, - ); - }, 60_000); - }); - - // This is just to test the wrapper code; most of the actual tests are in - // TaskWorker.test.ts - describe('createScheduledTaskRunner', () => { - it.each(databases.eachSupportedId())( - 'can run the happy path, %p', - async databaseId => { - const { manager } = await init(databaseId); - - const fn = jest.fn(); - const promise = new Promise(resolve => fn.mockImplementation(resolve)); - await manager - .createScheduledTaskRunner({ - timeout: Duration.fromMillis(5000), - frequency: Duration.fromMillis(5000), - scope: 'global', - }) - .run({ - id: 'task1', - fn, - }); - - await promise; - expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal)); - }, - ); - }); - - describe('can fetch task ids', () => { - it.each(databases.eachSupportedId())( - 'can fetch both global and local task ids, %p', - async databaseId => { - const { manager } = await init(databaseId); - const fn = jest.fn(); - - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromMillis(5000), - fn, - scope: 'global', - }); - - await manager.scheduleTask({ - id: 'task2', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromMillis(5000), - fn, - scope: 'local', - }); - - await expect(manager.getScheduledTasks()).resolves.toEqual([ - { - id: 'task1', - scope: 'global', - settings: expect.objectContaining({ cadence: 'PT5S' }), - }, - { - id: 'task2', - scope: 'local', - settings: expect.objectContaining({ cadence: 'PT5S' }), - }, - ]); - }, - ); - }); - - describe('parseDuration', () => { - it('should parse durations', () => { - expect(parseDuration({ milliseconds: 5000 })).toEqual('PT5S'); - expect(parseDuration(Duration.fromMillis(5000))).toEqual('PT5S'); - expect(parseDuration({ cron: '1 * * * *' })).toEqual('1 * * * *'); - expect(parseDuration({ trigger: 'manual' })).toEqual('manual'); - }); - }); -}); diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts deleted file mode 100644 index 0ccc77a6c1..0000000000 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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 { Knex } from 'knex'; -import { Duration } from 'luxon'; -import { LocalTaskWorker } from './LocalTaskWorker'; -import { TaskWorker } from './TaskWorker'; -import { - PluginTaskScheduler, - TaskDescriptor, - TaskFunction, - TaskInvocationDefinition, - TaskRunner, - TaskScheduleDefinition, - TaskSettingsV2, -} from './types'; -import { validateId } from './util'; -import { Counter, Histogram, metrics } from '@opentelemetry/api'; -import { LoggerService } from '@backstage/backend-plugin-api'; - -/** - * Implements the actual task management. - */ -export class PluginTaskSchedulerImpl implements PluginTaskScheduler { - private readonly localTasksById = new Map(); - private readonly allScheduledTasks: TaskDescriptor[] = []; - - private readonly counter: Counter; - private readonly duration: Histogram; - - constructor( - private readonly databaseFactory: () => Promise, - private readonly logger: LoggerService, - ) { - const meter = metrics.getMeter('default'); - this.counter = meter.createCounter('backend_tasks.task.runs.count', { - description: 'Total number of times a task has been run', - }); - this.duration = meter.createHistogram('backend_tasks.task.runs.duration', { - description: 'Histogram of task run durations', - unit: 'seconds', - }); - } - - async triggerTask(id: string): Promise { - const localTask = this.localTasksById.get(id); - if (localTask) { - localTask.trigger(); - return; - } - - const knex = await this.databaseFactory(); - await TaskWorker.trigger(knex, id); - } - - async scheduleTask( - task: TaskScheduleDefinition & TaskInvocationDefinition, - ): Promise { - validateId(task.id); - const scope = task.scope ?? 'global'; - - const settings: TaskSettingsV2 = { - version: 2, - cadence: parseDuration(task.frequency), - initialDelayDuration: - task.initialDelay && parseDuration(task.initialDelay), - timeoutAfterDuration: parseDuration(task.timeout), - }; - - if (scope === 'global') { - const knex = await this.databaseFactory(); - const worker = new TaskWorker( - task.id, - this.wrapInMetrics(task.fn, { labels: { taskId: task.id, scope } }), - knex, - this.logger.child({ task: task.id }), - ); - await worker.start(settings, { signal: task.signal }); - } else { - const worker = new LocalTaskWorker( - task.id, - this.wrapInMetrics(task.fn, { labels: { taskId: task.id, scope } }), - this.logger.child({ task: task.id }), - ); - worker.start(settings, { signal: task.signal }); - this.localTasksById.set(task.id, worker); - } - - this.allScheduledTasks.push({ - id: task.id, - scope: scope, - settings: settings, - }); - } - - createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner { - return { - run: async task => { - await this.scheduleTask({ ...task, ...schedule }); - }, - }; - } - - async getScheduledTasks(): Promise { - return this.allScheduledTasks; - } - - private wrapInMetrics( - fn: TaskFunction, - opts: { labels: Record }, - ): TaskFunction { - return async abort => { - const labels = { - ...opts.labels, - }; - this.counter.add(1, { ...labels, result: 'started' }); - - const startTime = process.hrtime(); - - try { - await fn(abort); - labels.result = 'completed'; - } catch (ex) { - labels.result = 'failed'; - throw ex; - } finally { - const delta = process.hrtime(startTime); - const endTime = delta[0] + delta[1] / 1e9; - this.counter.add(1, labels); - this.duration.record(endTime, labels); - } - }; - } -} - -export function parseDuration( - frequency: TaskScheduleDefinition['frequency'], -): string { - if ('cron' in frequency) { - return frequency.cron; - } - if ('trigger' in frequency) { - return frequency.trigger; - } - - const parsed = Duration.isDuration(frequency) - ? frequency - : Duration.fromObject(frequency); - - if (!parsed.isValid) { - throw new Error( - `Invalid duration, ${parsed.invalidReason}: ${parsed.invalidExplanation}`, - ); - } - - return parsed.toISO()!; -} diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerJanitor.test.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerJanitor.test.ts deleted file mode 100644 index 93b5d7d189..0000000000 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerJanitor.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 { TestDatabases, mockServices } from '@backstage/backend-test-utils'; -import { Knex } from 'knex'; -import { Duration } from 'luxon'; -import waitForExpect from 'wait-for-expect'; -import { migrateBackendTasks } from '../database/migrateBackendTasks'; -import { DbTasksRow, DB_TASKS_TABLE } from '../database/tables'; -import { PluginTaskSchedulerJanitor } from './PluginTaskSchedulerJanitor'; -import { createTestScopedSignal } from './__testUtils__/createTestScopedSignal'; - -const insertTask = async (knex: Knex, task: DbTasksRow) => { - return knex(DB_TASKS_TABLE) - .insert(task) - .onConflict('id') - .merge(['settings_json']); -}; - -const getTask = async (knex: Knex): Promise => { - return (await knex(DB_TASKS_TABLE))[0]; -}; - -describe('PluginTaskSchedulerJanitor', () => { - const logger = mockServices.logger.mock(); - const databases = TestDatabases.create({ - ids: [ - /* 'MYSQL_8' not supported yet */ - 'POSTGRES_16', - 'POSTGRES_12', - 'SQLITE_3', - 'MYSQL_8', - ], - }); - const testScopedSignal = createTestScopedSignal(); - - jest.setTimeout(60_000); - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it.each(databases.eachSupportedId())( - 'Should update date if current_run_expires_at expires, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const dateYesterday = new Date( - new Date().setDate(new Date().getDate() - 1), - ); - - await insertTask(knex, { - id: 'task1', - settings_json: '', - next_run_start_at: new Date('2023-03-01 00:00:00'), - current_run_ticket: 'ticket', - current_run_started_at: dateYesterday, - current_run_expires_at: dateYesterday, - }); - - const worker = new PluginTaskSchedulerJanitor({ - waitBetweenRuns: Duration.fromObject({ milliseconds: 20 }), - knex, - logger, - }); - - worker.start(testScopedSignal()); - - await waitForExpect(async () => { - await expect(getTask(knex)).resolves.toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: null, - current_run_started_at: null, - current_run_expires_at: null, - }), - ); - }); - }, - ); -}); diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerJanitor.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerJanitor.ts deleted file mode 100644 index 7b6becab91..0000000000 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerJanitor.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 { Knex } from 'knex'; -import { Duration } from 'luxon'; -import { DB_TASKS_TABLE, DbTasksRow } from '../database/tables'; -import { sleep } from './util'; -import { LoggerService } from '@backstage/backend-plugin-api'; - -/** - * Makes sure to auto-expire and clean up things that time out or for other - * reasons should not be left lingering. - */ -export class PluginTaskSchedulerJanitor { - private readonly knex: Knex; - private readonly waitBetweenRuns: Duration; - private readonly logger: LoggerService; - - constructor(options: { - knex: Knex; - waitBetweenRuns: Duration; - logger: LoggerService; - }) { - this.knex = options.knex; - this.waitBetweenRuns = options.waitBetweenRuns; - this.logger = options.logger; - } - - async start(abortSignal?: AbortSignal) { - while (!abortSignal?.aborted) { - try { - await this.runOnce(); - } catch (e) { - this.logger.warn(`Error while performing janitorial tasks, ${e}`); - } - - await sleep(this.waitBetweenRuns, abortSignal); - } - } - - private async runOnce() { - const dbNull = this.knex.raw('null'); - const configClient = this.knex.client.config.client; - - let tasks: Array<{ id: string }>; - if (configClient.includes('sqlite3') || configClient.includes('mysql')) { - tasks = await this.knex(DB_TASKS_TABLE) - .select('id') - .where('current_run_expires_at', '<', this.knex.fn.now()); - await this.knex(DB_TASKS_TABLE) - .whereIn( - 'id', - tasks.map(t => t.id), - ) - .update({ - current_run_ticket: dbNull, - current_run_started_at: dbNull, - current_run_expires_at: dbNull, - }); - } else { - tasks = await this.knex(DB_TASKS_TABLE) - .where('current_run_expires_at', '<', this.knex.fn.now()) - .update({ - current_run_ticket: dbNull, - current_run_started_at: dbNull, - current_run_expires_at: dbNull, - }) - .returning(['id']); - } - - // In rare cases, knex drivers may ignore "returning", and return the number - // of rows changed instead - if (typeof tasks === 'number') { - if (tasks > 0) { - this.logger.warn(`${tasks} tasks timed out and were lost`); - } - } else { - for (const { id } of tasks) { - this.logger.warn(`Task timed out and was lost: ${id}`); - } - } - } -} diff --git a/packages/backend-tasks/src/tasks/TaskScheduler.test.ts b/packages/backend-tasks/src/tasks/TaskScheduler.test.ts deleted file mode 100644 index 24ce73712f..0000000000 --- a/packages/backend-tasks/src/tasks/TaskScheduler.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 { DatabaseManager } from '@backstage/backend-common'; -import { - TestDatabaseId, - TestDatabases, - mockServices, -} from '@backstage/backend-test-utils'; -import { Duration } from 'luxon'; -import waitForExpect from 'wait-for-expect'; -import { TaskScheduler } from './TaskScheduler'; -import { createTestScopedSignal } from './__testUtils__/createTestScopedSignal'; - -jest.setTimeout(60_000); - -describe('TaskScheduler', () => { - const logger = mockServices.logger.mock(); - const databases = TestDatabases.create(); - const testScopedSignal = createTestScopedSignal(); - - async function createDatabase( - databaseId: TestDatabaseId, - ): Promise { - const knex = await databases.init(databaseId); - const databaseManager: Partial = { - forPlugin: () => ({ - getClient: async () => knex, - }), - }; - return databaseManager as DatabaseManager; - } - - it.each(databases.eachSupportedId())( - 'can return a working v1 plugin impl, %p', - async databaseId => { - const database = await createDatabase(databaseId); - const manager = new TaskScheduler(database, logger).forPlugin('test'); - const fn = jest.fn(); - - await manager.scheduleTask({ - id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromMillis(5000), - signal: testScopedSignal(), - fn, - }); - - await waitForExpect(() => { - expect(fn).toHaveBeenCalled(); - }); - }, - ); - - it.each(databases.eachSupportedId())( - 'can return a working v2 plugin impl, %p', - async databaseId => { - const database = await createDatabase(databaseId); - const manager = new TaskScheduler(database, logger).forPlugin('test'); - const fn = jest.fn(); - - await manager.scheduleTask({ - id: 'task2', - timeout: Duration.fromMillis(5000), - frequency: { cron: '* * * * * *' }, - signal: testScopedSignal(), - fn, - }); - - await waitForExpect(() => { - expect(fn).toHaveBeenCalled(); - }); - }, - ); -}); diff --git a/packages/backend-tasks/src/tasks/TaskScheduler.ts b/packages/backend-tasks/src/tasks/TaskScheduler.ts deleted file mode 100644 index ff6fd12017..0000000000 --- a/packages/backend-tasks/src/tasks/TaskScheduler.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 { - DatabaseManager, - getRootLogger, - LegacyRootDatabaseService, - PluginDatabaseManager, -} from '@backstage/backend-common'; -import { Config } from '@backstage/config'; -import { once } from 'lodash'; -import { Duration } from 'luxon'; -import { migrateBackendTasks } from '../database/migrateBackendTasks'; -import { PluginTaskSchedulerImpl } from './PluginTaskSchedulerImpl'; -import { PluginTaskSchedulerJanitor } from './PluginTaskSchedulerJanitor'; -import { PluginTaskScheduler } from './types'; -import { LoggerService } from '@backstage/backend-plugin-api'; - -/** - * Deals with the scheduling of distributed tasks. - * - * @public - * @deprecated Please migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from `@backstage/backend-defaults` - */ -export class TaskScheduler { - /** - * @deprecated - * It is only used by the legacy backend system, and should not be used in the new backend system. - */ - static fromConfig( - config: Config, - options?: { - databaseManager?: LegacyRootDatabaseService; - logger?: LoggerService; - }, - ): TaskScheduler { - const databaseManager = - options?.databaseManager ?? DatabaseManager.fromConfig(config); - const logger = (options?.logger || getRootLogger()).child({ - type: 'taskManager', - }); - return new TaskScheduler(databaseManager, logger); - } - - constructor( - private readonly databaseManager: LegacyRootDatabaseService, - private readonly logger: LoggerService, - ) {} - - /** - * Instantiates a task manager instance for the given plugin. - * - * @param pluginId - The unique ID of the plugin, for example "catalog" - * @returns A {@link PluginTaskScheduler} instance - * @deprecated Please migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from `@backstage/backend-defaults` - */ - forPlugin(pluginId: string): PluginTaskScheduler { - return TaskScheduler.forPlugin({ - pluginId, - databaseManager: this.databaseManager.forPlugin(pluginId), - logger: this.logger, - }); - } - - /** - * @deprecated Please migrate to the new backend system, and depend on `coreServices.scheduler` from `@backstage/backend-plugin-api` instead, or use `DefaultSchedulerService` from `@backstage/backend-defaults` - */ - static forPlugin(opts: { - pluginId: string; - databaseManager: PluginDatabaseManager; - logger: LoggerService; - }): PluginTaskScheduler { - const databaseFactory = once(async () => { - const knex = await opts.databaseManager.getClient(); - - if (!opts.databaseManager.migrations?.skip) { - await migrateBackendTasks(knex); - } - - if (process.env.NODE_ENV !== 'test') { - const janitor = new PluginTaskSchedulerJanitor({ - knex, - waitBetweenRuns: Duration.fromObject({ minutes: 1 }), - logger: opts.logger, - }); - janitor.start(); - } - - return knex; - }); - - return new PluginTaskSchedulerImpl(databaseFactory, opts.logger); - } -} diff --git a/packages/backend-tasks/src/tasks/TaskWorker.test.ts b/packages/backend-tasks/src/tasks/TaskWorker.test.ts deleted file mode 100644 index 829d7754f8..0000000000 --- a/packages/backend-tasks/src/tasks/TaskWorker.test.ts +++ /dev/null @@ -1,534 +0,0 @@ -/* - * 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 { TestDatabases, mockServices } from '@backstage/backend-test-utils'; -import { Duration, DateTime } from 'luxon'; -import waitForExpect from 'wait-for-expect'; -import { migrateBackendTasks } from '../database/migrateBackendTasks'; -import { DbTasksRow, DB_TASKS_TABLE } from '../database/tables'; -import { TaskWorker } from './TaskWorker'; -import { TaskSettingsV2 } from './types'; -import { createTestScopedSignal } from './__testUtils__/createTestScopedSignal'; - -jest.setTimeout(60_000); - -describe('TaskWorker', () => { - const logger = mockServices.logger.mock(); - const databases = TestDatabases.create(); - const testScopedSignal = createTestScopedSignal(); - - beforeEach(() => { - jest.resetAllMocks(); - }); - - it.each(databases.eachSupportedId())( - 'goes through the expected states, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn( - async () => new Promise(resolve => setTimeout(resolve, 50)), - ); - const settings: TaskSettingsV2 = { - version: 2, - cadence: '*/2 * * * * *', - initialDelayDuration: Duration.fromObject({ seconds: 1 }).toISO()!, - timeoutAfterDuration: Duration.fromObject({ minutes: 1 }).toISO()!, - }; - - const worker = new TaskWorker('task1', fn, knex, logger); - await worker.persistTask(settings); - - let row = (await knex(DB_TASKS_TABLE))[0]; - expect(row).toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: null, - current_run_started_at: null, - current_run_expires_at: null, - }), - ); - expect(JSON.parse(row.settings_json)).toEqual({ - version: 2, - cadence: '*/2 * * * * *', - initialDelayDuration: 'PT1S', - timeoutAfterDuration: 'PT1M', - }); - - await expect(worker.findReadyTask()).resolves.toEqual({ - result: 'not-ready-yet', - }); - - await waitForExpect(async () => { - await expect(worker.findReadyTask()).resolves.toEqual({ - result: 'ready', - settings, - }); - }); - - row = (await knex(DB_TASKS_TABLE))[0]; - expect(row).toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: null, - current_run_started_at: null, - current_run_expires_at: null, - }), - ); - - await expect(worker.tryClaimTask('ticket', settings)).resolves.toBe(true); - - row = (await knex(DB_TASKS_TABLE))[0]; - expect(row).toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: 'ticket', - current_run_started_at: expect.anything(), - current_run_expires_at: expect.anything(), - }), - ); - - await expect(worker.tryReleaseTask('ticket', settings)).resolves.toBe( - true, - ); - - row = (await knex(DB_TASKS_TABLE))[0]; - expect(row).toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: null, - current_run_started_at: null, - current_run_expires_at: null, - }), - ); - }, - ); - - it.each(databases.eachSupportedId())( - 'logs error when the task throws, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - jest.spyOn(logger, 'error'); - const fn = jest.fn().mockRejectedValue(new Error('failed')); - const settings: TaskSettingsV2 = { - version: 2, - initialDelayDuration: undefined, - cadence: '* * * * * *', - timeoutAfterDuration: Duration.fromMillis(60000).toISO()!, - }; - const checkFrequency = Duration.fromObject({ milliseconds: 100 }); - const worker = new TaskWorker('task1', fn, knex, logger, checkFrequency); - worker.start(settings, { signal: testScopedSignal() }); - - await waitForExpect(() => { - expect(logger.error).toHaveBeenCalled(); - }); - }, - ); - - it.each(databases.eachSupportedId())( - 'runs tasks more than once even when the task throws, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn().mockRejectedValue(new Error('failed')); - const settings: TaskSettingsV2 = { - version: 2, - initialDelayDuration: undefined, - cadence: '* * * * * *', - timeoutAfterDuration: Duration.fromMillis(60000).toISO()!, - }; - const checkFrequency = Duration.fromObject({ milliseconds: 100 }); - const worker = new TaskWorker('task1', fn, knex, logger, checkFrequency); - worker.start(settings, { signal: testScopedSignal() }); - - await waitForExpect(() => { - expect(fn).toHaveBeenCalledTimes(3); - }); - }, - ); - - it.each(databases.eachSupportedId())( - 'does not clobber ticket lock when stolen, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn( - async () => new Promise(resolve => setTimeout(resolve, 50)), - ); - const settings: TaskSettingsV2 = { - version: 2, - initialDelayDuration: undefined, - cadence: '* * * * * *', - timeoutAfterDuration: Duration.fromMillis(60000).toISO()!, - }; - - const worker = new TaskWorker('task1', fn, knex, logger); - await worker.persistTask(settings); - - await waitForExpect(async () => { - await expect(worker.findReadyTask()).resolves.toEqual({ - result: 'ready', - settings, - }); - }); - - await expect(worker.tryClaimTask('ticket', settings)).resolves.toBe(true); - - let row = (await knex(DB_TASKS_TABLE))[0]; - expect(row).toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: 'ticket', - current_run_started_at: expect.anything(), - current_run_expires_at: expect.anything(), - }), - ); - - await knex(DB_TASKS_TABLE) - .where('id', '=', 'task1') - .update({ current_run_ticket: 'stolen' }); - - await expect(worker.tryReleaseTask('ticket', settings)).resolves.toBe( - false, - ); - - row = (await knex(DB_TASKS_TABLE))[0]; - expect(row).toEqual( - expect.objectContaining({ - id: 'task1', - current_run_ticket: 'stolen', - current_run_started_at: expect.anything(), - current_run_expires_at: expect.anything(), - }), - ); - }, - ); - - it.each(databases.eachSupportedId())( - 'gracefully handles a disappeared task row, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn(async () => {}); - const settings: TaskSettingsV2 = { - version: 2, - initialDelayDuration: undefined, - cadence: '* * * * * *', - timeoutAfterDuration: Duration.fromMillis(60000).toISO()!, - }; - - const worker1 = new TaskWorker('task1', fn, knex, logger); - await worker1.persistTask(settings); - await knex(DB_TASKS_TABLE).where('id', '=', 'task1').delete(); - await expect(worker1.findReadyTask()).resolves.toEqual({ - result: 'abort', - }); - - const worker2 = new TaskWorker('task2', fn, knex, logger); - await worker2.persistTask(settings); - - await waitForExpect(async () => { - await expect(worker2.findReadyTask()).resolves.toEqual({ - result: 'ready', - settings, - }); - }); - - await knex(DB_TASKS_TABLE).where('id', '=', 'task2').delete(); - await expect(worker2.tryClaimTask('ticket', settings)).resolves.toBe( - false, - ); - - const worker3 = new TaskWorker('task3', fn, knex, logger); - await worker3.persistTask(settings); - - await waitForExpect(async () => { - await expect(worker3.findReadyTask()).resolves.toEqual({ - result: 'ready', - settings, - }); - }); - - await expect(worker3.tryClaimTask('ticket', settings)).resolves.toBe( - true, - ); - await knex(DB_TASKS_TABLE).where('id', '=', 'task3').delete(); - await expect(worker3.tryReleaseTask('ticket', settings)).resolves.toBe( - false, - ); - }, - ); - - it.each(databases.eachSupportedId())( - 'respects initialDelayDuration per worker, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const abortFirst = new AbortController(); - const settings: TaskSettingsV2 = { - version: 2, - initialDelayDuration: 'PT0.3S', - cadence: 'PT0.1S', - timeoutAfterDuration: 'PT10S', - }; - - // Start a single worker and make sure it waits and then goes to work - const fn1 = jest.fn(async () => {}); - const worker1 = new TaskWorker( - 'task1', - fn1, - knex, - logger, - Duration.fromMillis(10), - ); - await worker1.start(settings, { signal: abortFirst.signal }); - - expect(fn1).toHaveBeenCalledTimes(0); - await new Promise(resolve => setTimeout(resolve, 250)); - expect(fn1).toHaveBeenCalledTimes(0); - await new Promise(resolve => setTimeout(resolve, 100)); - expect(fn1.mock.calls.length).toBeGreaterThan(0); - - // Start a second worker and make sure it waits but the first worker still works along - const fn2 = jest.fn(); - const promise2 = new Promise(resolve => fn2.mockImplementation(resolve)); - const worker2 = new TaskWorker( - 'task1', - fn2, - knex, - logger, - Duration.fromMillis(10), - ); - await worker2.start(settings, { signal: testScopedSignal() }); - - // We eventually abort the first worker just to make sure that the second - // one for sure will get a go at running the task - setTimeout(() => abortFirst.abort(), 1000); - - const before = fn1.mock.calls.length; - await promise2; - expect(fn1.mock.calls.length).toBeGreaterThan(before); - - await knex.destroy(); - }, - ); - - it.each(databases.eachSupportedId())( - 'next_run_start_at is always the min between schedule changes from cron frequency, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn( - async () => new Promise(resolve => setTimeout(resolve, 50)), - ); - const settings: TaskSettingsV2 = { - version: 2, - cadence: '*/15 * * * *', - initialDelayDuration: 'PT2M', - timeoutAfterDuration: 'PT1M', - }; - - const worker = new TaskWorker('task99', fn, knex, logger); - await worker.persistTask(settings); - const row1 = (await knex(DB_TASKS_TABLE))[0]; - - const settings2 = { - ...settings, - cadence: '*/2 * * * *', - initialDelayDuration: 'PT1M', - }; - await worker.persistTask(settings2); - const row2 = (await knex(DB_TASKS_TABLE))[0]; - - expect(row2.next_run_start_at).not.toStrictEqual(row1.next_run_start_at); - - const settings3 = { ...settings }; - await worker.persistTask(settings3); - const row3 = (await knex(DB_TASKS_TABLE))[0]; - - // The new timestamp can basically be 0 or a minute depending on how the - // initialDelayDuration falls right on a cron boundary. This kinda - // contrived check removes a test flakiness based on wall clock time. - expect( - Math.abs( - +new Date(row3.next_run_start_at) - +new Date(row2.next_run_start_at), - ), - ).toBeLessThanOrEqual(60_000); - - await knex.destroy(); - }, - ); - - it.each(databases.eachSupportedId())( - 'next_run_start_at is always the min between schedule changes when using human duration frequency, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn( - async () => new Promise(resolve => setTimeout(resolve, 50)), - ); - - const initialSettings: TaskSettingsV2 = { - version: 2, - cadence: 'PT120M', - timeoutAfterDuration: 'PT1M', - }; - - const worker = new TaskWorker('task99', fn, knex, logger); - await worker.persistTask(initialSettings); - // replicate task running, sets next_run_start_at based on cadence - await worker.tryClaimTask('ticket', initialSettings); - await worker.tryReleaseTask('ticket', initialSettings); - - // grab initial row for comparisons later - const rowAfterClaimAndRelease = ( - await knex(DB_TASKS_TABLE) - )[0]; - - const settings: TaskSettingsV2 = { - ...initialSettings, - cadence: 'PT60M', - }; - await worker.persistTask(settings); - const row1 = (await knex(DB_TASKS_TABLE))[0]; - - const rowAfterClaimAndReleaseNextStartAt = DateTime.fromJSDate( - new Date(rowAfterClaimAndRelease.next_run_start_at), - ); - const row1NextStartAt = DateTime.fromJSDate( - new Date(row1.next_run_start_at), - ); - const now = DateTime.now(); - expect( - rowAfterClaimAndReleaseNextStartAt.diff(row1NextStartAt).as('minutes'), - ).toBeCloseTo(60, 1); // ensure that next start at is sooner than initial by one hour - expect(row1NextStartAt.diff(now).as('minutes')).toBeCloseTo(60, 1); // ensure that next start at is later than now by one hour - expect( - rowAfterClaimAndReleaseNextStartAt.diff(now).as('minutes'), - ).toBeCloseTo(120, 1); - - const settings2 = { - ...settings, - }; - await worker.persistTask(settings2); - const row2 = (await knex(DB_TASKS_TABLE))[0]; - - expect(row2.next_run_start_at).toStrictEqual(row1.next_run_start_at); - - await knex.destroy(); - }, - ); - - it.each(databases.eachSupportedId())( - 'next_run_start_at is always the min between schedule changes when using human duration frequency with initial start delay, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn( - async () => new Promise(resolve => setTimeout(resolve, 50)), - ); - - const initialSettings: TaskSettingsV2 = { - version: 2, - cadence: 'PT120M', - initialDelayDuration: 'PT2M', - timeoutAfterDuration: 'PT1M', - }; - - const worker = new TaskWorker('task99', fn, knex, logger); - await worker.persistTask(initialSettings); - // replicate task running, sets next_run_start_at based on cadence - await worker.tryClaimTask('ticket', initialSettings); - await worker.tryReleaseTask('ticket', initialSettings); - - // grab initial row for comparisons later - const rowAfterClaimAndRelease = ( - await knex(DB_TASKS_TABLE) - )[0]; - - const settings: TaskSettingsV2 = { - ...initialSettings, - cadence: 'PT60M', - }; - await worker.persistTask(settings); - const row1 = (await knex(DB_TASKS_TABLE))[0]; - - const rowAfterClaimAndReleaseNextStartAt = DateTime.fromJSDate( - new Date(rowAfterClaimAndRelease.next_run_start_at), - ); - const row1NextStartAt = DateTime.fromJSDate( - new Date(row1.next_run_start_at), - ); - const now = DateTime.now(); - expect( - rowAfterClaimAndReleaseNextStartAt.diff(row1NextStartAt).as('minutes'), - ).toBeCloseTo(62, 1); // ensure that next start at is sooner than initial by one hour, plus the 2 minute delay (set my tryReleaseTask) - expect(row1NextStartAt.diff(now).as('minutes')).toBeCloseTo(60, 1); // ensure that next start at is later than now by one hour (2 minute delay doesn't take effect here) - expect( - rowAfterClaimAndReleaseNextStartAt.diff(now).as('minutes'), - ).toBeCloseTo(122, 1); // includes 2 minute start delay (which is persisted from tryReleaseTask) - - const settings2 = { - ...settings, - }; - await worker.persistTask(settings2); - const row2 = (await knex(DB_TASKS_TABLE))[0]; - - expect(row2.next_run_start_at).toStrictEqual(row1.next_run_start_at); - - await knex.destroy(); - }, - ); - - it.each(databases.eachSupportedId())( - 'next_run_start_at is not set for manually-triggered tasks, %p', - async databaseId => { - const knex = await databases.init(databaseId); - await migrateBackendTasks(knex); - - const fn = jest.fn( - async () => new Promise(resolve => setTimeout(resolve, 50)), - ); - - const initialSettings: TaskSettingsV2 = { - version: 2, - cadence: 'manual', - timeoutAfterDuration: 'PT1M', - }; - - const worker = new TaskWorker('task99', fn, knex, logger); - await worker.persistTask(initialSettings); - await worker.tryClaimTask('ticket', initialSettings); - await worker.tryReleaseTask('ticket', initialSettings); - - const row = (await knex(DB_TASKS_TABLE))[0]; - expect(row.next_run_start_at).toBeNull(); - - await knex.destroy(); - }, - ); -}); diff --git a/packages/backend-tasks/src/tasks/TaskWorker.ts b/packages/backend-tasks/src/tasks/TaskWorker.ts deleted file mode 100644 index a50b988522..0000000000 --- a/packages/backend-tasks/src/tasks/TaskWorker.ts +++ /dev/null @@ -1,381 +0,0 @@ -/* - * 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 { ConflictError, NotFoundError } from '@backstage/errors'; -import { CronTime } from 'cron'; -import { Knex } from 'knex'; -import { DateTime, Duration } from 'luxon'; -import { v4 as uuid } from 'uuid'; -import { DB_TASKS_TABLE, DbTasksRow } from '../database/tables'; -import { TaskFunction, TaskSettingsV2, taskSettingsV2Schema } from './types'; -import { delegateAbortController, nowPlus, sleep } from './util'; -import { LoggerService } from '@backstage/backend-plugin-api'; - -const DEFAULT_WORK_CHECK_FREQUENCY = Duration.fromObject({ seconds: 5 }); - -/** - * Implements tasks that run across worker hosts, with collaborative locking. - * - * @private - */ -export class TaskWorker { - constructor( - private readonly taskId: string, - private readonly fn: TaskFunction, - private readonly knex: Knex, - private readonly logger: LoggerService, - private readonly workCheckFrequency: Duration = DEFAULT_WORK_CHECK_FREQUENCY, - ) {} - - async start(settings: TaskSettingsV2, options?: { signal?: AbortSignal }) { - try { - await this.persistTask(settings); - } catch (e) { - throw new Error(`Failed to persist task, ${e}`); - } - - this.logger.info( - `Task worker starting: ${this.taskId}, ${JSON.stringify(settings)}`, - ); - - let workCheckFrequency = this.workCheckFrequency; - const isDuration = settings?.cadence.startsWith('P'); - if (isDuration) { - const cadence = Duration.fromISO(settings.cadence); - if (cadence < workCheckFrequency) { - workCheckFrequency = cadence; - } - } - - let attemptNum = 1; - (async () => { - for (;;) { - try { - if (settings.initialDelayDuration) { - await sleep( - Duration.fromISO(settings.initialDelayDuration), - options?.signal, - ); - } - - while (!options?.signal?.aborted) { - const runResult = await this.runOnce(options?.signal); - - if (runResult.result === 'abort') { - break; - } - - await sleep(workCheckFrequency, options?.signal); - } - - this.logger.info(`Task worker finished: ${this.taskId}`); - attemptNum = 0; - break; - } catch (e) { - attemptNum += 1; - this.logger.warn( - `Task worker failed unexpectedly, attempt number ${attemptNum}, ${e}`, - ); - await sleep(Duration.fromObject({ seconds: 1 })); - } - } - })(); - } - - static async trigger(knex: Knex, taskId: string): Promise { - // check if task exists - const rows = await knex(DB_TASKS_TABLE) - .select(knex.raw(1)) - .where('id', '=', taskId); - if (rows.length !== 1) { - throw new NotFoundError(`Task ${taskId} does not exist`); - } - - const updatedRows = await knex(DB_TASKS_TABLE) - .where('id', '=', taskId) - .whereNull('current_run_ticket') - .update({ - next_run_start_at: knex.fn.now(), - }); - if (updatedRows < 1) { - throw new ConflictError(`Task ${taskId} is currently running`); - } - } - - /** - * Makes a single attempt at running the task to completion, if ready. - * - * @returns The outcome of the attempt - */ - private async runOnce( - signal?: AbortSignal, - ): Promise< - | { result: 'not-ready-yet' } - | { result: 'abort' } - | { result: 'failed' } - | { result: 'completed' } - > { - const findResult = await this.findReadyTask(); - if ( - findResult.result === 'not-ready-yet' || - findResult.result === 'abort' - ) { - return findResult; - } - - const taskSettings = findResult.settings; - const ticket = uuid(); - - const claimed = await this.tryClaimTask(ticket, taskSettings); - if (!claimed) { - return { result: 'not-ready-yet' }; - } - - // Abort the task execution either if the worker is stopped, or if the - // task timeout is hit - const taskAbortController = delegateAbortController(signal); - const timeoutHandle = setTimeout(() => { - taskAbortController.abort(); - }, Duration.fromISO(taskSettings.timeoutAfterDuration).as('milliseconds')); - - try { - await this.fn(taskAbortController.signal); - taskAbortController.abort(); // releases resources - } catch (e) { - this.logger.error(e); - await this.tryReleaseTask(ticket, taskSettings); - return { result: 'failed' }; - } finally { - clearTimeout(timeoutHandle); - } - - await this.tryReleaseTask(ticket, taskSettings); - return { result: 'completed' }; - } - - /** - * Perform the initial store of the task info - */ - async persistTask(settings: TaskSettingsV2) { - // Perform an initial parse to ensure that we will definitely be able to - // read it back again. - taskSettingsV2Schema.parse(settings); - - const isManual = settings?.cadence === 'manual'; - const isDuration = settings?.cadence.startsWith('P'); - const isCron = !isManual && !isDuration; - - let startAt: Knex.Raw | undefined; - let nextStartAt: Knex.Raw | undefined; - if (settings.initialDelayDuration) { - startAt = nowPlus( - Duration.fromISO(settings.initialDelayDuration), - this.knex, - ); - } - - if (isCron) { - const time = new CronTime(settings.cadence) - .sendAt() - .minus({ seconds: 1 }) // immediately, if "* * * * * *" - .toUTC(); - - nextStartAt = this.nextRunAtRaw(time); - startAt ||= nextStartAt; - } else if (isManual) { - nextStartAt = this.knex.raw('null'); - startAt ||= nextStartAt; - } else { - startAt ||= this.knex.fn.now(); - nextStartAt = nowPlus(Duration.fromISO(settings.cadence), this.knex); - } - - this.logger.debug(`task: ${this.taskId} configured to run at: ${startAt}`); - - // It's OK if the task already exists; if it does, just replace its - // settings with the new value and start the loop as usual. - const settingsJson = JSON.stringify(settings); - await this.knex(DB_TASKS_TABLE) - .insert({ - id: this.taskId, - settings_json: settingsJson, - next_run_start_at: startAt, - }) - .onConflict('id') - .merge( - this.knex.client.config.client.includes('mysql') - ? { - settings_json: settingsJson, - next_run_start_at: this.knex.raw( - `CASE WHEN ?? < ?? THEN ?? ELSE ?? END`, - [ - nextStartAt, - 'next_run_start_at', - nextStartAt, - 'next_run_start_at', - ], - ), - } - : { - settings_json: this.knex.ref('excluded.settings_json'), - next_run_start_at: this.knex.raw( - `CASE WHEN ?? < ?? THEN ?? ELSE ?? END`, - [ - nextStartAt, - `${DB_TASKS_TABLE}.next_run_start_at`, - nextStartAt, - `${DB_TASKS_TABLE}.next_run_start_at`, - ], - ), - }, - ); - } - - /** - * Check if the task is ready to run - */ - async findReadyTask(): Promise< - | { result: 'not-ready-yet' } - | { result: 'abort' } - | { result: 'ready'; settings: TaskSettingsV2 } - > { - const [row] = await this.knex(DB_TASKS_TABLE) - .where('id', '=', this.taskId) - .select({ - settingsJson: 'settings_json', - ready: this.knex.raw( - `CASE - WHEN next_run_start_at <= ? AND current_run_ticket IS NULL THEN TRUE - ELSE FALSE - END`, - [this.knex.fn.now()], - ), - }); - - if (!row) { - this.logger.info( - 'No longer able to find task; aborting and assuming that it has been unregistered or expired', - ); - return { result: 'abort' }; - } else if (!row.ready) { - return { result: 'not-ready-yet' }; - } - - try { - const obj = JSON.parse(row.settingsJson); - const settings = taskSettingsV2Schema.parse(obj); - return { result: 'ready', settings }; - } catch (e) { - this.logger.info( - `Task "${this.taskId}" is no longer able to parse task settings; aborting and assuming that a ` + - `newer version of the task has been issued and being handled by other workers, ${e}`, - ); - return { result: 'abort' }; - } - } - - /** - * Attempts to claim a task that's ready for execution, on this worker's - * behalf. We should not attempt to perform the work unless the claim really - * goes through. - * - * @param ticket - A globally unique string that changes for each invocation - * @param settings - The settings of the task to claim - * @returns True if it was successfully claimed - */ - async tryClaimTask( - ticket: string, - settings: TaskSettingsV2, - ): Promise { - const startedAt = this.knex.fn.now(); - const expiresAt = settings.timeoutAfterDuration - ? nowPlus(Duration.fromISO(settings.timeoutAfterDuration), this.knex) - : this.knex.raw('null'); - - const rows = await this.knex(DB_TASKS_TABLE) - .where('id', '=', this.taskId) - .whereNull('current_run_ticket') - .update({ - current_run_ticket: ticket, - current_run_started_at: startedAt, - current_run_expires_at: expiresAt, - }); - - return rows === 1; - } - - async tryReleaseTask( - ticket: string, - settings: TaskSettingsV2, - ): Promise { - const isManual = settings?.cadence === 'manual'; - const isDuration = settings?.cadence.startsWith('P'); - const isCron = !isManual && !isDuration; - - let nextRun: Knex.Raw; - if (isCron) { - const time = new CronTime(settings.cadence).sendAt().toUTC(); - this.logger.debug(`task: ${this.taskId} will next occur around ${time}`); - - nextRun = this.nextRunAtRaw(time); - } else if (isManual) { - nextRun = this.knex.raw('null'); - } else { - const dt = Duration.fromISO(settings.cadence).as('seconds'); - this.logger.debug( - `task: ${this.taskId} will next occur around ${DateTime.now().plus({ - seconds: dt, - })}`, - ); - - if (this.knex.client.config.client.includes('sqlite3')) { - nextRun = this.knex.raw( - `max(datetime(next_run_start_at, ?), datetime('now'))`, - [`+${dt} seconds`], - ); - } else if (this.knex.client.config.client.includes('mysql')) { - nextRun = this.knex.raw( - `greatest(next_run_start_at + interval ${dt} second, now())`, - ); - } else { - nextRun = this.knex.raw( - `greatest(next_run_start_at + interval '${dt} seconds', now())`, - ); - } - } - - const rows = await this.knex(DB_TASKS_TABLE) - .where('id', '=', this.taskId) - .where('current_run_ticket', '=', ticket) - .update({ - next_run_start_at: nextRun, - current_run_ticket: this.knex.raw('null'), - current_run_started_at: this.knex.raw('null'), - current_run_expires_at: this.knex.raw('null'), - }); - - return rows === 1; - } - - private nextRunAtRaw(time: DateTime): Knex.Raw { - if (this.knex.client.config.client.includes('sqlite3')) { - return this.knex.raw('datetime(?)', [time.toISO()]); - } else if (this.knex.client.config.client.includes('mysql')) { - return this.knex.raw(`?`, [time.toSQL({ includeOffset: false })]); - } - return this.knex.raw(`?`, [time.toISO()]); - } -} diff --git a/packages/backend-tasks/src/tasks/index.ts b/packages/backend-tasks/src/tasks/index.ts deleted file mode 100644 index 6f9ac6d859..0000000000 --- a/packages/backend-tasks/src/tasks/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { readTaskScheduleDefinitionFromConfig } from './readTaskScheduleDefinitionFromConfig'; -export { TaskScheduler } from './TaskScheduler'; -export type { - PluginTaskScheduler, - TaskFunction, - TaskDescriptor, - TaskInvocationDefinition, - TaskRunner, - TaskScheduleDefinition, - TaskScheduleDefinitionConfig, -} from './types'; diff --git a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts b/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts deleted file mode 100644 index adeb134611..0000000000 --- a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 { ConfigReader } from '@backstage/config'; -import { HumanDuration } from '@backstage/types'; -import { readTaskScheduleDefinitionFromConfig } from './readTaskScheduleDefinitionFromConfig'; - -describe('readTaskScheduleDefinitionFromConfig', () => { - it('all valid values', () => { - const config = new ConfigReader({ - frequency: { - cron: '0 30 * * * *', - }, - timeout: 'PT3M', - initialDelay: { - minutes: 20, - }, - scope: 'global', - }); - - const result = readTaskScheduleDefinitionFromConfig(config); - - expect((result.frequency as { cron: string }).cron).toBe('0 30 * * * *'); - expect(result.timeout).toEqual({ minutes: 3 }); - expect((result.initialDelay as HumanDuration).minutes).toEqual(20); - expect(result.scope).toBe('global'); - }); - - it('all valid required values', () => { - const config = new ConfigReader({ - frequency: { - cron: '0 30 * * * *', - }, - timeout: 'PT3M', - }); - - const result = readTaskScheduleDefinitionFromConfig(config); - - expect((result.frequency as { cron: string }).cron).toBe('0 30 * * * *'); - expect(result.timeout).toEqual({ minutes: 3 }); - expect(result.initialDelay).toBeUndefined(); - expect(result.scope).toBeUndefined(); - }); - - it('fail without required frequency', () => { - const config = new ConfigReader({ - timeout: 'PT3M', - }); - - expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( - "Missing required config value at 'frequency'", - ); - }); - - it('fail without required timeout', () => { - const config = new ConfigReader({ - frequency: 'PT30M', - }); - - expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( - "Missing required config value at 'timeout'", - ); - }); - - it('invalid frequency key', () => { - const config = new ConfigReader({ - frequency: { - invalid: 'value', - }, - timeout: 'PT3M', - }); - - expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( - "Failed to read duration from config at 'frequency', Error: Needs one or more of 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'", - ); - }); - - it('invalid frequency value', () => { - const config = new ConfigReader({ - frequency: { - minutes: 'value', - }, - timeout: 'PT3M', - }); - - expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( - "Failed to read duration from config, Error: Unable to convert config value for key 'frequency.minutes' in 'mock-config' to a number", - ); - }); - - it('frequency value with additional invalid prop', () => { - const config = new ConfigReader({ - frequency: { - minutes: 20, - invalid: 'value', - }, - timeout: 'PT3M', - }); - - expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( - "Failed to read duration from config at 'frequency', Error: Unknown property 'invalid'; expected one or more of 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'", - ); - }); - - it('invalid scope value', () => { - const config = new ConfigReader({ - frequency: { - years: 2, - }, - timeout: 'PT3M', - scope: 'invalid', - }); - - expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( - 'Only "global" or "local" are allowed for TaskScheduleDefinition.scope, but got: invalid', - ); - }); -}); diff --git a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts b/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts deleted file mode 100644 index 85099d82db..0000000000 --- a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 { Config, readDurationFromConfig } from '@backstage/config'; -import { HumanDuration } from '@backstage/types'; -import { TaskScheduleDefinition } from './types'; -import { Duration } from 'luxon'; - -function readDuration(config: Config, key: string): HumanDuration { - if (typeof config.get(key) === 'string') { - const value = config.getString(key); - const duration = Duration.fromISO(value); - if (!duration.isValid) { - throw new Error(`Invalid duration: ${value}`); - } - return duration.toObject(); - } - - return readDurationFromConfig(config, { key }); -} - -function readFrequency( - config: Config, - key: string, -): { cron: string } | Duration | HumanDuration | { trigger: 'manual' } { - const value = config.get(key); - if (typeof value === 'object' && (value as { cron?: string }).cron) { - return value as { cron: string }; - } - if ( - typeof value === 'object' && - (value as { trigger?: string }).trigger === 'manual' - ) { - return { trigger: 'manual' }; - } - - return readDuration(config, key); -} - -/** - * Reads a TaskScheduleDefinition from a Config. - * Expects the config not to be the root config, - * but the config for the definition. - * - * @param config - config for a TaskScheduleDefinition. - * @public - * @deprecated Please import `readSchedulerServiceTaskScheduleDefinitionFromConfig` from `@backstage/backend-plugin-api` instead - */ -export function readTaskScheduleDefinitionFromConfig( - config: Config, -): TaskScheduleDefinition { - const frequency = readFrequency(config, 'frequency'); - const timeout = readDuration(config, 'timeout'); - - const initialDelay = config.has('initialDelay') - ? readDuration(config, 'initialDelay') - : undefined; - - const scope = config.getOptionalString('scope'); - if (scope && !['global', 'local'].includes(scope)) { - throw new Error( - `Only "global" or "local" are allowed for TaskScheduleDefinition.scope, but got: ${scope}`, - ); - } - - return { - frequency, - timeout, - initialDelay, - scope: scope as 'global' | 'local' | undefined, - }; -} diff --git a/packages/backend-tasks/src/tasks/types.ts b/packages/backend-tasks/src/tasks/types.ts deleted file mode 100644 index a0bdbbaa64..0000000000 --- a/packages/backend-tasks/src/tasks/types.ts +++ /dev/null @@ -1,437 +0,0 @@ -/* - * 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 { HumanDuration, JsonObject } from '@backstage/types'; -import { CronTime } from 'cron'; -import { Duration } from 'luxon'; -import { z } from 'zod'; - -/** - * A function that can be called as a scheduled task. - * - * It may optionally accept an abort signal argument. When the signal triggers, - * processing should abort and return as quickly as possible. - * - * @public - * @deprecated Please import `SchedulerServiceTaskFunction` from `@backstage/backend-plugin-api` instead - */ -export type TaskFunction = - | ((abortSignal: AbortSignal) => void | Promise) - | (() => void | Promise); - -/** - * A semi-opaque type to describe an actively scheduled task. - * - * @public - * @deprecated Please import `SchedulerServiceTaskDescriptor` from `@backstage/backend-plugin-api` instead - */ -export type TaskDescriptor = { - /** - * The unique identifier of the task. - */ - id: string; - /** - * The scope of the task. - */ - scope: 'global' | 'local'; - /** - * The settings that control the task flow. This is a semi-opaque structure - * that is mainly there for debugging purposes. Do not make any assumptions - * about the contents of this field. - */ - settings: { version: number } & JsonObject; -}; - -/** - * Options that control the scheduling of a task. - * - * @public - * @deprecated Please import `SchedulerServiceTaskScheduleDefinition` from `@backstage/backend-plugin-api` instead - */ -export interface TaskScheduleDefinition { - /** - * How often you want the task to run. The system does its best to avoid - * overlapping invocations. - * - * @remarks - * - * This is the best effort value; under some circumstances there can be - * deviations. For example, if the task runtime is longer than the frequency - * and the timeout has not been given or not been exceeded yet, the next - * invocation of this task will be delayed until after the previous one - * finishes. - * - * This is a required field. - */ - frequency: - | { - /** - * A crontab style string. - * - * @remarks - * - * Overview: - * - * ``` - * ┌────────────── second (optional) - * │ ┌──────────── minute - * │ │ ┌────────── hour - * │ │ │ ┌──────── day of month - * │ │ │ │ ┌────── month - * │ │ │ │ │ ┌──── day of week - * │ │ │ │ │ │ - * │ │ │ │ │ │ - * * * * * * * - * ``` - */ - cron: string; - } - | Duration - | HumanDuration - /** - * This task will only run when manually triggered with the `triggerTask` method; no automatic - * scheduling. This is useful for locking of global tasks that should not be run concurrently. - */ - | { trigger: 'manual' }; - - /** - * The maximum amount of time that a single task invocation can take, before - * it's considered timed out and gets "released" such that a new invocation - * is permitted to take place (possibly, then, on a different worker). - */ - timeout: Duration | HumanDuration; - - /** - * The amount of time that should pass before the first invocation happens. - * - * @remarks - * - * This can be useful in cold start scenarios to stagger or delay some heavy - * compute jobs. If no value is given for this field then the first invocation - * will happen as soon as possible according to the cadence. - * - * NOTE: This is a per-worker delay. If you have a cluster of workers all - * collaborating on a task that has its `scope` field set to `'global'`, then - * you may still see the task being processed by other long-lived workers, - * while any given single worker is in its initial sleep delay time e.g. after - * a deployment. Therefore, this parameter is not useful for "globally" pausing - * work; its main intended use is for individual machines to get a chance to - * reach some equilibrium at startup before triggering heavy batch workloads. - */ - initialDelay?: Duration | HumanDuration; - - /** - * Sets the scope of concurrency control / locking to apply for invocations of - * this task. - * - * @remarks - * - * When the scope is set to the default value `'global'`, the scheduler will - * attempt to ensure that only one worker machine runs the task at a time, - * according to the given cadence. This means that as the number of worker - * hosts increases, the invocation frequency of this task will not go up. - * Instead, the load is spread randomly across hosts. This setting is useful - * for tasks that access shared resources, for example catalog ingestion tasks - * where you do not want many machines to repeatedly import the same data and - * trample over each other. - * - * When the scope is set to `'local'`, there is no concurrency control across - * hosts. Each host runs the task according to the given cadence similarly to - * `setInterval`, but the runtime ensures that there are no overlapping runs. - * - * @defaultValue 'global' - */ - scope?: 'global' | 'local'; -} - -/** - * Config options for {@link TaskScheduleDefinition} - * that control the scheduling of a task. - * - * @public - * @deprecated Please import `SchedulerServiceTaskScheduleDefinitionConfig` from `@backstage/backend-plugin-api` instead - */ -export interface TaskScheduleDefinitionConfig { - /** - * How often you want the task to run. The system does its best to avoid - * overlapping invocations. - * - * @remarks - * - * This is the best effort value; under some circumstances there can be - * deviations. For example, if the task runtime is longer than the frequency - * and the timeout has not been given or not been exceeded yet, the next - * invocation of this task will be delayed until after the previous one - * finishes. - * - * This is a required field. - */ - frequency: - | { - /** - * A crontab style string. - * - * @remarks - * - * Overview: - * - * ``` - * ┌────────────── second (optional) - * │ ┌──────────── minute - * │ │ ┌────────── hour - * │ │ │ ┌──────── day of month - * │ │ │ │ ┌────── month - * │ │ │ │ │ ┌──── day of week - * │ │ │ │ │ │ - * │ │ │ │ │ │ - * * * * * * * - * ``` - */ - cron: string; - } - | string - | HumanDuration; - - /** - * The maximum amount of time that a single task invocation can take, before - * it's considered timed out and gets "released" such that a new invocation - * is permitted to take place (possibly, then, on a different worker). - */ - timeout: string | HumanDuration; - - /** - * The amount of time that should pass before the first invocation happens. - * - * @remarks - * - * This can be useful in cold start scenarios to stagger or delay some heavy - * compute jobs. If no value is given for this field then the first invocation - * will happen as soon as possible according to the cadence. - * - * NOTE: This is a per-worker delay. If you have a cluster of workers all - * collaborating on a task that has its `scope` field set to `'global'`, then - * you may still see the task being processed by other long-lived workers, - * while any given single worker is in its initial sleep delay time e.g. after - * a deployment. Therefore, this parameter is not useful for "globally" pausing - * work; its main intended use is for individual machines to get a chance to - * reach some equilibrium at startup before triggering heavy batch workloads. - */ - initialDelay?: string | HumanDuration; - - /** - * Sets the scope of concurrency control / locking to apply for invocations of - * this task. - * - * @remarks - * - * When the scope is set to the default value `'global'`, the scheduler will - * attempt to ensure that only one worker machine runs the task at a time, - * according to the given cadence. This means that as the number of worker - * hosts increases, the invocation frequency of this task will not go up. - * Instead, the load is spread randomly across hosts. This setting is useful - * for tasks that access shared resources, for example catalog ingestion tasks - * where you do not want many machines to repeatedly import the same data and - * trample over each other. - * - * When the scope is set to `'local'`, there is no concurrency control across - * hosts. Each host runs the task according to the given cadence similarly to - * `setInterval`, but the runtime ensures that there are no overlapping runs. - * - * @defaultValue 'global' - */ - scope?: 'global' | 'local'; -} - -/** - * Options that apply to the invocation of a given task. - * - * @public - * @deprecated Please import `SchedulerServiceTaskInvocationDefinition` from `@backstage/backend-plugin-api` instead - */ -export interface TaskInvocationDefinition { - /** - * A unique ID (within the scope of the plugin) for the task. - */ - id: string; - - /** - * The actual task function to be invoked regularly. - */ - fn: TaskFunction; - - /** - * An abort signal that, when triggered, will stop the recurring execution of - * the task. - */ - signal?: AbortSignal; -} - -/** - * A previously prepared task schedule, ready to be invoked. - * - * @public - * @deprecated Please import `SchedulerServiceTaskRunner` from `@backstage/backend-plugin-api` instead - */ -export interface TaskRunner { - /** - * Takes the schedule and executes an actual task using it. - * - * @param task - The actual runtime properties of the task - */ - run(task: TaskInvocationDefinition): Promise; -} - -/** - * Deals with the scheduling of distributed tasks, for a given plugin. - * - * @public - * @deprecated Please use `SchedulerService` from `@backstage/backend-plugin-api` instead (most likely via `coreServices.scheduler`) - */ -export interface PluginTaskScheduler { - /** - * Manually triggers a task by ID. - * - * If the task doesn't exist, a NotFoundError is thrown. If the task is - * currently running, a ConflictError is thrown. - * - * @param id - The task ID - */ - triggerTask(id: string): Promise; - - /** - * Schedules a task function for recurring runs. - * - * @remarks - * - * The `scope` task field controls whether to use coordinated exclusive - * invocation across workers, or to just coordinate within the current worker. - * - * This convenience method performs both the scheduling and invocation in one - * go. - * - * @param task - The task definition - */ - scheduleTask( - task: TaskScheduleDefinition & TaskInvocationDefinition, - ): Promise; - - /** - * Creates a scheduled but dormant recurring task, ready to be launched at a - * later time. - * - * @remarks - * - * This method is useful for pre-creating a schedule in outer code to be - * passed into an inner implementation, such that the outer code controls - * scheduling while inner code controls implementation. - * - * @param schedule - The task schedule - */ - createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner; - - /** - * Returns all scheduled tasks registered to this scheduler. - * - * @remarks - * - * This method is useful for triggering tasks manually using the triggerTask - * functionality. Note that the returned tasks contain only tasks that have - * been initialized in this instance of the scheduler. - * - * @returns Scheduled tasks - */ - getScheduledTasks(): Promise; -} - -function isValidOptionalDurationString(d: string | undefined): boolean { - try { - return !d || Duration.fromISO(d).isValid; - } catch { - return false; - } -} - -function isValidCronFormat(c: string | undefined): boolean { - try { - if (!c) { - return false; - } - // parse cron format to ensure it's a valid format. - // eslint-disable-next-line no-new - new CronTime(c); - return true; - } catch { - return false; - } -} - -function isValidTrigger(t: string): boolean { - return t === 'manual'; -} - -export const taskSettingsV1Schema = z.object({ - version: z.literal(1), - initialDelayDuration: z - .string() - .optional() - .refine(isValidOptionalDurationString, { - message: 'Invalid duration, expecting ISO Period', - }), - recurringAtMostEveryDuration: z - .string() - .refine(isValidOptionalDurationString, { - message: 'Invalid duration, expecting ISO Period', - }), - timeoutAfterDuration: z.string().refine(isValidOptionalDurationString, { - message: 'Invalid duration, expecting ISO Period', - }), -}); - -/** - * The properties that control a scheduled task (version 1). - */ -export type TaskSettingsV1 = z.infer; - -export const taskSettingsV2Schema = z.object({ - version: z.literal(2), - cadence: z - .string() - .refine(isValidCronFormat, { message: 'Invalid cron' }) - .or( - z.string().refine(isValidTrigger, { - message: "Invalid trigger, expecting 'manual'", - }), - ) - .or( - z.string().refine(isValidOptionalDurationString, { - message: 'Invalid duration, expecting ISO Period', - }), - ), - timeoutAfterDuration: z.string().refine(isValidOptionalDurationString, { - message: 'Invalid duration, expecting ISO Period', - }), - initialDelayDuration: z - .string() - .optional() - .refine(isValidOptionalDurationString, { - message: 'Invalid duration, expecting ISO Period', - }), -}); - -/** - * The properties that control a scheduled task (version 2). - */ -export type TaskSettingsV2 = z.infer; diff --git a/packages/backend-tasks/src/tasks/util.test.ts b/packages/backend-tasks/src/tasks/util.test.ts deleted file mode 100644 index e2536abb88..0000000000 --- a/packages/backend-tasks/src/tasks/util.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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 knexFactory, { Knex } from 'knex'; -import { Duration } from 'luxon'; -import { delegateAbortController, nowPlus, sleep, validateId } from './util'; - -class KnexBuilder { - public build(client: string): Knex { - return knexFactory({ client, useNullAsDefault: true }); - } -} - -describe('util', () => { - describe('validateId', () => { - it.each(['a', 'a_b', 'ab123c_2', 'a!', 'A', 'a-b', 'a.b', '_a', 'a_'])( - 'accepts valid inputs, %p', - async input => { - expect(validateId(input)).toBeUndefined(); - }, - ); - - it.each(['', null, Symbol('a')])( - 'rejects invalid inputs, %p', - async input => { - expect(() => validateId(input as any)).toThrow(); - }, - ); - }); - - describe('sleep', () => { - it('finishes the wait as expected with no signal', async () => { - const ac = new AbortController(); - const start = Date.now(); - await sleep(Duration.fromObject({ seconds: 1 }), ac.signal); - expect(Date.now() - start).toBeGreaterThan(800); - }, 5_000); - - it('aborts properly on the signal', async () => { - const ac = new AbortController(); - const promise = sleep(Duration.fromObject({ seconds: 10 }), ac.signal); - ac.abort(); - await promise; - expect(true).toBe(true); - }, 1_000); - }); - - describe('delegateAbortController', () => { - it('inherits parent abort state', () => { - const parent = new AbortController(); - const child = delegateAbortController(parent.signal); - expect(parent.signal.aborted).toBe(false); - expect(child.signal.aborted).toBe(false); - parent.abort(); - expect(parent.signal.aborted).toBe(true); - expect(child.signal.aborted).toBe(true); - }); - - it('does not inherit from child to parent', () => { - const parent = new AbortController(); - const child = delegateAbortController(parent.signal); - expect(parent.signal.aborted).toBe(false); - expect(child.signal.aborted).toBe(false); - child.abort(); - expect(parent.signal.aborted).toBe(false); - expect(child.signal.aborted).toBe(true); - }); - }); - - describe('nowPlus', () => { - describe('without duration', () => { - const databases = [ - { client: 'sqlite3', expected: 'CURRENT_TIMESTAMP' }, - { client: 'mysql2', expected: 'CURRENT_TIMESTAMP' }, - { client: 'pg', expected: 'CURRENT_TIMESTAMP' }, - ]; - - it.each(databases)('for client $client', ({ client, expected }) => { - const knex = new KnexBuilder().build(client); - const result = nowPlus(undefined, knex); - - expect(result.toString()).toBe(expected); - }); - }); - describe('With duration', () => { - const databases = [ - { client: 'sqlite3', expected: "datetime('now', '20 seconds')" }, - { client: 'mysql2', expected: 'now() + interval 20 second' }, - { client: 'pg', expected: "now() + interval '20 seconds'" }, - ]; - it.each(databases)('for client $client', ({ client, expected }) => { - const duration = Duration.fromObject({ seconds: 20 }); - const knex = new KnexBuilder().build(client); - const result = nowPlus(duration, knex); - - expect(result.toString()).toBe(expected); - }); - }); - }); -}); diff --git a/packages/backend-tasks/src/tasks/util.ts b/packages/backend-tasks/src/tasks/util.ts deleted file mode 100644 index 70d67a9fbe..0000000000 --- a/packages/backend-tasks/src/tasks/util.ts +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { InputError } from '@backstage/errors'; -import { Knex } from 'knex'; -import { DateTime, Duration } from 'luxon'; - -// Keep the IDs compatible with e.g. Prometheus labels -export function validateId(id: string) { - if (typeof id !== 'string' || !id.trim()) { - throw new InputError(`${id} is not a valid ID, expected non-empty string`); - } -} - -export function dbTime(t: Date | string): DateTime { - if (typeof t === 'string') { - return DateTime.fromSQL(t); - } - return DateTime.fromJSDate(t); -} - -export function nowPlus(duration: Duration | undefined, knex: Knex) { - const seconds = duration?.as('seconds') ?? 0; - if (!seconds) { - return knex.fn.now(); - } - - if (knex.client.config.client.includes('sqlite3')) { - return knex.raw(`datetime('now', ?)`, [`${seconds} seconds`]); - } - - if (knex.client.config.client.includes('mysql')) { - return knex.raw(`now() + interval ${seconds} second`); - } - - return knex.raw(`now() + interval '${seconds} seconds'`); -} - -/** - * Sleep for the given duration, but return sooner if the abort signal - * triggers. - * - * @param duration - The amount of time to sleep, at most - * @param abortSignal - An optional abort signal that short circuits the wait - */ -export async function sleep( - duration: Duration, - abortSignal?: AbortSignal, -): Promise { - if (abortSignal?.aborted) { - return; - } - - await new Promise(resolve => { - let timeoutHandle: NodeJS.Timeout | undefined = undefined; - - const done = () => { - if (timeoutHandle) { - clearTimeout(timeoutHandle); - } - abortSignal?.removeEventListener('abort', done); - resolve(); - }; - - timeoutHandle = setTimeout(done, duration.as('milliseconds')); - abortSignal?.addEventListener('abort', done); - }); -} - -/** - * Creates a new AbortController that, in addition to working as a regular - * standalone controller, also gets aborted if the given parent signal - * reaches aborted state. - * - * @param parent - The "parent" signal that can trigger the delegate - */ -export function delegateAbortController(parent?: AbortSignal): AbortController { - const delegate = new AbortController(); - - if (parent) { - if (parent.aborted) { - delegate.abort(); - } else { - const onParentAborted = () => { - delegate.abort(); - }; - - const onChildAborted = () => { - parent.removeEventListener('abort', onParentAborted); - }; - - parent.addEventListener('abort', onParentAborted, { once: true }); - delegate.signal.addEventListener('abort', onChildAborted, { once: true }); - } - } - - return delegate; -} diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 59542fb241..49a2c44ddf 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,13 +1,37 @@ # @backstage/backend-test-utils +## 0.6.0-next.0 + +### Minor Changes + +- 19ff127: **BREAKING**: Removed service mocks for the identity and token manager services, which have been removed from `@backstage/backend-plugin-api`. +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 0363bf1: There is a new `mockErrorHandler` utility to help in mocking the error middleware in tests. +- Updated dependencies + - @backstage/backend-app-api@0.10.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.5.0 ### Minor Changes - 861f162: **BREAKING**: Removed these deprecated helpers: - - `setupRequestMockHandlers` Use `CreateMockDirectoryOptions` instead. - - `CreateMockDirectoryOptions` Use `registerMswTestHooks` instead. + - `setupRequestMockHandlers` is removed; use `registerMswTestHooks` instead. + - `MockDirectoryOptions` is removed; use `CreateMockDirectoryOptions` instead. Stopped exporting the deprecated and internal `isDockerDisabledForTests` helper. diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index f2a925c0ff..2bea998596 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -3,8 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// /// /// +/// import { AuthService } from '@backstage/backend-plugin-api'; import { Backend } from '@backstage/backend-app-api'; @@ -18,29 +20,28 @@ import { BackstageUserPrincipal } from '@backstage/backend-plugin-api'; import { CacheService } from '@backstage/backend-plugin-api'; import { DatabaseService } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; +import { ErrorRequestHandler } from 'express'; import { EventsService } from '@backstage/plugin-events-node'; import { ExtendedHttpServer } from '@backstage/backend-defaults/rootHttpRouter'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { HttpAuthService } from '@backstage/backend-plugin-api'; import { HttpRouterService } from '@backstage/backend-plugin-api'; -import { IdentityService } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import Keyv from 'keyv'; import { Knex } from 'knex'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { ParamsDictionary } from 'express-serve-static-core'; +import { ParsedQs } from 'qs'; import { PermissionsService } from '@backstage/backend-plugin-api'; import { RootConfigService } from '@backstage/backend-plugin-api'; import { RootHealthService } from '@backstage/backend-plugin-api'; -import { RootHttpRouterFactoryOptions } from '@backstage/backend-defaults/rootHttpRouter'; import { RootHttpRouterService } from '@backstage/backend-plugin-api'; import { RootLifecycleService } from '@backstage/backend-plugin-api'; import { RootLoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; -import { TokenManagerService } from '@backstage/backend-plugin-api'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { UserInfoService } from '@backstage/backend-plugin-api'; @@ -140,6 +141,15 @@ export interface MockDirectoryContentOptions { shouldReadAsText?: boolean | ((path: string, buffer: Buffer) => boolean); } +// @public +export function mockErrorHandler(): ErrorRequestHandler< + ParamsDictionary, + any, + any, + ParsedQs, + Record +>; + // @public (undocumented) export namespace mockServices { // (undocumented) @@ -150,12 +160,7 @@ export namespace mockServices { // (undocumented) export namespace auth { const // (undocumented) - factory: ServiceFactoryCompat< - AuthService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -164,12 +169,7 @@ export namespace mockServices { // (undocumented) export namespace cache { const // (undocumented) - factory: ServiceFactoryCompat< - CacheService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -178,12 +178,7 @@ export namespace mockServices { // (undocumented) export namespace database { const // (undocumented) - factory: ServiceFactoryCompat< - DatabaseService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -194,12 +189,7 @@ export namespace mockServices { // (undocumented) export namespace discovery { const // (undocumented) - factory: ServiceFactoryCompat< - DiscoveryService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -208,12 +198,7 @@ export namespace mockServices { // (undocumented) export namespace events { const // (undocumented) - factory: ServiceFactoryCompat< - EventsService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -225,10 +210,9 @@ export namespace mockServices { }): HttpAuthService; // (undocumented) export namespace httpAuth { - const factory: ((options?: { + const factory: (options?: { defaultCredentials?: BackstageCredentials; - }) => ServiceFactory) & - ServiceFactory; + }) => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -237,42 +221,16 @@ export namespace mockServices { // (undocumented) export namespace httpRouter { const // (undocumented) - factory: ServiceFactoryCompat< - HttpRouterService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, ) => ServiceMock; } // (undocumented) - export function identity(): IdentityService; - // (undocumented) - export namespace identity { - const // (undocumented) - factory: ServiceFactoryCompat< - IdentityService, - 'plugin', - 'singleton', - undefined - >; - const // (undocumented) - mock: ( - partialImpl?: Partial | undefined, - ) => ServiceMock; - } - // (undocumented) export namespace lifecycle { const // (undocumented) - factory: ServiceFactoryCompat< - LifecycleService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -281,12 +239,7 @@ export namespace mockServices { // (undocumented) export namespace logger { const // (undocumented) - factory: ServiceFactoryCompat< - LoggerService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -295,12 +248,7 @@ export namespace mockServices { // (undocumented) export namespace permissions { const // (undocumented) - factory: ServiceFactoryCompat< - PermissionsService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -315,28 +263,18 @@ export namespace mockServices { data?: JsonObject; }; const // (undocumented) - factory: ServiceFactory< - RootConfigService, - 'root', - 'singleton' | 'multiton' - > & - (( - options?: Options | undefined, - ) => ServiceFactory< - RootConfigService, - 'root', - 'singleton' | 'multiton' - >); + factory: ( + options?: Options | undefined, + ) => ServiceFactory; + const // (undocumented) + mock: ( + partialImpl?: Partial | undefined, + ) => ServiceMock; } // (undocumented) export namespace rootHealth { const // (undocumented) - factory: ServiceFactoryCompat< - RootHealthService, - 'root', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -345,10 +283,7 @@ export namespace mockServices { // (undocumented) export namespace rootHttpRouter { const // (undocumented) - factory: (( - options?: RootHttpRouterFactoryOptions | undefined, - ) => ServiceFactory) & - ServiceFactory; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -357,12 +292,7 @@ export namespace mockServices { // (undocumented) export namespace rootLifecycle { const // (undocumented) - factory: ServiceFactoryCompat< - RootLifecycleService, - 'root', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -377,10 +307,9 @@ export namespace mockServices { level?: 'none' | 'error' | 'warn' | 'info' | 'debug'; }; const // (undocumented) - factory: ServiceFactory & - (( - options?: Options | undefined, - ) => ServiceFactory); + factory: ( + options?: Options | undefined, + ) => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -389,42 +318,16 @@ export namespace mockServices { // (undocumented) export namespace scheduler { const // (undocumented) - factory: ServiceFactoryCompat< - SchedulerService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, ) => ServiceMock; } // (undocumented) - export function tokenManager(): TokenManagerService; - // (undocumented) - export namespace tokenManager { - const // (undocumented) - factory: ServiceFactoryCompat< - TokenManagerService, - 'plugin', - 'singleton', - undefined - >; - const // (undocumented) - mock: ( - partialImpl?: Partial | undefined, - ) => ServiceMock; - } - // (undocumented) export namespace urlReader { const // (undocumented) - factory: ServiceFactoryCompat< - UrlReaderService, - 'plugin', - 'singleton', - undefined - >; + factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -435,12 +338,7 @@ export namespace mockServices { ): UserInfoService; // (undocumented) export namespace userInfo { - const factory: ServiceFactoryCompat< - UserInfoService, - 'plugin', - 'singleton', - undefined - >; + const factory: () => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 682ec3067e..680a7b9c2c 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "0.5.0", + "version": "0.6.0-next.0", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" @@ -55,7 +55,10 @@ "@backstage/types": "workspace:^", "@keyv/memcache": "^1.3.5", "@keyv/redis": "^2.5.3", + "@types/express": "^4.17.6", + "@types/express-serve-static-core": "^4.17.5", "@types/keyv": "^4.2.0", + "@types/qs": "^6.9.6", "better-sqlite3": "^11.0.0", "cookie": "^0.6.0", "express": "^4.17.1", diff --git a/packages/backend-test-utils/src/index.ts b/packages/backend-test-utils/src/index.ts index d1d80bf387..d8e0e2c4a6 100644 --- a/packages/backend-test-utils/src/index.ts +++ b/packages/backend-test-utils/src/index.ts @@ -25,3 +25,4 @@ export * from './database'; export * from './msw'; export * from './filesystem'; export * from './next'; +export { mockErrorHandler } from './util'; diff --git a/packages/backend-test-utils/src/next/services/MockIdentityService.ts b/packages/backend-test-utils/src/next/services/MockIdentityService.ts deleted file mode 100644 index ff424f79c2..0000000000 --- a/packages/backend-test-utils/src/next/services/MockIdentityService.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023 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 { IdentityService } from '@backstage/backend-plugin-api'; -import { - IdentityApiGetIdentityRequest, - BackstageIdentityResponse, -} from '@backstage/plugin-auth-node'; - -export class MockIdentityService implements IdentityService { - getIdentity( - _options: IdentityApiGetIdentityRequest, - ): Promise { - return Promise.resolve({ - token: 'mock-token', - identity: { - type: 'user', - userEntityRef: 'user:default/mock-user', - ownershipEntityRefs: [], - }, - }); - } -} diff --git a/packages/backend-tasks/src/tasks/__testUtils__/createTestScopedSignal.ts b/packages/backend-test-utils/src/next/services/mockServices.test.ts similarity index 50% rename from packages/backend-tasks/src/tasks/__testUtils__/createTestScopedSignal.ts rename to packages/backend-test-utils/src/next/services/mockServices.test.ts index 6a497ea266..80d6e4c6f1 100644 --- a/packages/backend-tasks/src/tasks/__testUtils__/createTestScopedSignal.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.test.ts @@ -14,15 +14,19 @@ * limitations under the License. */ -export function createTestScopedSignal(): () => AbortSignal { - let testAbortController = new AbortController(); +import { coreServices } from '@backstage/backend-plugin-api'; +import { mockServices } from './mockServices'; - beforeEach(() => { - testAbortController = new AbortController(); - }); - afterEach(() => { - testAbortController.abort(); - }); +describe('mockServices', () => { + const coreServiceKeys = Object.keys(coreServices).filter( + key => key !== 'pluginMetadata', + ) as Array; - return () => testAbortController.signal; -} + it.each(coreServiceKeys)('should have mock implementations for %s', key => { + expect(mockServices[key]).toBeDefined(); + expect(mockServices[key].mock).toEqual(expect.any(Function)); + expect(mockServices[key].mock()).toEqual(expect.any(Object)); + expect(mockServices[key].factory).toEqual(expect.any(Function)); + expect(mockServices[key].factory()).toEqual(expect.any(Object)); + }); +}); diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts index 8d36fec50e..8b0f196399 100644 --- a/packages/backend-test-utils/src/next/services/mockServices.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.ts @@ -35,12 +35,10 @@ import { BackstageUserInfo, DiscoveryService, HttpAuthService, - IdentityService, LoggerService, RootConfigService, ServiceFactory, ServiceRef, - TokenManagerService, UserInfoService, coreServices, createServiceFactory, @@ -53,7 +51,6 @@ import { import { JsonObject } from '@backstage/types'; import { MockAuthService } from './MockAuthService'; import { MockHttpAuthService } from './MockHttpAuthService'; -import { MockIdentityService } from './MockIdentityService'; import { MockRootLoggerService } from './MockRootLoggerService'; import { MockUserInfoService } from './MockUserInfoService'; import { mockCredentials } from './mockCredentials'; @@ -77,8 +74,7 @@ function simpleFactoryWithOptions< >( ref: ServiceRef, factory: (...options: TOptions) => TService, -): ServiceFactory & - ((...options: TOptions) => ServiceFactory) { +): (...options: TOptions) => ServiceFactory { const factoryWithOptions = (...options: TOptions) => createServiceFactory({ service: ref as ServiceRef, @@ -86,7 +82,7 @@ function simpleFactoryWithOptions< async factory() { return factory(...options); }, - })(); + }); return Object.assign( factoryWithOptions, factoryWithOptions(...([undefined] as unknown as TOptions)), @@ -126,7 +122,7 @@ function simpleMock( service: ref, deps: {}, factory: () => mock, - })(), + }), }) as ServiceMock; }; } @@ -145,6 +141,24 @@ export namespace mockServices { coreServices.rootConfig, rootConfig, ); + export const mock = simpleMock(coreServices.rootConfig, () => ({ + get: jest.fn(), + getBoolean: jest.fn(), + getConfig: jest.fn(), + getConfigArray: jest.fn(), + getNumber: jest.fn(), + getOptional: jest.fn(), + getOptionalBoolean: jest.fn(), + getOptionalConfig: jest.fn(), + getOptionalConfigArray: jest.fn(), + getOptionalNumber: jest.fn(), + getOptionalString: jest.fn(), + getOptionalStringArray: jest.fn(), + getString: jest.fn(), + getStringArray: jest.fn(), + has: jest.fn(), + keys: jest.fn(), + })); } export function rootLogger(options?: rootLogger.Options): LoggerService { @@ -168,44 +182,6 @@ export namespace mockServices { })); } - export function tokenManager(): TokenManagerService { - return { - async getToken(): Promise<{ token: string }> { - return { token: 'mock-token' }; - }, - async authenticate(token: string): Promise { - if (token !== 'mock-token') { - throw new Error('Invalid token'); - } - }, - }; - } - export namespace tokenManager { - export const factory = createServiceFactory({ - service: coreServices.tokenManager, - deps: {}, - factory: () => tokenManager(), - }); - export const mock = simpleMock(coreServices.tokenManager, () => ({ - authenticate: jest.fn(), - getToken: jest.fn(), - })); - } - - export function identity(): IdentityService { - return new MockIdentityService(); - } - export namespace identity { - export const factory = createServiceFactory({ - service: coreServices.identity, - deps: {}, - factory: () => identity(), - }); - export const mock = simpleMock(coreServices.identity, () => ({ - getIdentity: jest.fn(), - })); - } - export function auth(options?: { pluginId?: string; disableDefaultAuthPolicy?: boolean; @@ -216,24 +192,25 @@ export namespace mockServices { }); } export namespace auth { - export const factory = createServiceFactory({ - service: coreServices.auth, - deps: { - plugin: coreServices.pluginMetadata, - config: coreServices.rootConfig, - }, - factory({ plugin, config }) { - const disableDefaultAuthPolicy = Boolean( - config.getOptionalBoolean( - 'backend.auth.dangerouslyDisableDefaultAuthPolicy', - ), - ); - return new MockAuthService({ - pluginId: plugin.getId(), - disableDefaultAuthPolicy, - }); - }, - }); + export const factory = () => + createServiceFactory({ + service: coreServices.auth, + deps: { + plugin: coreServices.pluginMetadata, + config: coreServices.rootConfig, + }, + factory({ plugin, config }) { + const disableDefaultAuthPolicy = Boolean( + config.getOptionalBoolean( + 'backend.auth.dangerouslyDisableDefaultAuthPolicy', + ), + ); + return new MockAuthService({ + pluginId: plugin.getId(), + disableDefaultAuthPolicy, + }); + }, + }); export const mock = simpleMock(coreServices.auth, () => ({ authenticate: jest.fn(), getNoneCredentials: jest.fn(), @@ -257,7 +234,7 @@ export namespace mockServices { ); } export namespace discovery { - export const factory = discoveryServiceFactory; + export const factory = () => discoveryServiceFactory; export const mock = simpleMock(coreServices.discovery, () => ({ getBaseUrl: jest.fn(), getExternalBaseUrl: jest.fn(), @@ -288,7 +265,14 @@ export namespace mockServices { ); } export namespace httpAuth { - const factoryWithOptions = (options?: { + /** + * Creates a mock service factory for the `HttpAuthService`. + * + * By default all requests without credentials are treated as requests from + * the default mock user principal. This behavior can be configured with the + * `defaultCredentials` option. + */ + export const factory = (options?: { defaultCredentials?: BackstageCredentials; }) => createServiceFactory({ @@ -299,18 +283,7 @@ export namespace mockServices { plugin.getId(), options?.defaultCredentials ?? mockCredentials.user(), ), - })(); - /** - * Creates a mock service factory for the `HttpAuthService`. - * - * By default all requests without credentials are treated as requests from - * the default mock user principal. This behavior can be configured with the - * `defaultCredentials` option. - */ - export const factory = Object.assign( - factoryWithOptions, - factoryWithOptions(), - ); + }); export const mock = simpleMock(coreServices.httpAuth, () => ({ credentials: jest.fn(), issueUserCookie: jest.fn(), @@ -336,13 +309,14 @@ export namespace mockServices { * By default it extracts the user's entity ref from a user principal and * returns that as the only ownership entity ref. */ - export const factory = createServiceFactory({ - service: coreServices.userInfo, - deps: {}, - factory() { - return new MockUserInfoService(); - }, - }); + export const factory = () => + createServiceFactory({ + service: coreServices.userInfo, + deps: {}, + factory() { + return new MockUserInfoService(); + }, + }); export const mock = simpleMock(coreServices.userInfo, () => ({ getUserInfo: jest.fn(), })); @@ -352,7 +326,7 @@ export namespace mockServices { // some may need a bit more refactoring for it to be simpler to // re-implement functioning mock versions here. export namespace cache { - export const factory = cacheServiceFactory; + export const factory = () => cacheServiceFactory; export const mock = simpleMock(coreServices.cache, () => ({ delete: jest.fn(), get: jest.fn(), @@ -362,14 +336,14 @@ export namespace mockServices { } export namespace database { - export const factory = databaseServiceFactory; + export const factory = () => databaseServiceFactory; export const mock = simpleMock(coreServices.database, () => ({ getClient: jest.fn(), })); } export namespace rootHealth { - export const factory = rootHealthServiceFactory; + export const factory = () => rootHealthServiceFactory; export const mock = simpleMock(coreServices.rootHealth, () => ({ getLiveness: jest.fn(), getReadiness: jest.fn(), @@ -377,7 +351,7 @@ export namespace mockServices { } export namespace httpRouter { - export const factory = httpRouterServiceFactory; + export const factory = () => httpRouterServiceFactory; export const mock = simpleMock(coreServices.httpRouter, () => ({ use: jest.fn(), addAuthPolicy: jest.fn(), @@ -385,14 +359,14 @@ export namespace mockServices { } export namespace rootHttpRouter { - export const factory = rootHttpRouterServiceFactory; + export const factory = () => rootHttpRouterServiceFactory(); export const mock = simpleMock(coreServices.rootHttpRouter, () => ({ use: jest.fn(), })); } export namespace lifecycle { - export const factory = lifecycleServiceFactory; + export const factory = () => lifecycleServiceFactory; export const mock = simpleMock(coreServices.lifecycle, () => ({ addShutdownHook: jest.fn(), addStartupHook: jest.fn(), @@ -400,15 +374,14 @@ export namespace mockServices { } export namespace logger { - export const factory = loggerServiceFactory; - + export const factory = () => loggerServiceFactory; export const mock = simpleMock(coreServices.logger, () => createLoggerMock(), ); } export namespace permissions { - export const factory = permissionsServiceFactory; + export const factory = () => permissionsServiceFactory; export const mock = simpleMock(coreServices.permissions, () => ({ authorize: jest.fn(), authorizeConditional: jest.fn(), @@ -416,7 +389,7 @@ export namespace mockServices { } export namespace rootLifecycle { - export const factory = rootLifecycleServiceFactory; + export const factory = () => rootLifecycleServiceFactory; export const mock = simpleMock(coreServices.rootLifecycle, () => ({ addShutdownHook: jest.fn(), addStartupHook: jest.fn(), @@ -424,7 +397,7 @@ export namespace mockServices { } export namespace scheduler { - export const factory = schedulerServiceFactory; + export const factory = () => schedulerServiceFactory; export const mock = simpleMock(coreServices.scheduler, () => ({ createScheduledTaskRunner: jest.fn(), getScheduledTasks: jest.fn(), @@ -434,7 +407,7 @@ export namespace mockServices { } export namespace urlReader { - export const factory = urlReaderServiceFactory; + export const factory = () => urlReaderServiceFactory; export const mock = simpleMock(coreServices.urlReader, () => ({ readTree: jest.fn(), readUrl: jest.fn(), @@ -443,7 +416,7 @@ export namespace mockServices { } export namespace events { - export const factory = eventsServiceFactory; + export const factory = () => eventsServiceFactory; export const mock = simpleMock(eventsServiceRef, () => ({ publish: jest.fn(), subscribe: jest.fn(), diff --git a/packages/backend-test-utils/src/next/wiring/ServiceFactoryTester.test.ts b/packages/backend-test-utils/src/next/wiring/ServiceFactoryTester.test.ts index 5970f6ea52..6690f76e76 100644 --- a/packages/backend-test-utils/src/next/wiring/ServiceFactoryTester.test.ts +++ b/packages/backend-test-utils/src/next/wiring/ServiceFactoryTester.test.ts @@ -93,7 +93,7 @@ describe('ServiceFactoryTester', () => { deps: { root: rootServiceRef, plugin: pluginServiceRef }, factory: async ({ root, plugin }) => `${root}, ${plugin}`, }), - { dependencies: [rootFactory, pluginFactory()] }, + { dependencies: [rootFactory, pluginFactory] }, ); await expect(tester.getSubject('x')).resolves.toBe('root, x-plugin'); @@ -106,7 +106,7 @@ describe('ServiceFactoryTester', () => { deps: { shared: sharedPluginServiceRef, plugin: pluginServiceRef }, factory: async ({ shared, plugin }) => `${shared}, ${plugin}`, }), - { dependencies: [sharedPluginFactory(), pluginFactory] }, + { dependencies: [sharedPluginFactory, pluginFactory] }, ); await expect(tester.getSubject('x')).resolves.toBe('x-1-plugin, x-plugin'); diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index a01d4bebf5..7bf3887c93 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -71,34 +71,34 @@ describe('TestBackend', () => { features: [ // @ts-expect-error [extensionPoint1, { a: 'a' }], - createServiceFactory(() => ({ + createServiceFactory({ service: serviceRef, deps: {}, // @ts-expect-error factory: async () => ({ a: 'a' }), - })), - createServiceFactory(() => ({ + }), + createServiceFactory({ service: serviceRef, deps: {}, factory: async () => ({ a: 'a', b: 'b' }), - })), - createServiceFactory(() => ({ + }), + createServiceFactory({ service: serviceRef, deps: {}, // @ts-expect-error factory: async () => ({ c: 'c' }), - })), - createServiceFactory(() => ({ + }), + createServiceFactory({ service: serviceRef, deps: {}, // @ts-expect-error factory: async () => ({ a: 'a', c: 'c' }), - })), - createServiceFactory(() => ({ + }), + createServiceFactory({ service: serviceRef, deps: {}, factory: async () => ({ a: 'a', b: 'b', c: 'c' }), - })), + }), ], extensionPoints: [ // @ts-expect-error @@ -144,7 +144,7 @@ describe('TestBackend', () => { }); await startTestBackend({ - features: [testModule, sf()], + features: [testModule, sf], }); expect(testFn).toHaveBeenCalledWith('winning'); @@ -198,13 +198,12 @@ describe('TestBackend', () => { rootLifecycle: coreServices.rootLifecycle, rootLogger: coreServices.rootLogger, scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, urlReader: coreServices.urlReader, auth: coreServices.auth, httpAuth: coreServices.httpAuth, }, async init(deps) { - expect(Object.keys(deps)).toHaveLength(17); + expect(Object.keys(deps)).toHaveLength(16); expect(Object.values(deps)).not.toContain(undefined); }, }); diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.ts index 6b4110d4de..96bf0f7dc1 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.ts @@ -72,7 +72,6 @@ export const defaultServiceFactories = [ mockServices.database.factory(), mockServices.httpAuth.factory(), mockServices.httpRouter.factory(), - mockServices.identity.factory(), mockServices.lifecycle.factory(), mockServices.logger.factory(), mockServices.permissions.factory(), @@ -80,7 +79,6 @@ export const defaultServiceFactories = [ mockServices.rootLifecycle.factory(), mockServices.rootLogger.factory(), mockServices.scheduler.factory(), - mockServices.tokenManager.factory(), mockServices.userInfo.factory(), mockServices.urlReader.factory(), mockServices.events.factory(), diff --git a/packages/backend-tasks/src/deprecated.ts b/packages/backend-test-utils/src/util/errorHandler.ts similarity index 59% rename from packages/backend-tasks/src/deprecated.ts rename to packages/backend-test-utils/src/util/errorHandler.ts index 629e5f6d4d..f534823f50 100644 --- a/packages/backend-tasks/src/deprecated.ts +++ b/packages/backend-test-utils/src/util/errorHandler.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Backstage Authors + * Copyright 2024 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. @@ -14,14 +14,16 @@ * limitations under the License. */ -import { HumanDuration as TypesHumanDuration } from '@backstage/types'; +import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter'; +import { mockServices } from '../next'; /** - * Human friendly durations object. - * + * A mock for error handler middleware that can be used in router tests. * @public - * @deprecated Import from `@backstage/types` instead */ -export type HumanDuration = TypesHumanDuration; - -export * from './tasks'; +export function mockErrorHandler() { + return MiddlewareFactory.create({ + config: mockServices.rootConfig(), + logger: mockServices.rootLogger(), + }).error(); +} diff --git a/packages/backend-test-utils/src/util/index.ts b/packages/backend-test-utils/src/util/index.ts index a6cdc621d4..621c36ba8f 100644 --- a/packages/backend-test-utils/src/util/index.ts +++ b/packages/backend-test-utils/src/util/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ +export { mockErrorHandler } from './errorHandler'; export { isDockerDisabledForTests } from './isDockerDisabledForTests'; diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 8c856f53ca..ccb3825cd2 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,41 @@ # example-backend +## 0.0.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-backend@1.10.13-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-search-backend@1.5.17-next.0 + - @backstage/plugin-kubernetes-backend@0.18.6-next.0 + - @backstage/plugin-scaffolder-backend@1.25.0-next.0 + - @backstage/plugin-app-backend@0.3.74-next.0 + - @backstage/plugin-signals-backend@0.2.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.0 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-permission-backend@0.5.49-next.0 + - @backstage/plugin-proxy-backend@0.5.6-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.0-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.0 + - @backstage/plugin-devtools-backend@0.4.0-next.0 + - @backstage/plugin-notifications-backend@0.4.0-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-permission-common@0.8.1 + ## 0.0.29 ### Patch Changes diff --git a/packages/backend/Dockerfile b/packages/backend/Dockerfile index e0e6ab8a31..b64a26afad 100644 --- a/packages/backend/Dockerfile +++ b/packages/backend/Dockerfile @@ -35,7 +35,7 @@ USER node # This switches many Node.js dependencies to production mode. -ENV NODE_ENV production +ENV NODE_ENV=production # Copy over Yarn 3 configuration, release, and plugins COPY --chown=node:node .yarn ./.yarn diff --git a/packages/backend/package.json b/packages/backend/package.json index 570a545741..4319728c04 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.29", + "version": "0.0.30-next.0", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -57,9 +57,10 @@ "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-signals-backend": "workspace:^", "@backstage/plugin-techdocs-backend": "workspace:^", - "@opentelemetry/auto-instrumentations-node": "^0.43.0", + "@opentelemetry/auto-instrumentations-node": "^0.49.0", "@opentelemetry/exporter-prometheus": "^0.50.0", - "@opentelemetry/sdk-node": "^0.50.0" + "@opentelemetry/sdk-node": "^0.50.0", + "example-app": "link:../app" }, "devDependencies": { "@backstage/cli": "workspace:^" diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 2440f043d7..2e7912b69a 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -18,7 +18,6 @@ import { CompoundEntityRef, Entity, parseEntityRef, - stringifyEntityRef, stringifyLocationRef, } from '@backstage/catalog-model'; import { ResponseError } from '@backstage/errors'; @@ -142,35 +141,7 @@ export class CatalogClient implements CatalogApi { options, ), ); - - // do not sort entities, if order is provided - if (encodedOrder.length) { - return { items: entities }; - } - - const refCompare = (a: Entity, b: Entity) => { - // in case field filtering is used, these fields might not be part of the response - if ( - a.metadata?.name === undefined || - a.kind === undefined || - b.metadata?.name === undefined || - b.kind === undefined - ) { - return 0; - } - - const aRef = stringifyEntityRef(a); - const bRef = stringifyEntityRef(b); - if (aRef < bRef) { - return -1; - } - if (aRef > bRef) { - return 1; - } - return 0; - }; - - return { items: entities.sort(refCompare) }; + return { items: entities }; } /** diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index ec823ef23e..c2a17a5b93 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/cli +## 0.27.1-next.0 + +### Patch Changes + +- 1b5c264: Add `checks: 'read'` for default GitHub app permissions +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/eslint-plugin@0.1.8 + - @backstage/integration@1.14.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + ## 0.27.0 ### Minor Changes diff --git a/packages/cli/config/eslint-factory.js b/packages/cli/config/eslint-factory.js index 96d8478ec0..856068eef9 100644 --- a/packages/cli/config/eslint-factory.js +++ b/packages/cli/config/eslint-factory.js @@ -277,7 +277,7 @@ function createConfigForRole(dir, role, extraConfig = {}) { restrictedSrcSyntax: [ { message: - "`__dirname` doesn't refer to the same dir in production builds, try `resolvePackagePath()` from `@backstage/backend-common` instead.", + "`__dirname` doesn't refer to the same dir in production builds, try `resolvePackagePath()` from `@backstage/backend-plugin-api` instead.", selector: 'Identifier[name="__dirname"]', }, ...(extraConfig.restrictedSrcSyntax ?? []), diff --git a/packages/cli/package.json b/packages/cli/package.json index c3f70bcbf4..89230c13b2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.27.0", + "version": "0.27.1-next.0", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" @@ -91,7 +91,7 @@ "css-loader": "^6.5.1", "ctrlc-windows": "^2.1.0", "diff": "^5.0.0", - "esbuild": "^0.21.0", + "esbuild": "^0.23.0", "esbuild-loader": "^4.0.0", "eslint": "^8.6.0", "eslint-config-prettier": "^9.0.0", diff --git a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts index 70b8c0ca53..b1825ad996 100644 --- a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts +++ b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts @@ -114,10 +114,16 @@ export class GithubCreateAppServer { default_events: ['create', 'delete', 'push', 'repository'], default_permissions: { metadata: 'read', - ...(this.permissions.includes('members') && { members: 'read' }), - ...(this.permissions.includes('read') && { contents: 'read' }), + ...(this.permissions.includes('members') && { + members: 'read', + }), + ...(this.permissions.includes('read') && { + contents: 'read', + checks: 'read', + }), ...(this.permissions.includes('write') && { contents: 'write', + checks: 'read', actions: 'write', }), }, diff --git a/packages/cli/src/commands/create-github-app/index.ts b/packages/cli/src/commands/create-github-app/index.ts index 6b3e732f18..681b471c91 100644 --- a/packages/cli/src/commands/create-github-app/index.ts +++ b/packages/cli/src/commands/create-github-app/index.ts @@ -22,6 +22,7 @@ import { paths } from '../../lib/paths'; import { GithubCreateAppServer } from './GithubCreateAppServer'; import fetch from 'node-fetch'; import openBrowser from 'react-dev-utils/openBrowser'; + // This is an experimental command that at this point does not support GitHub Enterprise // due to lacking support for creating apps from manifests. // https://docs.github.com/en/free-pro-team@latest/developers/apps/creating-a-github-app-from-a-manifest diff --git a/packages/cli/templates/default-backend-module/package.json.hbs b/packages/cli/templates/default-backend-module/package.json.hbs index db8b8749b3..7e82e047ce 100644 --- a/packages/cli/templates/default-backend-module/package.json.hbs +++ b/packages/cli/templates/default-backend-module/package.json.hbs @@ -29,7 +29,6 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "{{versionQuery '@backstage/backend-common'}}", "@backstage/backend-plugin-api": "{{versionQuery '@backstage/backend-plugin-api'}}" }, "devDependencies": { diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index b05d1a844b..6c25562c21 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -28,7 +28,6 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "{{versionQuery '@backstage/backend-common'}}", "@backstage/backend-defaults": "{{versionQuery '@backstage/backend-defaults'}}", "@backstage/backend-plugin-api": "{{versionQuery '@backstage/backend-plugin-api'}}", "express": "{{versionQuery 'express' '4.17.1'}}", diff --git a/packages/cli/templates/scaffolder-module/package.json.hbs b/packages/cli/templates/scaffolder-module/package.json.hbs index df4bf352f4..a6e6b5c5bc 100644 --- a/packages/cli/templates/scaffolder-module/package.json.hbs +++ b/packages/cli/templates/scaffolder-module/package.json.hbs @@ -32,7 +32,6 @@ "@backstage/plugin-scaffolder-node": "{{versionQuery '@backstage/plugin-scaffolder-node'}}" }, "devDependencies": { - "@backstage/backend-common": "{{versionQuery '@backstage/backend-common'}}", "@backstage/cli": "{{versionQuery '@backstage/cli'}}" }, "files": [ diff --git a/packages/cli/templates/scaffolder-module/src/actions/example/example.test.ts b/packages/cli/templates/scaffolder-module/src/actions/example/example.test.ts index 242f93c2f3..d0e5374f3f 100644 --- a/packages/cli/templates/scaffolder-module/src/actions/example/example.test.ts +++ b/packages/cli/templates/scaffolder-module/src/actions/example/example.test.ts @@ -1,6 +1,5 @@ import { PassThrough } from 'stream'; import { createAcmeExampleAction } from './example'; -import { getVoidLogger } from '@backstage/backend-common'; describe('acme:example', () => { afterEach(() => { @@ -10,15 +9,14 @@ describe('acme:example', () => { it('should call action', async () => { const action = createAcmeExampleAction(); - const logger = getVoidLogger(); - jest.spyOn(logger, 'info'); + const logger = { info: jest.fn() }; await action.handler({ input: { myParameter: 'test', }, workspacePath: '/tmp', - logger, + logger: logger as any, logStream: new PassThrough(), output: jest.fn(), createTemporaryDirectory() { diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 6bab6f3df8..d8c759c34e 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -46,7 +46,7 @@ "@backstage/cli-common": "workspace:^", "chalk": "^4.0.0", "commander": "^12.0.0", - "jscodeshift": "^0.15.0", + "jscodeshift": "^0.16.0", "jscodeshift-add-imports": "^1.0.10" }, "devDependencies": { diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 35849d453c..4e4047863c 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-compat-api +## 0.2.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + ## 0.2.8 ### Patch Changes diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index 75b91cc055..e61ee451d9 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -12,9 +12,10 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { BackstagePlugin as BackstagePlugin_2 } from '@backstage/frontend-plugin-api'; import { ComponentType } from 'react'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; -import { FrontendFeature } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -28,7 +29,7 @@ export function compatWrapper(element: ReactNode): React_2.JSX.Element; // @public (undocumented) export function convertLegacyApp( rootElement: React_2.JSX.Element, -): FrontendFeature[]; +): (FrontendPlugin | ExtensionOverrides)[]; // @public (undocumented) export function convertLegacyPageExtension( @@ -37,13 +38,13 @@ export function convertLegacyPageExtension( name?: string; defaultPath?: string; }, -): ExtensionDefinition; +): ExtensionDefinition; // @public (undocumented) export function convertLegacyPlugin( legacyPlugin: BackstagePlugin, options: { - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; }, ): BackstagePlugin_2; diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 5af7d1b960..7cb411855f 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.2.8", + "version": "0.2.9-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index f8b40a561c..4c2acd35a6 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -31,15 +31,15 @@ import { Navigate, Route, Routes } from 'react-router-dom'; import { collectLegacyRoutes } from './collectLegacyRoutes'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalBackstagePlugin } from '../../frontend-plugin-api/src/wiring/createFrontendPlugin'; +import { toInternalFrontendPlugin } from '../../frontend-plugin-api/src/wiring/createFrontendPlugin'; import { createPlugin, createRoutableExtension, createRouteRef, useApp, } from '@backstage/core-plugin-api'; -import { createSpecializedApp } from '@backstage/frontend-app-api'; -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/frontend-test-utils'; describe('collectLegacyRoutes', () => { it('should collect legacy routes', () => { @@ -55,7 +55,7 @@ describe('collectLegacyRoutes', () => { expect( collected.map(p => ({ id: p.id, - extensions: toInternalBackstagePlugin(p).extensions.map(e => ({ + extensions: toInternalFrontendPlugin(p).extensions.map(e => ({ id: e.id, attachTo: e.attachTo, disabled: e.disabled, @@ -74,7 +74,7 @@ describe('collectLegacyRoutes', () => { }, { id: 'api:plugin.scoringdata.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], @@ -90,7 +90,7 @@ describe('collectLegacyRoutes', () => { }, { id: 'api:plugin.stackstorm.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], @@ -112,7 +112,7 @@ describe('collectLegacyRoutes', () => { }, { id: 'api:plugin.puppetdb.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], @@ -158,7 +158,7 @@ describe('collectLegacyRoutes', () => { expect( collected.map(p => ({ id: p.id, - extensions: toInternalBackstagePlugin(p).extensions.map(e => ({ + extensions: toInternalFrontendPlugin(p).extensions.map(e => ({ id: e.id, attachTo: e.attachTo, disabled: e.disabled, @@ -211,7 +211,7 @@ describe('collectLegacyRoutes', () => { { id: 'api:plugin.catalog.service', attachTo: { - id: 'app', + id: 'root', input: 'apis', }, defaultConfig: undefined, @@ -220,7 +220,7 @@ describe('collectLegacyRoutes', () => { { id: 'api:catalog-react.starred-entities', attachTo: { - id: 'app', + id: 'root', input: 'apis', }, defaultConfig: undefined, @@ -229,7 +229,7 @@ describe('collectLegacyRoutes', () => { { id: 'api:plugin.catalog.entity-presentation', attachTo: { - id: 'app', + id: 'root', input: 'apis', }, defaultConfig: undefined, @@ -242,7 +242,7 @@ describe('collectLegacyRoutes', () => { extensions: [ { id: 'api:plugin.scoringdata.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], @@ -262,7 +262,15 @@ describe('collectLegacyRoutes', () => { component: () => Promise.resolve(() => { const app = useApp(); - return
plugins: {app.getPlugins().map(p => p.getId())}
; + return ( +
+ plugins:{' '} + {app + .getPlugins() + .map(p => p.getId()) + .join(', ')} +
+ ); }), }), ); @@ -273,10 +281,10 @@ describe('collectLegacyRoutes', () => { , ); - render(createSpecializedApp({ features }).createRoot()); + renderInTestApp(
, { features }); await expect( - screen.findByText('plugins: test'), + screen.findByText('plugins: app, test'), ).resolves.toBeInTheDocument(); }); diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index c5cb13b6e1..197c245278 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -106,7 +106,7 @@ function visitRouteChildren(options: { parentExtensionId: string; context: { pluginId: string; - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; getUniqueName: () => string; discoverPlugin: (plugin: LegacyBackstagePlugin) => void; }; @@ -159,7 +159,7 @@ export function collectLegacyRoutes( ): BackstagePlugin[] { const pluginExtensions = new Map< LegacyBackstagePlugin, - ExtensionDefinition[] + ExtensionDefinition[] >(); const getUniqueName = (() => { @@ -268,7 +268,10 @@ export function collectLegacyRoutes( extensions: [ ...extensions, ...Array.from(plugin.getApis()).map(factory => - ApiBlueprint.make({ namespace: factory.api.id, params: { factory } }), + ApiBlueprint.make({ + namespace: factory.api.id, + params: { factory }, + }), ), ], routes: convertLegacyRouteRefs(plugin.routes ?? {}), diff --git a/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx b/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx index 445f42124f..0f9ccfb2ad 100644 --- a/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx +++ b/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx @@ -22,7 +22,7 @@ import { AppContextProvider } from '../../../core-app-api/src/app/AppContext'; import { RouteResolver } from '../../../core-plugin-api/src/routing/useRouteRef'; import { createFrontendPlugin as createNewPlugin, - BackstagePlugin as NewBackstagePlugin, + FrontendPlugin as NewFrontendPlugin, appTreeApiRef, componentsApiRef, coreComponentRefs, @@ -47,11 +47,11 @@ import { convertLegacyRouteRef } from '../convertLegacyRouteRef'; // Make sure that we only convert each new plugin instance to its legacy equivalent once const legacyPluginStore = getOrCreateGlobalSingleton( 'legacy-plugin-compatibility-store', - () => new WeakMap(), + () => new WeakMap(), ); export function toLegacyPlugin( - plugin: NewBackstagePlugin, + plugin: NewFrontendPlugin, ): LegacyBackstagePlugin { let legacy = legacyPluginStore.get(plugin); if (legacy) { @@ -83,7 +83,7 @@ export function toLegacyPlugin( } // TODO: Currently a very naive implementation, may need some more work -function toNewPlugin(plugin: LegacyBackstagePlugin): NewBackstagePlugin { +function toNewPlugin(plugin: LegacyBackstagePlugin): NewFrontendPlugin { return createNewPlugin({ id: plugin.getId(), }); diff --git a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx index 6357f13906..bee379a792 100644 --- a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx +++ b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx @@ -42,35 +42,39 @@ import { renderInTestApp as renderInOldTestApp } from '@backstage/test-utils'; describe('BackwardsCompatProvider', () => { it('should convert the app context', () => { // TODO(Rugvip): Replace with the new renderInTestApp once it's available, and have some plugins - createExtensionTester( - createExtension({ - attachTo: { id: 'ignored', input: 'ignored' }, - output: [coreExtensionData.reactElement], - factory() { - function Component() { - const app = useApp(); - return ( -
- plugins: - {app - .getPlugins() - .map(p => p.getId()) - .join(', ')} - {'\n'} - components: {Object.keys(app.getComponents()).join(', ')} - {'\n'} - icons: {Object.keys(app.getSystemIcons()).join(', ')} -
- ); - } + renderInNewTestApp( + createExtensionTester( + createExtension({ + attachTo: { id: 'ignored', input: 'ignored' }, + output: [coreExtensionData.reactElement], + factory() { + function Component() { + const app = useApp(); + return ( +
+ plugins:{' '} + {app + .getPlugins() + .map(p => p.getId()) + .join(', ')} + {'\n'} + components: {Object.keys(app.getComponents()).join(', ')} + {'\n'} + icons: {Object.keys(app.getSystemIcons()).join(', ')} +
+ ); + } - return [coreExtensionData.reactElement(compatWrapper())]; - }, - }), - ).render(); + return [ + coreExtensionData.reactElement(compatWrapper()), + ]; + }, + }), + ).reactElement(), + ); expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(` - "plugins: + "plugins: app components: NotFoundErrorPage, BootErrorPage, Progress, Router, ErrorBoundaryFallback icons: brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, user, warning" `); diff --git a/packages/core-compat-api/src/convertLegacyApp.test.tsx b/packages/core-compat-api/src/convertLegacyApp.test.tsx index ea84c06ba9..b76a6c2d71 100644 --- a/packages/core-compat-api/src/convertLegacyApp.test.tsx +++ b/packages/core-compat-api/src/convertLegacyApp.test.tsx @@ -66,7 +66,7 @@ describe('convertLegacyApp', () => { }, { id: 'api:plugin.scoringdata.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], @@ -82,7 +82,7 @@ describe('convertLegacyApp', () => { }, { id: 'api:plugin.stackstorm.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], @@ -104,7 +104,7 @@ describe('convertLegacyApp', () => { }, { id: 'api:plugin.puppetdb.service', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, disabled: false, }, ], diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index ffeab3bdc6..2729664941 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -22,7 +22,8 @@ import React, { isValidElement, } from 'react'; import { - FrontendFeature, + ExtensionOverrides, + FrontendPlugin, coreExtensionData, createExtension, createExtensionInput, @@ -60,7 +61,7 @@ function selectChildren( /** @public */ export function convertLegacyApp( rootElement: React.JSX.Element, -): FrontendFeature[] { +): (FrontendPlugin | ExtensionOverrides)[] { if (getComponentData(rootElement, 'core.type') === 'FlatRoutes') { return collectLegacyRoutes(rootElement); } diff --git a/packages/core-compat-api/src/convertLegacyPageExtension.test.tsx b/packages/core-compat-api/src/convertLegacyPageExtension.test.tsx index 482474d580..c1630b31b5 100644 --- a/packages/core-compat-api/src/convertLegacyPageExtension.test.tsx +++ b/packages/core-compat-api/src/convertLegacyPageExtension.test.tsx @@ -48,12 +48,11 @@ describe('convertLegacyPageExtension', () => { ); const converted = convertLegacyPageExtension(LegacyExtension); - expect(converted.kind).toBe('page'); - expect(converted.namespace).toBe(undefined); - expect(converted.name).toBe('example'); const tester = createExtensionTester(converted); + expect(tester.query(converted).node.spec.id).toBe('page:example'); + await renderInTestApp(tester.reactElement(), { mountedRoutes: { '/': convertLegacyRouteRef(routeRef), @@ -79,12 +78,11 @@ describe('convertLegacyPageExtension', () => { name: 'other', defaultPath: '/other', }); - expect(converted.kind).toBe('page'); - expect(converted.namespace).toBe(undefined); - expect(converted.name).toBe('other'); const tester = createExtensionTester(converted); + expect(tester.query(converted).node.spec.id).toBe('page:other'); + await renderInTestApp(tester.reactElement(), { mountedRoutes: { '/': convertLegacyRouteRef(routeRef), diff --git a/packages/core-compat-api/src/convertLegacyPageExtension.tsx b/packages/core-compat-api/src/convertLegacyPageExtension.tsx index 7ef4e0faff..3931a730c8 100644 --- a/packages/core-compat-api/src/convertLegacyPageExtension.tsx +++ b/packages/core-compat-api/src/convertLegacyPageExtension.tsx @@ -35,7 +35,7 @@ export function convertLegacyPageExtension( name?: string; defaultPath?: string; }, -): ExtensionDefinition { +): ExtensionDefinition { const element = ; const extName = getComponentData(element, 'core.extensionName'); diff --git a/packages/core-compat-api/src/convertLegacyPlugin.test.tsx b/packages/core-compat-api/src/convertLegacyPlugin.test.tsx index 657ee91725..3d5b001c61 100644 --- a/packages/core-compat-api/src/convertLegacyPlugin.test.tsx +++ b/packages/core-compat-api/src/convertLegacyPlugin.test.tsx @@ -24,7 +24,7 @@ import { import { convertLegacyPlugin } from './convertLegacyPlugin'; import { PageBlueprint } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalBackstagePlugin } from '../../frontend-plugin-api/src/wiring/createFrontendPlugin'; +import { toInternalFrontendPlugin } from '../../frontend-plugin-api/src/wiring/createFrontendPlugin'; describe('convertLegacyPlugin', () => { it('should convert a plain legacy plugin to a new plugin', () => { @@ -34,7 +34,7 @@ describe('convertLegacyPlugin', () => { }), ).toMatchInlineSnapshot(` { - "$$type": "@backstage/BackstagePlugin", + "$$type": "@backstage/FrontendPlugin", "extensions": [], "externalRoutes": {}, "featureFlags": [], @@ -73,7 +73,7 @@ describe('convertLegacyPlugin', () => { }, ); - const internalConverted = toInternalBackstagePlugin(converted); + const internalConverted = toInternalFrontendPlugin(converted); expect(internalConverted.id).toBe('test'); expect(internalConverted.routes).toEqual({ diff --git a/packages/core-compat-api/src/convertLegacyPlugin.ts b/packages/core-compat-api/src/convertLegacyPlugin.ts index 5856108f29..bf5759f214 100644 --- a/packages/core-compat-api/src/convertLegacyPlugin.ts +++ b/packages/core-compat-api/src/convertLegacyPlugin.ts @@ -26,7 +26,7 @@ import { convertLegacyRouteRefs } from './convertLegacyRouteRef'; /** @public */ export function convertLegacyPlugin( legacyPlugin: LegacyBackstagePlugin, - options: { extensions: ExtensionDefinition[] }, + options: { extensions: ExtensionDefinition[] }, ): NewBackstagePlugin { const apiExtensions = Array.from(legacyPlugin.getApis()).map(factory => ApiBlueprint.make({ namespace: factory.api.id, params: { factory } }), diff --git a/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx b/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx index 8bfb228f53..c96ab67782 100644 --- a/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx +++ b/packages/core-components/src/layout/ErrorBoundary/ErrorBoundary.test.tsx @@ -77,7 +77,8 @@ describe('', () => { /^The above error occurred in the component:/, ), expect.stringMatching(/^ErrorBoundary/), + expect.stringMatching(/Warning: findDOMNode/), // React warning, unfortunate but currently true ]); - expect(error.length).toEqual(4); + expect(error.length).toEqual(5); }); }); diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 9a21164f9b..2b8b261e4d 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/create-app +## 0.5.19-next.0 + +### Patch Changes + +- 019d9ad: Minor dockerfile syntax update +- Updated dependencies + - @backstage/cli-common@0.1.14 + ## 0.5.18 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 48a3651f71..3fb0d4ffa8 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.5.18", + "version": "0.5.19-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index 443b474bd3..ae87cd4ed6 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -50,7 +50,6 @@ jest.mock('./versions', () => ({ packageVersions: { root: '1.2.3', '@backstage/cli': '1.0.0', - '@backstage/backend-common': '1.0.0', '@backstage/backend-defaults': '1.0.0', '@backstage/backend-tasks': '1.0.0', '@backstage/catalog-model': '1.0.0', diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 194ffa21d9..84fbc25450 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -32,9 +32,7 @@ leaving any imports in place. import { version as root } from '../../../../package.json'; import { version as appDefaults } from '../../../app-defaults/package.json'; -import { version as backendCommon } from '../../../backend-common/package.json'; import { version as backendDefaults } from '../../../backend-defaults/package.json'; -import { version as backendTasks } from '../../../backend-tasks/package.json'; import { version as catalogClient } from '../../../catalog-client/package.json'; import { version as catalogModel } from '../../../catalog-model/package.json'; import { version as cli } from '../../../cli/package.json'; @@ -89,9 +87,7 @@ import { version as pluginUserSettings } from '../../../../plugins/user-settings export const packageVersions = { root, '@backstage/app-defaults': appDefaults, - '@backstage/backend-common': backendCommon, '@backstage/backend-defaults': backendDefaults, - '@backstage/backend-tasks': backendTasks, '@backstage/catalog-client': catalogClient, '@backstage/catalog-model': catalogModel, '@backstage/cli': cli, diff --git a/packages/create-app/templates/default-app/packages/backend/Dockerfile b/packages/create-app/templates/default-app/packages/backend/Dockerfile index bef67b5ce7..22fe721379 100644 --- a/packages/create-app/templates/default-app/packages/backend/Dockerfile +++ b/packages/create-app/templates/default-app/packages/backend/Dockerfile @@ -34,7 +34,7 @@ USER node WORKDIR /app # This switches many Node.js dependencies to production mode. -ENV NODE_ENV production +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, diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 757d849fd7..c7625ba5cc 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -16,9 +16,7 @@ "build-image": "docker build ../.. -f Dockerfile --tag backstage" }, "dependencies": { - "@backstage/backend-common": "^{{version '@backstage/backend-common'}}", "@backstage/backend-defaults": "^{{version '@backstage/backend-defaults'}}", - "@backstage/backend-tasks": "^{{version '@backstage/backend-tasks'}}", "@backstage/config": "^{{version '@backstage/config'}}", "@backstage/plugin-app-backend": "^{{version '@backstage/plugin-app-backend'}}", "@backstage/plugin-auth-backend": "^{{version '@backstage/plugin-auth-backend'}}", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 6d40e8fe18..4b2e3fbced 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.0.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + ## 1.0.37 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 447cc13cdc..45b32babf4 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.0.37", + "version": "1.0.38-next.0", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 13f01f7458..bec3eb9e36 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.19-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/errors@1.2.4 + ## 0.2.19 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 2e2ca6e331..d9dae4270c 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.19", + "version": "0.2.20-next.0", "private": true, "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 961d099fa1..878cebea2a 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/frontend-app-api +## 0.9.0-next.0 + +### Minor Changes + +- 62cce6c: Removed deprecated `icons` property passing to `createApp` and `createSpecializedApp`. Use `IconBundleBlueprint.make` to create extensions instead and include them in the app. + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. +- f3a2b91: Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the `app` extension to the respective API extensions. For example, extensions created with `ThemeBlueprint` are now attached to the `themes` input of `api:app-theme` rather than the `app` extension. +- 5446061: Internal refactor following removal of v1 extension support. The app implementation itself still supports v1 extensions at runtime. +- 98850de: Added support for defining `replaces` in `createExtensionInput` which will allow extensions to redirect missing `attachTo` points to an input of the created extension. + + ```ts + export const AppThemeApi = ApiBlueprint.makeWithOverrides({ + name: 'app-theme', + inputs: { + themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { + // attachTo: { id: 'app', input: 'themes'} will be redirected to this input instead + replaces: [{ id: 'app', input: 'themes' }], + }), + }, + factory: () { + ... + } + }); + ``` + +- 4a66456: Added the `root` extension the replace the `app` extension as the root of the app. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/plugin-app@0.1.0-next.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 0.8.0 ### Minor Changes diff --git a/packages/frontend-app-api/api-report.md b/packages/frontend-app-api/api-report.md index 27d486b6a8..e102d6bf20 100644 --- a/packages/frontend-app-api/api-report.md +++ b/packages/frontend-app-api/api-report.md @@ -4,36 +4,20 @@ ```ts import { ConfigApi } from '@backstage/core-plugin-api'; +import { createApp as createApp_2 } from '@backstage/frontend-defaults'; +import { CreateAppFeatureLoader as CreateAppFeatureLoader_2 } from '@backstage/frontend-defaults'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; -import { FrontendFeature } from '@backstage/frontend-plugin-api'; -import { IconComponent } from '@backstage/core-plugin-api'; +import { FrontendModule } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; -import { ReactNode } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; import { SubRouteRef } from '@backstage/frontend-plugin-api'; -// @public (undocumented) -export function createApp(options?: { - icons?: { - [key in string]: IconComponent; - }; - features?: (FrontendFeature | CreateAppFeatureLoader)[]; - configLoader?: () => Promise<{ - config: ConfigApi; - }>; - bindRoutes?(context: { bind: CreateAppRouteBinder }): void; - loadingComponent?: ReactNode; -}): { - createRoot(): JSX_2.Element; -}; +// @public @deprecated (undocumented) +export const createApp: typeof createApp_2; -// @public -export interface CreateAppFeatureLoader { - getLoaderName(): string; - load(options: { config: ConfigApi }): Promise<{ - features: FrontendFeature[]; - }>; -} +// @public @deprecated (undocumented) +export type CreateAppFeatureLoader = CreateAppFeatureLoader_2; // @public export type CreateAppRouteBinder = < @@ -50,13 +34,21 @@ export type CreateAppRouteBinder = < // @public export function createSpecializedApp(options?: { - icons?: { - [key in string]: IconComponent; - }; features?: FrontendFeature[]; config?: ConfigApi; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; }): { createRoot(): JSX_2.Element; }; + +// @public (undocumented) +export type FrontendFeature = + | FrontendPlugin + | FrontendModule + | { + $$type: '@backstage/ExtensionOverrides'; + } + | { + $$type: '@backstage/BackstagePlugin'; + }; ``` diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 3f13b4ad93..52078a4ff7 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.8.0", + "version": "0.9.0-next.0", "backstage": { "role": "web-library" }, @@ -34,20 +34,18 @@ "dependencies": { "@backstage/config": "workspace:^", "@backstage/core-app-api": "workspace:^", - "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/frontend-defaults": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", - "@backstage/theme": "workspace:^", "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", - "@material-ui/core": "^4.12.4", - "@material-ui/icons": "^4.11.3", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21" }, "devDependencies": { "@backstage/cli": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^15.0.0" diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx index e2c7581e63..3d19fc413f 100644 --- a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx @@ -15,57 +15,22 @@ */ import React from 'react'; -import { - coreExtensionData, - createComponentExtension, - createComponentRef, - createExtension, - createExtensionOverrides, -} from '@backstage/frontend-plugin-api'; -import { resolveAppNodeSpecs } from '../../../tree/resolveAppNodeSpecs'; -import { resolveAppTree } from '../../../tree/resolveAppTree'; -import { App } from '../../../extensions/App'; +import { createComponentRef } from '@backstage/frontend-plugin-api'; import { DefaultComponentsApi } from './DefaultComponentsApi'; import { render, screen } from '@testing-library/react'; -import { instantiateAppNodeTree } from '../../../tree/instantiateAppNodeTree'; const testRefA = createComponentRef({ id: 'test.a' }); const testRefB1 = createComponentRef({ id: 'test.b' }); const testRefB2 = createComponentRef({ id: 'test.b' }); -const baseOverrides = createExtensionOverrides({ - extensions: [ - App, - createExtension({ - namespace: 'app', - name: 'root', - attachTo: { id: 'app', input: 'root' }, - output: [coreExtensionData.reactElement], - factory: () => [coreExtensionData.reactElement(
root
)], - }), - ], -}); - describe('DefaultComponentsApi', () => { it('should provide components', () => { - const tree = resolveAppTree( - 'app', - resolveAppNodeSpecs({ - features: [ - baseOverrides, - createExtensionOverrides({ - extensions: [ - createComponentExtension({ - ref: testRefA, - loader: { sync: () => () =>
test.a
}, - }), - ], - }), - ], - }), - ); - instantiateAppNodeTree(tree.root); - const api = DefaultComponentsApi.fromTree(tree); + const api = DefaultComponentsApi.fromComponents([ + { + ref: testRefA, + impl: () =>
test.a
, + }, + ]); const ComponentA = api.getComponent(testRefA); render(); @@ -74,24 +39,12 @@ describe('DefaultComponentsApi', () => { }); it('should key extension refs by ID', () => { - const tree = resolveAppTree( - 'app', - resolveAppNodeSpecs({ - features: [ - baseOverrides, - createExtensionOverrides({ - extensions: [ - createComponentExtension({ - ref: testRefB1, - loader: { sync: () => () =>
test.b
}, - }), - ], - }), - ], - }), - ); - instantiateAppNodeTree(tree.root); - const api = DefaultComponentsApi.fromTree(tree); + const api = DefaultComponentsApi.fromComponents([ + { + ref: testRefB1, + impl: () =>
test.b
, + }, + ]); const ComponentB1 = api.getComponent(testRefB1); const ComponentB2 = api.getComponent(testRefB2); diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts index 572b6fc3c4..317233d797 100644 --- a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.ts @@ -16,7 +16,6 @@ import { ComponentType } from 'react'; import { - AppTree, ComponentRef, ComponentsApi, createComponentExtension, @@ -30,19 +29,12 @@ import { export class DefaultComponentsApi implements ComponentsApi { #components: Map>; - static fromTree(tree: AppTree) { - const componentEntries = tree.root.edges.attachments - .get('components') - ?.reduce((map, e) => { - const data = e.instance?.getData( - createComponentExtension.componentDataRef, - ); - if (data) { - map.set(data.ref.id, data.impl); - } - return map; - }, new Map()); - return new DefaultComponentsApi(componentEntries ?? new Map()); + static fromComponents( + components: Array, + ) { + return new DefaultComponentsApi( + new Map(components.map(entry => [entry.ref.id, entry.impl])), + ); } constructor(components: Map) { diff --git a/packages/frontend-app-api/src/extensions/Root.ts b/packages/frontend-app-api/src/extensions/Root.ts new file mode 100644 index 0000000000..3b1c237e7a --- /dev/null +++ b/packages/frontend-app-api/src/extensions/Root.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2024 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 { + ApiBlueprint, + coreExtensionData, + createExtension, + createExtensionInput, +} from '@backstage/frontend-plugin-api'; + +export const Root = createExtension({ + namespace: 'root', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + app: createExtensionInput([coreExtensionData.reactElement], { + singleton: true, + }), + apis: createExtensionInput([ApiBlueprint.dataRefs.factory], { + replaces: [{ id: 'app', input: 'apis' }], + }), + }, + output: [coreExtensionData.reactElement], + factory: ({ inputs }) => inputs.app, +}); diff --git a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx index 935449b697..aeec03992f 100644 --- a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx +++ b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx @@ -46,7 +46,10 @@ describe('RouteTracker', () => { caseSensitive: false, children: [MATCH_ALL_ROUTE], appNode: { - spec: { extension: { id: 'home.page.index' }, source: { id: 'home' } }, + spec: { + extension: { id: 'home.page.index' }, + source: { id: 'home' }, + }, } as AppNode, }, { diff --git a/packages/frontend-app-api/src/routing/collectRouteIds.ts b/packages/frontend-app-api/src/routing/collectRouteIds.ts index d45871ae89..d89834c02e 100644 --- a/packages/frontend-app-api/src/routing/collectRouteIds.ts +++ b/packages/frontend-app-api/src/routing/collectRouteIds.ts @@ -18,7 +18,6 @@ import { RouteRef, SubRouteRef, ExternalRouteRef, - FrontendFeature, } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { @@ -29,6 +28,9 @@ import { import { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalSubRouteRef } from '../../../frontend-plugin-api/src/routing/SubRouteRef'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { isInternalFrontendPlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; +import { FrontendFeature } from '../wiring'; /** @internal */ export interface RouteRefsById { @@ -42,7 +44,7 @@ export function collectRouteIds(features: FrontendFeature[]): RouteRefsById { const externalRoutesById = new Map(); for (const feature of features) { - if (feature.$$type !== '@backstage/BackstagePlugin') { + if (!isInternalFrontendPlugin(feature)) { continue; } diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts index 51d26ec51b..4e1c37e9a0 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts @@ -28,9 +28,16 @@ import { createFrontendPlugin, createRouteRef, } from '@backstage/frontend-plugin-api'; -import { MockConfigApi } from '@backstage/test-utils'; -import { createAppTree } from '../tree'; -import { builtinExtensions } from '../wiring/createApp'; +import { MockConfigApi, TestApiRegistry } from '@backstage/test-utils'; +import appPlugin from '@backstage/plugin-app'; + +import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig'; +import { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs'; +import { resolveAppTree } from '../tree/resolveAppTree'; +import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree'; +import { Root } from '../extensions/Root'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; const ref1 = createRouteRef(); const ref2 = createRouteRef(); @@ -72,16 +79,23 @@ function createTestExtension(options: { }); } -function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { +function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { const plugin = createFrontendPlugin({ id: 'test', extensions, }); - const tree = createAppTree({ - config: new MockConfigApi({}), - builtinExtensions, - features: [plugin], - }); + + const tree = resolveAppTree( + 'root', + resolveAppNodeSpecs({ + features: [appPlugin, plugin], + builtinExtensions: [resolveExtensionDefinition(Root)], + parameters: readAppExtensionsConfig(new MockConfigApi({})), + forbidden: new Set(['root']), + }), + ); + + instantiateAppNodeTree(tree.root, TestApiRegistry.from()); return extractRouteInfoFromAppNode(tree.root); } diff --git a/packages/frontend-app-api/src/tree/createAppTree.test.ts b/packages/frontend-app-api/src/tree/createAppTree.test.ts deleted file mode 100644 index f5df5270cd..0000000000 --- a/packages/frontend-app-api/src/tree/createAppTree.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2023 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 { - createExtension, - createExtensionOverrides, - createFrontendPlugin, -} from '@backstage/frontend-plugin-api'; -import { MockConfigApi } from '@backstage/test-utils'; -import { createAppTree } from './createAppTree'; - -const extBase = { - id: 'test', - attachTo: { id: 'app', input: 'root' }, - output: [], - factory: () => [], -}; - -describe('createAppTree', () => { - it('throws an error when a app extension is parametrized', () => { - const config = new MockConfigApi({ - app: { - extensions: [ - { - app: {}, - }, - ], - }, - }); - const features = [ - createFrontendPlugin({ - id: 'plugin', - extensions: [], - }), - ]; - expect(() => - createAppTree({ features, config, builtinExtensions: [] }), - ).toThrow("Configuration of the 'app' extension is forbidden"); - }); - - it('throws an error when a app extension is overridden', () => { - const config = new MockConfigApi({}); - const features = [ - createExtensionOverrides({ - extensions: [ - createExtension({ - name: 'app', - attachTo: { id: 'app/routes', input: 'route' }, - inputs: {}, - output: [], - factory: () => [], - }), - ], - }), - ]; - expect(() => - createAppTree({ features, config, builtinExtensions: [] }), - ).toThrow( - "It is forbidden to override the following extension(s): 'app', which is done by one or more extension overrides", - ); - }); - - it('throws an error when duplicated extension overrides are detected', () => { - expect(() => - createAppTree({ - features: [ - createExtensionOverrides({ - extensions: [ - createExtension({ ...extBase, name: 'a' }), - createExtension({ ...extBase, name: 'a' }), - createExtension({ ...extBase, name: 'b' }), - ], - }), - createExtensionOverrides({ - extensions: [createExtension({ ...extBase, name: 'b' })], - }), - ], - config: new MockConfigApi({}), - builtinExtensions: [], - }), - ).toThrow('The following extensions had duplicate overrides: a, b'); - }); -}); diff --git a/packages/frontend-app-api/src/tree/createAppTree.ts b/packages/frontend-app-api/src/tree/createAppTree.ts deleted file mode 100644 index ee9704c3c3..0000000000 --- a/packages/frontend-app-api/src/tree/createAppTree.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023 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 { Extension, FrontendFeature } from '@backstage/frontend-plugin-api'; -import { readAppExtensionsConfig } from './readAppExtensionsConfig'; -import { resolveAppTree } from './resolveAppTree'; -import { resolveAppNodeSpecs } from './resolveAppNodeSpecs'; -import { AppTree } from '@backstage/frontend-plugin-api'; -import { Config } from '@backstage/config'; -import { instantiateAppNodeTree } from './instantiateAppNodeTree'; - -/** @internal */ -export interface CreateAppTreeOptions { - features: FrontendFeature[]; - builtinExtensions: Extension[]; - config: Config; -} - -/** @internal */ -export function createAppTree(options: CreateAppTreeOptions): AppTree { - const tree = resolveAppTree( - 'app', - resolveAppNodeSpecs({ - features: options.features, - builtinExtensions: options.builtinExtensions, - parameters: readAppExtensionsConfig(options.config), - forbidden: new Set(['app']), - }), - ); - instantiateAppNodeTree(tree.root); - return tree; -} diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index b8acb6643d..06d412b30e 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -38,8 +38,9 @@ import { } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { createSchemaFromZod } from '../../../frontend-plugin-api/src/schema/createSchemaFromZod'; -import { withLogCollector } from '@backstage/test-utils'; +import { TestApiRegistry, withLogCollector } from '@backstage/test-utils'; +const testApis = TestApiRegistry.from(); const testDataRef = createExtensionDataRef().with({ id: 'test' }); const otherDataRef = createExtensionDataRef().with({ id: 'other' }); const inputMirrorDataRef = createExtensionDataRef().with({ @@ -82,6 +83,7 @@ function makeInstanceWithId( instance: createAppNodeInstance({ node, attachments: new Map(), + apis: testApis, }), }; } @@ -149,12 +151,12 @@ describe('instantiateAppNodeTree', () => { makeSpec(simpleExtension, { id: 'root-node' }), ]); expect(tree.root.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(tree.root.instance?.getData(testDataRef)).toBe('test'); // Multiple calls should have no effect - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); }); @@ -163,7 +165,7 @@ describe('instantiateAppNodeTree', () => { makeSpec(simpleExtension, { id: 'root-node', disabled: true }), ]); expect(tree.root.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).not.toBeDefined(); }); @@ -195,7 +197,7 @@ describe('instantiateAppNodeTree', () => { expect(tree.root.instance).not.toBeDefined(); expect(childNode?.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).toBeDefined(); expect(tree.root.instance?.getData(inputMirrorDataRef)).toMatchObject({ @@ -205,7 +207,7 @@ describe('instantiateAppNodeTree', () => { }); // Multiple calls should have no effect - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).toBeDefined(); }); @@ -243,7 +245,7 @@ describe('instantiateAppNodeTree', () => { expect(tree.root.instance).not.toBeDefined(); expect(childNode?.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).not.toBeDefined(); expect(tree.root.instance?.getData(inputMirrorDataRef)).toEqual({ @@ -257,6 +259,7 @@ describe('instantiateAppNodeTree', () => { const instance = createAppNodeInstance({ node: makeNode(simpleExtension), attachments, + apis: testApis, }); expect(Array.from(instance.getDataRefs())).toEqual([ @@ -297,6 +300,7 @@ describe('instantiateAppNodeTree', () => { ], ]); const instance = createAppNodeInstance({ + apis: testApis, attachments, node: makeNode( createV1Extension({ @@ -365,6 +369,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an extension with invalid config', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode(simpleExtension, { config: { other: 'not-a-number' }, }), @@ -378,6 +383,7 @@ describe('instantiateAppNodeTree', () => { it('should forward extension factory errors', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( createV1Extension({ namespace: 'app', @@ -401,6 +407,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with duplicate output', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( createV1Extension({ namespace: 'app', @@ -425,6 +432,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with disconnected output data', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( createV1Extension({ namespace: 'app', @@ -448,6 +456,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with missing required input', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( createV1Extension({ namespace: 'app', @@ -475,6 +484,7 @@ describe('instantiateAppNodeTree', () => { it('should warn when creating an instance with undeclared inputs', () => { const { warn } = withLogCollector(['warn'], () => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'declared', @@ -518,6 +528,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple undeclared inputs', () => { const { warn } = withLogCollector(['warn'], () => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'undeclared1', @@ -552,6 +563,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple inputs for required singleton', () => { expect(() => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'singleton', @@ -587,6 +599,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple inputs for optional singleton', () => { expect(() => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'singleton', @@ -622,6 +635,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple inputs that did not provide required data', () => { expect(() => createAppNodeInstance({ + apis: testApis, attachments: new Map([ ['singleton', [makeInstanceWithId(simpleExtension, undefined)]], ]), @@ -713,12 +727,12 @@ describe('instantiateAppNodeTree', () => { makeSpec(simpleExtension, { id: 'root-node' }), ]); expect(tree.root.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(tree.root.instance?.getData(testDataRef)).toBe('test'); // Multiple calls should have no effect - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); }); @@ -727,7 +741,7 @@ describe('instantiateAppNodeTree', () => { makeSpec(simpleExtension, { id: 'root-node', disabled: true }), ]); expect(tree.root.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).not.toBeDefined(); }); @@ -757,7 +771,7 @@ describe('instantiateAppNodeTree', () => { expect(tree.root.instance).not.toBeDefined(); expect(childNode?.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).toBeDefined(); expect(tree.root.instance?.getData(inputMirrorDataRef)).toMatchObject({ @@ -765,7 +779,7 @@ describe('instantiateAppNodeTree', () => { }); // Multiple calls should have no effect - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).toBeDefined(); }); @@ -801,7 +815,7 @@ describe('instantiateAppNodeTree', () => { expect(tree.root.instance).not.toBeDefined(); expect(childNode?.instance).not.toBeDefined(); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, testApis); expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).not.toBeDefined(); expect(tree.root.instance?.getData(inputMirrorDataRef)).toEqual({ @@ -815,6 +829,7 @@ describe('instantiateAppNodeTree', () => { const instance = createAppNodeInstance({ node: makeNode(simpleExtension), attachments, + apis: testApis, }); expect(Array.from(instance.getDataRefs())).toEqual([testDataRef]); @@ -854,6 +869,7 @@ describe('instantiateAppNodeTree', () => { const instance = createAppNodeInstance({ attachments, + apis: testApis, node: makeNode( resolveExtensionDefinition( createExtension({ @@ -912,6 +928,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an extension with invalid config', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode(simpleExtension, { config: { other: 'not-a-number' }, }), @@ -925,6 +942,7 @@ describe('instantiateAppNodeTree', () => { it('should forward extension factory errors', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( resolveExtensionDefinition( createExtension({ @@ -950,6 +968,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with duplicate output', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( resolveExtensionDefinition( createExtension({ @@ -973,6 +992,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance without required', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( resolveExtensionDefinition( createExtension({ @@ -996,6 +1016,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with unknown output data', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( resolveExtensionDefinition( // @ts-expect-error @@ -1020,6 +1041,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with missing required input', () => { expect(() => createAppNodeInstance({ + apis: testApis, node: makeNode( resolveExtensionDefinition( createExtension({ @@ -1046,6 +1068,7 @@ describe('instantiateAppNodeTree', () => { it('should warn when creating an instance with undeclared inputs', () => { const { warn } = withLogCollector(['warn'], () => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'declared', @@ -1089,6 +1112,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple undeclared inputs', () => { const { warn } = withLogCollector(['warn'], () => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'undeclared1', @@ -1125,6 +1149,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple inputs for required singleton', () => { expect(() => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'singleton', @@ -1159,6 +1184,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple inputs for optional singleton', () => { expect(() => createAppNodeInstance({ + apis: testApis, attachments: new Map([ [ 'singleton', @@ -1194,6 +1220,7 @@ describe('instantiateAppNodeTree', () => { it('should refuse to create an instance with multiple inputs that did not provide required data', () => { expect(() => createAppNodeInstance({ + apis: testApis, attachments: new Map([ ['singleton', [makeInstanceWithId(simpleExtension, undefined)]], ]), diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index fd8d324652..cc17bfae27 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -16,6 +16,7 @@ import { AnyExtensionDataRef, + ApiHolder, ExtensionDataContainer, ExtensionDataRef, ExtensionInput, @@ -242,9 +243,10 @@ function resolveV2Inputs( /** @internal */ export function createAppNodeInstance(options: { node: AppNode; + apis: ApiHolder; attachments: ReadonlyMap; }): AppNodeInstance { - const { node, attachments } = options; + const { node, apis, attachments } = options; const { id, extension, config } = node.spec; const extensionData = new Map(); const extensionDataRefs = new Set>(); @@ -268,6 +270,7 @@ export function createAppNodeInstance(options: { if (internalExtension.version === 'v1') { const namedOutputs = internalExtension.factory({ node, + apis, config: parsedConfig, inputs: resolveV1Inputs(internalExtension.inputs, attachments), }); @@ -288,6 +291,7 @@ export function createAppNodeInstance(options: { } else if (internalExtension.version === 'v2') { const outputDataValues = internalExtension.factory({ node, + apis, config: parsedConfig, inputs: resolveV2Inputs(internalExtension.inputs, attachments), }); @@ -349,7 +353,10 @@ export function createAppNodeInstance(options: { * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled. * @internal */ -export function instantiateAppNodeTree(rootNode: AppNode): void { +export function instantiateAppNodeTree( + rootNode: AppNode, + apis: ApiHolder, +): void { function createInstance(node: AppNode): AppNodeInstance | undefined { if (node.instance) { return node.instance; @@ -375,6 +382,7 @@ export function instantiateAppNodeTree(rootNode: AppNode): void { (node as Mutable).instance = createAppNodeInstance({ node, + apis, attachments: instantiatedAttachments, }); diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts index a732b327b6..e4500df6c0 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -16,6 +16,7 @@ import { createExtensionOverrides, + createFrontendModule, createFrontendPlugin, Extension, ExtensionDefinition, @@ -38,18 +39,19 @@ function makeExt( } function makeExtDef( - name: string, + name: string | undefined = undefined, status: 'disabled' | 'enabled' = 'enabled', attachId: string = 'root', ) { return { $$type: '@backstage/ExtensionDefinition', + T: undefined as any, version: 'v1', name, attachTo: { id: attachId, input: 'default' }, disabled: status === 'disabled', - override: () => ({} as ExtensionDefinition), - } as ExtensionDefinition; + override: () => ({} as ExtensionDefinition), + } as ExtensionDefinition; } describe('resolveAppNodeSpecs', () => { @@ -323,6 +325,56 @@ describe('resolveAppNodeSpecs', () => { ]); }); + it('should apply module overrides', () => { + const plugin = createFrontendPlugin({ + id: 'test', + extensions: [makeExtDef('a'), makeExtDef('b')], + }); + const aOverride = makeExt('test/a', 'enabled', 'other'); + const bOverride = makeExt('test/b', 'disabled', 'other'); + const cOverride = makeExt('test/c'); + + expect( + resolveAppNodeSpecs({ + features: [ + plugin, + createFrontendModule({ + pluginId: 'test', + extensions: [ + makeExtDef('a', 'enabled', 'other'), + makeExtDef('b', 'disabled', 'other'), + makeExtDef('c'), + ], + }), + ], + builtinExtensions: [], + parameters: [], + }), + ).toEqual([ + { + id: 'test/a', + extension: expect.objectContaining(aOverride), + attachTo: { id: 'other', input: 'default' }, + source: plugin, + disabled: false, + }, + { + id: 'test/b', + extension: expect.objectContaining(bOverride), + attachTo: { id: 'other', input: 'default' }, + source: plugin, + disabled: true, + }, + { + id: 'test/c', + extension: expect.objectContaining(cOverride), + attachTo: { id: 'root', input: 'default' }, + source: plugin, + disabled: false, + }, + ]); + }); + it('should use order from configuration when rather than overrides', () => { const result = resolveAppNodeSpecs({ features: [ @@ -356,6 +408,40 @@ describe('resolveAppNodeSpecs', () => { ]); }); + it('should use order from configuration when rather than modules', () => { + const result = resolveAppNodeSpecs({ + features: [ + createFrontendPlugin({ + id: 'test', + extensions: [ + makeExtDef('a', 'disabled'), + makeExtDef('b', 'disabled'), + makeExtDef('c', 'disabled'), + ], + }), + createFrontendModule({ + pluginId: 'test', + extensions: [ + makeExtDef('c', 'disabled'), + makeExtDef('b', 'disabled'), + makeExtDef('a', 'disabled'), + ], + }), + ], + builtinExtensions: [], + parameters: ['test/b', 'test/c', 'test/a'].map(id => ({ + id, + disabled: false, + })), + }); + + expect(result.map(r => r.extension.id)).toEqual([ + 'test/b', + 'test/c', + 'test/a', + ]); + }); + it('throws an error when a forbidden extension is overridden by a plugin', () => { expect(() => resolveAppNodeSpecs({ @@ -389,6 +475,28 @@ describe('resolveAppNodeSpecs', () => { ); }); + it('throws an error when a forbidden extension is overridden by module', () => { + expect(() => + resolveAppNodeSpecs({ + features: [ + createFrontendPlugin({ + id: 'forbidden', + extensions: [], + }), + createFrontendModule({ + pluginId: 'forbidden', + extensions: [makeExtDef()], + }), + ], + builtinExtensions: [], + parameters: [], + forbidden: new Set(['forbidden']), + }), + ).toThrow( + "It is forbidden to override the following extension(s): 'forbidden', which is done by a module for the following plugin(s): 'forbidden'", + ); + }); + it('throws an error when a forbidden extension is parametrized', () => { expect(() => resolveAppNodeSpecs({ diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 0560066f6b..bc3da52cd6 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -14,25 +14,29 @@ * limitations under the License. */ -import { - BackstagePlugin, - Extension, - ExtensionOverrides, - FrontendFeature, -} from '@backstage/frontend-plugin-api'; +import { Extension, ExtensionOverrides } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; import { ExtensionParameters } from './readAppExtensionsConfig'; import { AppNodeSpec } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; +import { + isInternalFrontendPlugin, + toInternalFrontendPlugin, +} from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { + isInternalFrontendModule, + toInternalFrontendModule, +} from '../../../frontend-plugin-api/src/wiring/createFrontendModule'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; +import { FrontendFeature } from '../wiring'; /** @internal */ export function resolveAppNodeSpecs(options: { features?: FrontendFeature[]; - builtinExtensions?: Extension[]; + builtinExtensions?: Extension[]; parameters?: Array; forbidden?: Set; }): AppNodeSpec[] { @@ -43,20 +47,30 @@ export function resolveAppNodeSpecs(options: { features = [], } = options; - const plugins = features.filter( - (f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin', - ); + const plugins = features.filter(isInternalFrontendPlugin); const overrides = features.filter( (f): f is ExtensionOverrides => f.$$type === '@backstage/ExtensionOverrides', ); + const modules = features.filter(isInternalFrontendModule); const pluginExtensions = plugins.flatMap(source => { - return toInternalBackstagePlugin(source).extensions.map(extension => ({ + return toInternalFrontendPlugin(source).extensions.map(extension => ({ ...extension, source, })); }); + const moduleExtensions = modules.flatMap(mod => + toInternalFrontendModule(mod).extensions.flatMap(extension => { + // Modules for plugins that are not installed are ignored + const source = plugins.find(p => p.id === mod.pluginId); + if (!source) { + return []; + } + + return [{ ...extension, source }]; + }), + ); const overrideExtensions = overrides.flatMap( override => toInternalExtensionOverrides(override).extensions, ); @@ -72,13 +86,23 @@ export function resolveAppNodeSpecs(options: { `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by the following plugin(s): ${pluginsStr}`, ); } - + if (moduleExtensions.some(({ id }) => forbidden.has(id))) { + const pluginsStr = moduleExtensions + .filter(({ id }) => forbidden.has(id)) + .map(({ source }) => `'${source.id}'`) + .join(', '); + const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); + throw new Error( + `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by a module for the following plugin(s): ${pluginsStr}`, + ); + } if (overrideExtensions.some(({ id }) => forbidden.has(id))) { const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); throw new Error( `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by one or more extension overrides`, ); } + const overrideExtensionIds = overrideExtensions.map(({ id }) => id); if (overrideExtensionIds.length !== new Set(overrideExtensionIds).size) { const counts = new Map(); @@ -149,6 +173,33 @@ export function resolveAppNodeSpecs(options: { } } + // Install all module overrides + for (const extension of moduleExtensions) { + const internalExtension = toInternalExtension(extension); + + // Check if our override is overriding an extension that already exists + const index = configuredExtensions.findIndex( + e => e.extension.id === extension.id, + ); + if (index !== -1) { + // Only implementation, attachment point and default disabled status are overridden, the source is kept + configuredExtensions[index].extension = internalExtension; + configuredExtensions[index].params.attachTo = internalExtension.attachTo; + configuredExtensions[index].params.disabled = internalExtension.disabled; + } else { + // Add the extension as a new one when not overriding an existing one + configuredExtensions.push({ + extension: internalExtension, + params: { + source: extension.source, + attachTo: internalExtension.attachTo, + disabled: internalExtension.disabled, + config: undefined, + }, + }); + } + } + const duplicatedExtensionIds = new Set(); const duplicatedExtensionData = configuredExtensions.reduce< Record> diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts index 740f05d5f7..0517e943aa 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import { createExtension, Extension } from '@backstage/frontend-plugin-api'; +import { + coreExtensionData, + createExtension, + createExtensionInput, + Extension, +} from '@backstage/frontend-plugin-api'; import { resolveAppTree } from './resolveAppTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; @@ -165,4 +170,146 @@ describe('buildAppTree', () => { ]), ).toThrow("Unexpected duplicate extension id 'a'"); }); + + describe('redirects', () => { + it('should throw an error when theres a duplicate redirect target', () => { + const e1 = resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'nonexistent', input: 'nonexistent' }, + inputs: { + test: createExtensionInput([coreExtensionData.reactElement], { + replaces: [{ id: 'a', input: 'test' }], + }), + }, + output: [], + factory: () => [], + }), + ) as Extension; + + const e2 = resolveExtensionDefinition( + createExtension({ + name: 'test-2', + attachTo: { id: 'nonexistent', input: 'nonexistent' }, + inputs: { + test: createExtensionInput([coreExtensionData.reactElement], { + replaces: [{ id: 'a', input: 'test' }], + }), + }, + output: [], + factory: () => [], + }), + ) as Extension; + + expect(() => + resolveAppTree('a', [ + { ...baseSpec, id: 'a', extension: e1 }, + { ...baseSpec, id: 'b', extension: e2 }, + ]), + ).toThrow("Duplicate redirect target for input 'test' in extension 'b'"); + }); + + it('should set the correct attachment point for a redirect', () => { + const e1 = resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'nonexistent', input: 'nonexistent' }, + inputs: { + test: createExtensionInput([coreExtensionData.reactElement], { + replaces: [{ id: 'replace', input: 'me' }], + }), + }, + output: [], + factory: () => [], + }), + ) as Extension; + + const e2 = resolveExtensionDefinition( + createExtension({ + name: 'test-2', + attachTo: { id: 'replace', input: 'me' }, + output: [], + factory: () => [], + }), + ) as Extension; + + const tree = resolveAppTree('a', [ + { attachTo: e1.attachTo, id: 'a', extension: e1, disabled: false }, + { attachTo: e2.attachTo, id: 'b', extension: e2, disabled: false }, + ]); + + expect(tree.root).toMatchInlineSnapshot(` + { + "attachments": { + "test": [ + { + "attachments": undefined, + "id": "b", + "output": undefined, + }, + ], + }, + "id": "a", + "output": undefined, + } + `); + + expect(tree.orphans).toMatchInlineSnapshot(`[]`); + + expect(String(tree.root)).toMatchInlineSnapshot(` + " + test [ + + ] + " + `); + }); + + it('should not allow redirects for attachment points that already exist', () => { + const e1 = resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'a', input: 'a' }, + inputs: { + test: createExtensionInput([coreExtensionData.reactElement], { + replaces: [{ id: 'test-2', input: 'test' }], + }), + }, + output: [], + factory: () => [], + }), + ) as Extension; + + const e2 = resolveExtensionDefinition( + createExtension({ + name: 'test-2', + attachTo: { id: 'b', input: 'b' }, + inputs: { + test: createExtensionInput([coreExtensionData.reactElement]), + }, + output: [], + factory: () => [], + }), + ) as Extension; + + const e3 = resolveExtensionDefinition( + createExtension({ + name: 'test-3', + attachTo: { id: 'test-2', input: 'test' }, + output: [], + factory: () => [], + }), + ) as Extension; + + const tree = resolveAppTree('test-2', [ + { attachTo: e1.attachTo, id: e1.id, extension: e1, disabled: false }, + { attachTo: e2.attachTo, id: e2.id, extension: e2, disabled: false }, + { attachTo: e3.attachTo, id: e3.id, extension: e3, disabled: false }, + ]); + + expect(tree.nodes.get('test-3')?.edges.attachedTo?.node).toBe( + tree.nodes.get('test-2'), + ); + }); + }); }); diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.ts b/packages/frontend-app-api/src/tree/resolveAppTree.ts index 24948373bf..dc9985da30 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.ts @@ -21,6 +21,9 @@ import { AppNodeSpec, } from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; + function indent(str: string) { return str.replace(/^/gm, ' '); } @@ -38,9 +41,7 @@ class SerializableAppNode implements AppNode { this.spec = spec; } - setParent(parent: SerializableAppNode) { - const input = this.spec.attachTo.input; - + setParent(parent: SerializableAppNode, input: string) { this.edges.attachedTo = { node: parent, input }; const parentInputEdges = parent.edges.attachments.get(input); @@ -87,6 +88,24 @@ class SerializableAppNode implements AppNode { } } +function makeRedirectKey(attachTo: { id: string; input: string }) { + return `${attachTo.id}%${attachTo.input}`; +} + +const isValidAttachmentPoint = ( + attachTo: { id: string; input: string }, + nodes: Map, +) => { + if (!nodes.has(attachTo.id)) { + return false; + } + + return ( + attachTo.input in + toInternalExtension(nodes.get(attachTo.id)!.spec.extension).inputs + ); +}; + /** * Build the app tree by iterating through all node specs and constructing the app * tree with all attachments in the same order as they appear in the input specs array. @@ -98,17 +117,7 @@ export function resolveAppTree( ): AppTree { const nodes = new Map(); - // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything - let rootNode: AppNode | undefined = undefined; - - // While iterating through the inputs specs we keep track of all nodes that were created - // before their parent, and attach them later when the parent is created. - // As we find the parents and attach the children, we remove them from this map. This means - // that after iterating through all input specs, this will be a map for each root node. - const orphansByParent = new Map< - string /* parentId */, - SerializableAppNode[] - >(); + const redirectTargetsByKey = new Map(); for (const spec of specs) { // The main check with a more helpful error message happens in resolveAppNodeSpecs @@ -119,28 +128,46 @@ export function resolveAppTree( const node = new SerializableAppNode(spec); nodes.set(spec.id, node); + const internal = toInternalExtension(spec.extension); + for (const [inputName, input] of Object.entries(internal.inputs)) { + if (input.replaces) { + for (const replace of input.replaces) { + const key = makeRedirectKey(replace); + if (redirectTargetsByKey.has(key)) { + throw new Error( + `Duplicate redirect target for input '${inputName}' in extension '${spec.id}'`, + ); + } + redirectTargetsByKey.set(key, { id: spec.id, input: inputName }); + } + } + } + } + + const orphans = new Array(); + + // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything + let rootNode: AppNode | undefined = undefined; + + for (const node of nodes.values()) { + const spec = node.spec; + // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it if (spec.id === rootNodeId) { rootNode = node; } else { - const parent = nodes.get(spec.attachTo.id); - if (parent) { - node.setParent(parent); - } else { - const orphanNodesForParent = orphansByParent.get(spec.attachTo.id); - if (orphanNodesForParent) { - orphanNodesForParent.push(node); - } else { - orphansByParent.set(spec.attachTo.id, [node]); - } - } - } + let attachTo = node.spec.attachTo; - const orphanedChildren = orphansByParent.get(spec.id); - if (orphanedChildren) { - orphansByParent.delete(spec.id); - for (const orphan of orphanedChildren) { - orphan.setParent(node); + if (!isValidAttachmentPoint(attachTo, nodes)) { + attachTo = + redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo; + } + + const parent = nodes.get(attachTo.id); + if (parent) { + node.setParent(parent, attachTo.input); + } else { + orphans.push(node); } } } @@ -152,6 +179,6 @@ export function resolveAppTree( return { root: rootNode, nodes, - orphans: Array.from(orphansByParent.values()).flat(), + orphans, }; } diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx deleted file mode 100644 index ca42dce3e4..0000000000 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2023 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, { JSX, ReactNode } from 'react'; -import { ConfigReader } from '@backstage/config'; -import { - ApiBlueprint, - AppTree, - appTreeApiRef, - componentsApiRef, - coreExtensionData, - FrontendFeature, - IconBundleBlueprint, - iconsApiRef, - RouteResolutionApi, - routeResolutionApiRef, - ThemeBlueprint, - TranslationBlueprint, -} from '@backstage/frontend-plugin-api'; -import { App } from '../extensions/App'; -import { AppRoutes } from '../extensions/AppRoutes'; -import { AppLayout } from '../extensions/AppLayout'; -import { AppNav } from '../extensions/AppNav'; -import { - AnyApiFactory, - ApiHolder, - appThemeApiRef, - ConfigApi, - configApiRef, - IconComponent, - featureFlagsApiRef, - identityApiRef, - AppTheme, - errorApiRef, - discoveryApiRef, - fetchApiRef, -} from '@backstage/core-plugin-api'; -import { getAvailableFeatures } from './discovery'; -import { - ApiFactoryRegistry, - ApiProvider, - ApiResolver, - AppThemeSelector, -} from '@backstage/core-app-api'; - -// TODO: Get rid of all of these -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { isProtectedApp } from '../../../core-app-api/src/app/isProtectedApp'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppThemeProvider } from '../../../core-app-api/src/app/AppThemeProvider'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { LocalStorageFeatureFlags } from '../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { overrideBaseUrlConfigs } from '../../../core-app-api/src/app/overrideBaseUrlConfigs'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppLanguageSelector } from '../../../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { I18nextTranslationApi } from '../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { apis as defaultApis } from '../../../app-defaults/src/defaults'; -import { DarkTheme, LightTheme } from '../extensions/themes'; -import { - oauthRequestDialogAppRootElement, - alertDisplayAppRootElement, -} from '../extensions/elements'; -import { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode'; -import { - appLanguageApiRef, - translationApiRef, -} from '@backstage/core-plugin-api/alpha'; -import { CreateAppRouteBinder } from '../routing'; -import { RouteResolver } from '../routing/RouteResolver'; -import { resolveRouteBindings } from '../routing/resolveRouteBindings'; -import { collectRouteIds } from '../routing/collectRouteIds'; -import { createAppTree } from '../tree'; -import { - DefaultProgressComponent, - DefaultErrorBoundaryComponent, - DefaultNotFoundErrorPageComponent, -} from '../extensions/components'; -import { InternalAppContext } from './InternalAppContext'; -import { AppRoot } from '../extensions/AppRoot'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; -import { DefaultComponentsApi } from '../apis/implementations/ComponentsApi'; -import { DefaultIconsApi } from '../apis/implementations/IconsApi'; -import { stringifyError } from '@backstage/errors'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { icons as defaultIcons } from '../../../app-defaults/src/defaults'; -import { getBasePath } from '../routing/getBasePath'; - -const DefaultApis = defaultApis.map(factory => - ApiBlueprint.make({ namespace: factory.api.id, params: { factory } }), -); - -export const builtinExtensions = [ - App, - AppRoot, - AppRoutes, - AppNav, - AppLayout, - DefaultProgressComponent, - DefaultErrorBoundaryComponent, - DefaultNotFoundErrorPageComponent, - LightTheme, - DarkTheme, - oauthRequestDialogAppRootElement, - alertDisplayAppRootElement, - ...DefaultApis, -].map(def => resolveExtensionDefinition(def)); - -function deduplicateFeatures( - allFeatures: FrontendFeature[], -): FrontendFeature[] { - // Start by removing duplicates by reference - const features = Array.from(new Set(allFeatures)); - - // Plugins are deduplicated by ID, last one wins - const seenIds = new Set(); - return features - .reverse() - .filter(feature => { - if (feature.$$type !== '@backstage/BackstagePlugin') { - return true; - } - if (seenIds.has(feature.id)) { - return false; - } - seenIds.add(feature.id); - return true; - }) - .reverse(); -} - -/** - * A source of dynamically loaded frontend features. - * - * @public - */ -export interface CreateAppFeatureLoader { - /** - * Returns name of this loader. suitable for showing to users. - */ - getLoaderName(): string; - - /** - * Loads a number of features dynamically. - */ - load(options: { config: ConfigApi }): Promise<{ - features: FrontendFeature[]; - }>; -} - -/** @public */ -export function createApp(options?: { - /** @deprecated - Please use {@link @backstage/frontend-plugin-api#IconBundleBlueprint} to make new icon bundles which can be installed in the app seperately */ - icons?: { [key in string]: IconComponent }; - features?: (FrontendFeature | CreateAppFeatureLoader)[]; - configLoader?: () => Promise<{ config: ConfigApi }>; - bindRoutes?(context: { bind: CreateAppRouteBinder }): void; - /** - * The component to render while loading the app (waiting for config, features, etc) - * - * Is the text "Loading..." by default. - * If set to "null" then no loading fallback component is rendered. * - */ - loadingComponent?: ReactNode; -}): { - createRoot(): JSX.Element; -} { - let suspenseFallback = options?.loadingComponent; - if (suspenseFallback === undefined) { - suspenseFallback = 'Loading...'; - } - - async function appLoader() { - const config = - (await options?.configLoader?.().then(c => c.config)) ?? - ConfigReader.fromConfigs( - overrideBaseUrlConfigs(defaultConfigLoaderSync()), - ); - - const discoveredFeatures = getAvailableFeatures(config); - - const providedFeatures: FrontendFeature[] = []; - for (const entry of options?.features ?? []) { - if ('load' in entry) { - try { - const result = await entry.load({ config }); - providedFeatures.push(...result.features); - } catch (e) { - throw new Error( - `Failed to read frontend features from loader '${entry.getLoaderName()}', ${stringifyError( - e, - )}`, - ); - } - } else { - providedFeatures.push(entry); - } - } - - const app = createSpecializedApp({ - icons: options?.icons, - config, - features: [...discoveredFeatures, ...providedFeatures], - bindRoutes: options?.bindRoutes, - }).createRoot(); - - return { default: () => app }; - } - - return { - createRoot() { - const LazyApp = React.lazy(appLoader); - return ( - - - - ); - }, - }; -} - -/** - * Synchronous version of {@link createApp}, expecting all features and - * config to have been loaded already. - * - * @public - */ -export function createSpecializedApp(options?: { - /** @deprecated - Please use {@link @backstage/frontend-plugin-api#IconBundleBlueprint} to make new icon bundles which can be installed in the app seperately */ - icons?: { [key in string]: IconComponent }; - features?: FrontendFeature[]; - config?: ConfigApi; - bindRoutes?(context: { bind: CreateAppRouteBinder }): void; -}): { createRoot(): JSX.Element } { - const { - features: duplicatedFeatures = [], - config = new ConfigReader({}, 'empty-config'), - } = options ?? {}; - - const features = deduplicateFeatures(duplicatedFeatures); - - const tree = createAppTree({ - features, - builtinExtensions, - config, - }); - - const routeInfo = extractRouteInfoFromAppNode(tree.root); - const routeBindings = resolveRouteBindings( - options?.bindRoutes, - config, - collectRouteIds(features), - ); - - const appIdentityProxy = new AppIdentityProxy(); - const apiHolder = createApiHolder( - tree, - config, - appIdentityProxy, - new RouteResolver( - routeInfo.routePaths, - routeInfo.routeParents, - routeInfo.routeObjects, - routeBindings, - getBasePath(config), - ), - options?.icons, - ); - - if (isProtectedApp()) { - const discoveryApi = apiHolder.get(discoveryApiRef); - const errorApi = apiHolder.get(errorApiRef); - const fetchApi = apiHolder.get(fetchApiRef); - if (!discoveryApi || !errorApi || !fetchApi) { - throw new Error( - 'App is running in protected mode but missing required APIs', - ); - } - appIdentityProxy.enableCookieAuth({ - discoveryApi, - errorApi, - fetchApi, - }); - } - - const featureFlagApi = apiHolder.get(featureFlagsApiRef); - if (featureFlagApi) { - for (const feature of features) { - if (feature.$$type === '@backstage/BackstagePlugin') { - toInternalBackstagePlugin(feature).featureFlags.forEach(flag => - featureFlagApi.registerFlag({ - name: flag.name, - pluginId: feature.id, - }), - ); - } - if (feature.$$type === '@backstage/ExtensionOverrides') { - toInternalExtensionOverrides(feature).featureFlags.forEach(flag => - featureFlagApi.registerFlag({ name: flag.name, pluginId: '' }), - ); - } - } - } - - const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement); - - const AppComponent = () => ( - - - - {rootEl} - - - - ); - - return { - createRoot() { - return ; - }, - }; -} - -function createApiHolder( - tree: AppTree, - configApi: ConfigApi, - appIdentityProxy: AppIdentityProxy, - routeResolutionApi: RouteResolutionApi, - icons?: { [key in string]: IconComponent }, -): ApiHolder { - const factoryRegistry = new ApiFactoryRegistry(); - - const pluginApis = - tree.root.edges.attachments - .get('apis') - ?.map(e => e.instance?.getData(ApiBlueprint.dataRefs.factory)) - .filter((x): x is AnyApiFactory => !!x) ?? []; - - const themeExtensions = - tree.root.edges.attachments - .get('themes') - ?.map(e => e.instance?.getData(ThemeBlueprint.dataRefs.theme)) - .filter((x): x is AppTheme => !!x) ?? []; - - const translationResources = - tree.root.edges.attachments - .get('translations') - ?.map(e => e.instance?.getData(TranslationBlueprint.dataRefs.translation)) - .filter( - (x): x is typeof TranslationBlueprint.dataRefs.translation.T => !!x, - ) ?? []; - - const extensionIcons = tree.root.edges.attachments - .get('icons') - ?.map(e => e.instance?.getData(IconBundleBlueprint.dataRefs.icons)) - .reduce((acc, bundle) => ({ ...acc, ...bundle }), {}); - - for (const factory of pluginApis) { - factoryRegistry.register('default', factory); - } - - // TODO: properly discovery feature flags, maybe rework the whole thing - factoryRegistry.register('default', { - api: featureFlagsApiRef, - deps: {}, - factory: () => new LocalStorageFeatureFlags(), - }); - - factoryRegistry.register('static', { - api: identityApiRef, - deps: {}, - factory: () => appIdentityProxy, - }); - - factoryRegistry.register('static', { - api: appTreeApiRef, - deps: {}, - factory: () => ({ - getTree: () => ({ tree }), - }), - }); - - factoryRegistry.register('static', { - api: routeResolutionApiRef, - deps: {}, - factory: () => routeResolutionApi, - }); - - factoryRegistry.register('static', { - api: componentsApiRef, - deps: {}, - factory: () => DefaultComponentsApi.fromTree(tree), - }); - - factoryRegistry.register('static', { - api: iconsApiRef, - deps: {}, - factory: () => - new DefaultIconsApi({ ...defaultIcons, ...extensionIcons, ...icons }), - }); - - factoryRegistry.register('static', { - api: appThemeApiRef, - deps: {}, - // TODO: add extension for registering themes - factory: () => AppThemeSelector.createWithStorage(themeExtensions), - }); - - factoryRegistry.register('static', { - api: appLanguageApiRef, - deps: {}, - factory: () => AppLanguageSelector.createWithStorage(), - }); - - factoryRegistry.register('static', { - api: configApiRef, - deps: {}, - factory: () => configApi, - }); - - factoryRegistry.register('static', { - api: appLanguageApiRef, - deps: {}, - factory: () => AppLanguageSelector.createWithStorage(), - }); - - factoryRegistry.register('static', { - api: translationApiRef, - deps: { languageApi: appLanguageApiRef }, - factory: ({ languageApi }) => - I18nextTranslationApi.create({ - languageApi, - resources: translationResources, - }), - }); - - ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis()); - - return new ApiResolver(factoryRegistry); -} diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx new file mode 100644 index 0000000000..e98014f273 --- /dev/null +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx @@ -0,0 +1,283 @@ +/* + * Copyright 2023 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 { + AppTreeApi, + appTreeApiRef, + coreExtensionData, + createExtension, + createFrontendPlugin, + ApiBlueprint, + createRouteRef, + createExternalRouteRef, + createExtensionInput, + useRouteRef, +} from '@backstage/frontend-plugin-api'; +import { screen, render } from '@testing-library/react'; +import { createSpecializedApp } from './createSpecializedApp'; +import { MockConfigApi } from '@backstage/test-utils'; +import React from 'react'; +import { + configApiRef, + createApiFactory, + featureFlagsApiRef, +} from '@backstage/core-plugin-api'; +import { MemoryRouter } from 'react-router-dom'; +import { ApiProvider } from '@backstage/core-app-api'; + +describe('createSpecializedApp', () => { + it('should render the root app', () => { + const app = createSpecializedApp({ + features: [ + createFrontendPlugin({ + id: 'test', + extensions: [ + createExtension({ + attachTo: { id: 'root', input: 'app' }, + output: [coreExtensionData.reactElement], + factory: () => [coreExtensionData.reactElement(
Test
)], + }), + ], + }), + ], + }); + + render(app.createRoot()); + + expect(screen.getByText('Test')).toBeInTheDocument(); + }); + + it('should deduplicate features keeping the last received one', () => { + const app = createSpecializedApp({ + features: [ + createFrontendPlugin({ + id: 'test', + extensions: [ + createExtension({ + attachTo: { id: 'root', input: 'app' }, + output: [coreExtensionData.reactElement], + factory: () => [ + coreExtensionData.reactElement(
Test 1
), + ], + }), + ], + }), + createFrontendPlugin({ + id: 'test', + extensions: [ + createExtension({ + attachTo: { id: 'root', input: 'app' }, + output: [coreExtensionData.reactElement], + factory: () => [ + coreExtensionData.reactElement(
Test 2
), + ], + }), + ], + }), + ], + }); + + render(app.createRoot()); + + expect(screen.getByText('Test 2')).toBeInTheDocument(); + }); + + it('should forward config', () => { + const app = createSpecializedApp({ + config: new MockConfigApi({ test: 'foo' }), + features: [ + createFrontendPlugin({ + id: 'test', + extensions: [ + createExtension({ + attachTo: { id: 'root', input: 'app' }, + output: [coreExtensionData.reactElement], + factory: ({ apis }) => [ + coreExtensionData.reactElement( +
Test {apis.get(configApiRef)!.getString('test')}
, + ), + ], + }), + ], + }), + ], + }); + + render(app.createRoot()); + + expect(screen.getByText('Test foo')).toBeInTheDocument(); + }); + + it('should support APIs and feature flags', async () => { + const flags = new Array<{ name: string; pluginId: string }>(); + const app = createSpecializedApp({ + features: [ + createFrontendPlugin({ + id: 'test', + featureFlags: [{ name: 'a' }, { name: 'b' }], + extensions: [ + createExtension({ + attachTo: { id: 'root', input: 'app' }, + output: [coreExtensionData.reactElement], + factory: ({ apis }) => [ + coreExtensionData.reactElement( +
+ flags: + {apis + .get(featureFlagsApiRef)! + .getRegisteredFlags() + .map(f => `${f.pluginId}=${f.name}`) + .join(',')} +
, + ), + ], + }), + ApiBlueprint.make({ + params: { + factory: createApiFactory(featureFlagsApiRef, { + registerFlag(flag) { + flags.push(flag); + }, + getRegisteredFlags() { + return flags; + }, + } as typeof featureFlagsApiRef.T), + }, + }), + ], + }), + ], + }); + + render(app.createRoot()); + + expect(screen.getByText('flags:test=a,test=b')).toBeInTheDocument(); + }); + + it('should make the app structure available through the AppTreeApi', async () => { + let appTreeApi: AppTreeApi | undefined = undefined; + + createSpecializedApp({ + features: [ + createFrontendPlugin({ + id: 'test', + extensions: [ + createExtension({ + attachTo: { id: 'root', input: 'app' }, + output: [coreExtensionData.reactElement], + factory: ({ apis }) => { + appTreeApi = apis.get(appTreeApiRef); + return [coreExtensionData.reactElement(
)]; + }, + }), + ], + }), + ], + }); + + expect(String(appTreeApi!.getTree().tree.root)).toMatchInlineSnapshot(` + " + app [ + + ] + " + `); + }); + + it('should support route bindings', async () => { + const routeRef = createRouteRef(); + const extRouteRef = createExternalRouteRef(); + + const pluginA = createFrontendPlugin({ + id: 'a', + externalRoutes: { + ext: extRouteRef, + }, + extensions: [ + createExtension({ + name: 'parent', + attachTo: { id: 'root', input: 'app' }, + inputs: { + children: createExtensionInput([coreExtensionData.reactElement]), + }, + output: [coreExtensionData.reactElement], + factory: ({ apis, inputs }) => { + return [ + coreExtensionData.reactElement( + + + {inputs.children.map(i => ( + + {i.get(coreExtensionData.reactElement)} + + ))} + + , + ), + ]; + }, + }), + createExtension({ + name: 'child', + attachTo: { id: 'a/parent', input: 'children' }, + output: [coreExtensionData.reactElement], + factory: () => { + const Component = () => { + const link = useRouteRef(extRouteRef); + return
link: {link?.() ?? 'none'}
; + }; + return [coreExtensionData.reactElement()]; + }, + }), + ], + }); + const pluginB = createFrontendPlugin({ + id: 'b', + routes: { + root: routeRef, + }, + extensions: [ + createExtension({ + name: 'child', + attachTo: { id: 'a/parent', input: 'children' }, + output: [ + coreExtensionData.reactElement, + coreExtensionData.routePath, + coreExtensionData.routeRef, + ], + factory: () => { + return [ + coreExtensionData.reactElement(
), + coreExtensionData.routePath('/test'), + coreExtensionData.routeRef(routeRef), + ]; + }, + }), + ], + }); + + render( + createSpecializedApp({ + features: [pluginA, pluginB], + bindRoutes({ bind }) { + bind(pluginA.externalRoutes, { ext: pluginB.routes.root }); + }, + }).createRoot(), + ); + + expect(screen.getByText('link: /test')).toBeInTheDocument(); + }); +}); diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx new file mode 100644 index 0000000000..27f1d52822 --- /dev/null +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -0,0 +1,317 @@ +/* + * Copyright 2023 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, { JSX } from 'react'; +import { ConfigReader } from '@backstage/config'; +import { + ApiBlueprint, + AppTree, + AppTreeApi, + appTreeApiRef, + coreExtensionData, + RouteRef, + ExternalRouteRef, + SubRouteRef, + AnyRouteRefParams, + RouteFunc, + RouteResolutionApiResolveOptions, + RouteResolutionApi, + createApiFactory, + routeResolutionApiRef, +} from '@backstage/frontend-plugin-api'; +import { + AnyApiFactory, + ApiHolder, + ConfigApi, + configApiRef, + featureFlagsApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api'; + +// TODO: Get rid of all of these + +import { + createApp as _createApp, + CreateAppFeatureLoader as _CreateAppFeatureLoader, +} from '@backstage/frontend-defaults'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; + +import { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode'; + +import { CreateAppRouteBinder } from '../routing'; +import { RouteResolver } from '../routing/RouteResolver'; +import { resolveRouteBindings } from '../routing/resolveRouteBindings'; +import { collectRouteIds } from '../routing/collectRouteIds'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { + toInternalFrontendPlugin, + isInternalFrontendPlugin, +} from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { + toInternalFrontendModule, + isInternalFrontendModule, +} from '../../../frontend-plugin-api/src/wiring/createFrontendModule'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; +import { getBasePath } from '../routing/getBasePath'; +import { Root } from '../extensions/Root'; +import { resolveAppTree } from '../tree/resolveAppTree'; +import { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs'; +import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig'; +import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; +import { BackstageRouteObject } from '../routing/types'; +import { FrontendFeature } from './types'; + +function deduplicateFeatures( + allFeatures: FrontendFeature[], +): FrontendFeature[] { + // Start by removing duplicates by reference + const features = Array.from(new Set(allFeatures)); + + // Plugins are deduplicated by ID, last one wins + const seenIds = new Set(); + return features + .reverse() + .filter(feature => { + if (!isInternalFrontendPlugin(feature)) { + return true; + } + if (seenIds.has(feature.id)) { + return false; + } + seenIds.add(feature.id); + return true; + }) + .reverse(); +} + +// Helps delay callers from reaching out to the API before the app tree has been materialized +class AppTreeApiProxy implements AppTreeApi { + #safeToUse: boolean = false; + + constructor(private readonly tree: AppTree) {} + + getTree() { + if (!this.#safeToUse) { + throw new Error( + `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`, + ); + } + return { tree: this.tree }; + } + + initialize() { + this.#safeToUse = true; + } +} + +// Helps delay callers from reaching out to the API before the app tree has been materialized +class RouteResolutionApiProxy implements RouteResolutionApi { + #delegate: RouteResolutionApi | undefined; + #routeObjects: BackstageRouteObject[] | undefined; + + constructor( + private readonly tree: AppTree, + private readonly routeBindings: Map< + ExternalRouteRef, + RouteRef | SubRouteRef + >, + private readonly basePath: string, + ) {} + + resolve( + anyRouteRef: + | RouteRef + | SubRouteRef + | ExternalRouteRef, + options?: RouteResolutionApiResolveOptions, + ): RouteFunc | undefined { + if (!this.#delegate) { + throw new Error( + `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`, + ); + } + + return this.#delegate.resolve(anyRouteRef, options); + } + + initialize() { + const routeInfo = extractRouteInfoFromAppNode(this.tree.root); + + this.#delegate = new RouteResolver( + routeInfo.routePaths, + routeInfo.routeParents, + routeInfo.routeObjects, + this.routeBindings, + this.basePath, + ); + this.#routeObjects = routeInfo.routeObjects; + + return routeInfo; + } + + getRouteObjects() { + return this.#routeObjects; + } +} + +/** + * @public + * @deprecated Import from `@backstage/frontend-defaults` instead. + */ +export const createApp = _createApp; + +/** + * @public + * @deprecated Import from `@backstage/frontend-defaults` instead. + */ +export type CreateAppFeatureLoader = _CreateAppFeatureLoader; + +/** + * Creates an empty app without any default features. This is a low-level API is + * intended for use in tests or specialized setups. Typically wou want to use + * `createApp` from `@backstage/frontend-defaults` instead. + * + * @public + */ +export function createSpecializedApp(options?: { + features?: FrontendFeature[]; + config?: ConfigApi; + bindRoutes?(context: { bind: CreateAppRouteBinder }): void; +}): { createRoot(): JSX.Element } { + const config = options?.config ?? new ConfigReader({}, 'empty-config'); + const features = deduplicateFeatures(options?.features ?? []); + + const tree = resolveAppTree( + 'root', + resolveAppNodeSpecs({ + features, + builtinExtensions: [resolveExtensionDefinition(Root)], + parameters: readAppExtensionsConfig(config), + forbidden: new Set(['root']), + }), + ); + + const factories = createApiFactories({ tree }); + const appTreeApi = new AppTreeApiProxy(tree); + const routeResolutionApi = new RouteResolutionApiProxy( + tree, + resolveRouteBindings( + options?.bindRoutes, + config, + collectRouteIds(features), + ), + getBasePath(config), + ); + + const appIdentityProxy = new AppIdentityProxy(); + const apiHolder = createApiHolder({ + factories, + staticFactories: [ + createApiFactory(appTreeApiRef, appTreeApi), + createApiFactory(configApiRef, config), + createApiFactory(routeResolutionApiRef, routeResolutionApi), + createApiFactory(identityApiRef, appIdentityProxy), + ], + }); + + const featureFlagApi = apiHolder.get(featureFlagsApiRef); + if (featureFlagApi) { + for (const feature of features) { + if (isInternalFrontendPlugin(feature)) { + toInternalFrontendPlugin(feature).featureFlags.forEach(flag => + featureFlagApi.registerFlag({ + name: flag.name, + pluginId: feature.id, + }), + ); + } + if (isInternalFrontendModule(feature)) { + toInternalFrontendModule(feature).featureFlags.forEach(flag => + featureFlagApi.registerFlag({ + name: flag.name, + pluginId: feature.pluginId, + }), + ); + } + if (feature.$$type === '@backstage/ExtensionOverrides') { + toInternalExtensionOverrides(feature).featureFlags.forEach(flag => + featureFlagApi.registerFlag({ name: flag.name, pluginId: '' }), + ); + } + } + } + + // Now instantiate the entire tree, which will skip anything that's already been instantiated + instantiateAppNodeTree(tree.root, apiHolder); + + routeResolutionApi.initialize(); + appTreeApi.initialize(); + + const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement); + + const AppComponent = () => rootEl; + + return { + createRoot() { + return ; + }, + }; +} + +function createApiFactories(options: { tree: AppTree }): AnyApiFactory[] { + const emptyApiHolder = ApiRegistry.from([]); + const factories = new Array(); + + for (const apiNode of options.tree.root.edges.attachments.get('apis') ?? []) { + instantiateAppNodeTree(apiNode, emptyApiHolder); + const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory); + if (!apiFactory) { + throw new Error( + `No API factory found in for extension ${apiNode.spec.id}`, + ); + } + factories.push(apiFactory); + } + + return factories; +} + +function createApiHolder(options: { + factories: AnyApiFactory[]; + staticFactories: AnyApiFactory[]; +}): ApiHolder { + const factoryRegistry = new ApiFactoryRegistry(); + + for (const factory of options.factories) { + factoryRegistry.register('default', factory); + } + + for (const factory of options.staticFactories) { + factoryRegistry.register('static', factory); + } + + ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis()); + + return new ApiResolver(factoryRegistry); +} diff --git a/packages/frontend-app-api/src/wiring/index.ts b/packages/frontend-app-api/src/wiring/index.ts index d1498514c8..d76055f63b 100644 --- a/packages/frontend-app-api/src/wiring/index.ts +++ b/packages/frontend-app-api/src/wiring/index.ts @@ -18,4 +18,5 @@ export { createApp, createSpecializedApp, type CreateAppFeatureLoader, -} from './createApp'; +} from './createSpecializedApp'; +export * from './types'; diff --git a/packages/app/src/plugins.ts b/packages/frontend-app-api/src/wiring/types.ts similarity index 59% rename from packages/app/src/plugins.ts rename to packages/frontend-app-api/src/wiring/types.ts index 9280536997..fb887534e3 100644 --- a/packages/app/src/plugins.ts +++ b/packages/frontend-app-api/src/wiring/types.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2024 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. @@ -13,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { FrontendModule, FrontendPlugin } from '@backstage/frontend-plugin-api'; -// TODO(Rugvip): This plugin is currently not part of the app element tree, -// ideally we have an API for the context menu that permits that. -export { homePlugin } from '@backstage/plugin-home'; -export { signalsPlugin } from '@backstage/plugin-signals'; +/** @public */ +export type FrontendFeature = + | FrontendPlugin + | FrontendModule + // TODO(blam): This is just forwards backwards compatibility, remove after v1.31.0 + | { $$type: '@backstage/ExtensionOverrides' } + | { $$type: '@backstage/BackstagePlugin' }; diff --git a/packages/backend-tasks/.eslintrc.js b/packages/frontend-defaults/.eslintrc.js similarity index 100% rename from packages/backend-tasks/.eslintrc.js rename to packages/frontend-defaults/.eslintrc.js diff --git a/packages/frontend-defaults/README.md b/packages/frontend-defaults/README.md new file mode 100644 index 0000000000..2cca17ee85 --- /dev/null +++ b/packages/frontend-defaults/README.md @@ -0,0 +1,9 @@ +# @backstage/frontend-defaults + +**The [new frontend system](https://backstage.io/docs/frontend-system/) that this package is part of is in alpha, and we do not yet recommend using it for production deployments** + +This package provides the high-level APIs used to create Backstage frontend applications with the default setup. For more information, see the [documentation on how to build Backstage frontend applications](https://backstage.io/docs/frontend-system/building-apps/index). + +## Documentation + +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/frontend-defaults/api-report.md b/packages/frontend-defaults/api-report.md new file mode 100644 index 0000000000..f1ef153e43 --- /dev/null +++ b/packages/frontend-defaults/api-report.md @@ -0,0 +1,31 @@ +## API Report File for "@backstage/frontend-defaults" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ConfigApi } from '@backstage/frontend-plugin-api'; +import { CreateAppRouteBinder } from '@backstage/frontend-app-api'; +import { FrontendFeature } from '@backstage/frontend-app-api'; +import { JSX as JSX_2 } from 'react'; +import { ReactNode } from 'react'; + +// @public +export function createApp(options?: { + features?: (FrontendFeature | CreateAppFeatureLoader)[]; + configLoader?: () => Promise<{ + config: ConfigApi; + }>; + bindRoutes?(context: { bind: CreateAppRouteBinder }): void; + loadingComponent?: ReactNode; +}): { + createRoot(): JSX_2.Element; +}; + +// @public +export interface CreateAppFeatureLoader { + getLoaderName(): string; + load(options: { config: ConfigApi }): Promise<{ + features: FrontendFeature[]; + }>; +} +``` diff --git a/packages/frontend-defaults/catalog-info.yaml b/packages/frontend-defaults/catalog-info.yaml new file mode 100644 index 0000000000..cd0ac9b79a --- /dev/null +++ b/packages/frontend-defaults/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-frontend-defaults + title: '@backstage/frontend-defaults' +spec: + lifecycle: experimental + type: backstage-web-library + owner: maintainers diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json new file mode 100644 index 0000000000..c70bb42424 --- /dev/null +++ b/packages/frontend-defaults/package.json @@ -0,0 +1,51 @@ +{ + "name": "@backstage/frontend-defaults", + "version": "0.0.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" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/frontend-defaults" + }, + "backstage": { + "role": "web-library" + }, + "sideEffects": false, + "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" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^15.0.0" + }, + "files": [ + "dist" + ], + "dependencies": { + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/frontend-app-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-app": "workspace:^", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + } +} diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx similarity index 60% rename from packages/frontend-app-api/src/wiring/createApp.test.tsx rename to packages/frontend-defaults/src/createApp.test.tsx index c79d8eed76..1954554816 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-defaults/src/createApp.test.tsx @@ -19,16 +19,17 @@ import { appTreeApiRef, coreExtensionData, createExtension, - createExtensionOverrides, PageBlueprint, createFrontendPlugin, ThemeBlueprint, + createFrontendModule, } from '@backstage/frontend-plugin-api'; import { screen, waitFor } from '@testing-library/react'; import { CreateAppFeatureLoader, createApp } from './createApp'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import React from 'react'; import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api'; +import appPlugin from '@backstage/plugin-app'; describe('createApp', () => { it('should allow themes to be installed', async () => { @@ -136,7 +137,7 @@ describe('createApp', () => { configLoader: async () => ({ config: new MockConfigApi({ key: 'config-value' }), }), - features: [loader], + features: [appPlugin, loader], }); await renderWithEffects(app.createRoot()); @@ -174,6 +175,13 @@ describe('createApp', () => { const app = createApp({ configLoader: async () => ({ config: new MockConfigApi({}) }), features: [ + appPlugin.withOverrides({ + extensions: [ + appPlugin + .getExtension('app/root') + .override({ disabled: true, factory: orig => orig() }), + ], + }), createFrontendPlugin({ id: 'test', featureFlags: [{ name: 'test-1' }], @@ -200,18 +208,10 @@ describe('createApp', () => { }), ], }), - createExtensionOverrides({ + createFrontendPlugin({ + id: 'other', featureFlags: [{ name: 'test-2' }], - extensions: [ - createExtension({ - namespace: 'app', - name: 'root', - attachTo: { id: 'app', input: 'root' }, - disabled: true, - output: [], - factory: () => [], - }), - ], + extensions: [], }), ], }); @@ -219,7 +219,7 @@ describe('createApp', () => { await renderWithEffects(app.createRoot()); await expect( - screen.findByText("Flags: test-1 from 'test', test-2 from ''"), + screen.findByText("Flags: test-1 from 'test', test-2 from 'other'"), ).resolves.toBeInTheDocument(); }); @@ -255,59 +255,71 @@ describe('createApp', () => { const { tree } = appTreeApi!.getTree(); expect(String(tree.root)).toMatchInlineSnapshot(` - " - root [ - - children [ - - content [ - - routes [ - - ] - - ] - nav [ - - ] - - ] - elements [ - - - ] - - ] - components [ - - - - ] - themes [ - - - ] + " apis [ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + themes [ + + + ] + + + components [ + + + + ] + + + + ] - " + app [ + + root [ + + children [ + + nav [ + + ] + content [ + + routes [ + + ] + + ] + + ] + elements [ + + + ] + + ] + + ] + " `); }); @@ -342,4 +354,80 @@ describe('createApp', () => { expect(screen.queryByText('Custom loading message')).toBeNull(); }); + + it('should allow overriding the app plugin', async () => { + const app = createApp({ + configLoader: () => new Promise(() => {}), + features: [ + appPlugin.withOverrides({ + extensions: [ + appPlugin.getExtension('app/root').override({ + factory: () => [ + coreExtensionData.reactElement( +
Custom app root element
, + ), + ], + }), + ], + }), + ], + }); + + await renderWithEffects(app.createRoot()); + + expect(screen.queryByText('Custom app root element')).toBeNull(); + }); + + describe('modules', () => { + it('should be able to override extensions with a plugin extension override', async () => { + const mod = createFrontendModule({ + pluginId: 'app', + extensions: [ + appPlugin.getExtension('app/root').override({ + factory: () => [ + coreExtensionData.reactElement( +
Custom app root element
, + ), + ], + }), + ], + }); + + const app = createApp({ + configLoader: () => new Promise(() => {}), + features: [mod], + }); + + await renderWithEffects(app.createRoot()); + + expect(screen.queryByText('Custom app root element')).toBeNull(); + }); + + it('should be able to override extensions with a standalone extension override', async () => { + const mod = createFrontendModule({ + pluginId: 'app', + extensions: [ + createExtension({ + name: 'root', + attachTo: { id: 'app', input: 'root' }, + output: [coreExtensionData.reactElement], + factory: () => [ + coreExtensionData.reactElement( +
Custom app root element
, + ), + ], + }), + ], + }); + + const app = createApp({ + configLoader: () => new Promise(() => {}), + features: [mod], + }); + + await renderWithEffects(app.createRoot()); + + expect(screen.queryByText('Custom app root element')).toBeNull(); + }); + }); }); diff --git a/packages/frontend-defaults/src/createApp.tsx b/packages/frontend-defaults/src/createApp.tsx new file mode 100644 index 0000000000..688e01534a --- /dev/null +++ b/packages/frontend-defaults/src/createApp.tsx @@ -0,0 +1,122 @@ +/* + * Copyright 2024 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, { JSX, ReactNode } from 'react'; +import { ConfigApi } from '@backstage/frontend-plugin-api'; +import { stringifyError } from '@backstage/errors'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseUrlConfigs'; +import { getAvailableFeatures } from './discovery'; +import { ConfigReader } from '@backstage/config'; +import appPlugin from '@backstage/plugin-app'; +import { + CreateAppRouteBinder, + FrontendFeature, + createSpecializedApp, +} from '@backstage/frontend-app-api'; + +/** + * A source of dynamically loaded frontend features. + * + * @public + */ +export interface CreateAppFeatureLoader { + /** + * Returns name of this loader. suitable for showing to users. + */ + getLoaderName(): string; + + /** + * Loads a number of features dynamically. + */ + load(options: { config: ConfigApi }): Promise<{ + features: FrontendFeature[]; + }>; +} + +/** + * Creates a new Backstage frontend app instance. See https://backstage.io/docs/frontend-system/building-apps/index + * + * @public + */ +export function createApp(options?: { + features?: (FrontendFeature | CreateAppFeatureLoader)[]; + configLoader?: () => Promise<{ config: ConfigApi }>; + bindRoutes?(context: { bind: CreateAppRouteBinder }): void; + /** + * The component to render while loading the app (waiting for config, features, etc) + * + * Is the text "Loading..." by default. + * If set to "null" then no loading fallback component is rendered. * + */ + loadingComponent?: ReactNode; +}): { + createRoot(): JSX.Element; +} { + let suspenseFallback = options?.loadingComponent; + if (suspenseFallback === undefined) { + suspenseFallback = 'Loading...'; + } + + async function appLoader() { + const config = + (await options?.configLoader?.().then(c => c.config)) ?? + ConfigReader.fromConfigs( + overrideBaseUrlConfigs(defaultConfigLoaderSync()), + ); + + const discoveredFeatures = getAvailableFeatures(config); + + const providedFeatures: FrontendFeature[] = []; + for (const entry of options?.features ?? []) { + if ('load' in entry) { + try { + const result = await entry.load({ config }); + providedFeatures.push(...result.features); + } catch (e) { + throw new Error( + `Failed to read frontend features from loader '${entry.getLoaderName()}', ${stringifyError( + e, + )}`, + ); + } + } else { + providedFeatures.push(entry); + } + } + + const app = createSpecializedApp({ + config, + features: [appPlugin, ...discoveredFeatures, ...providedFeatures], + bindRoutes: options?.bindRoutes, + }).createRoot(); + + return { default: () => app }; + } + + return { + createRoot() { + const LazyApp = React.lazy(appLoader); + return ( + + + + ); + }, + }; +} diff --git a/packages/frontend-app-api/src/wiring/discovery.test.ts b/packages/frontend-defaults/src/discovery.test.ts similarity index 100% rename from packages/frontend-app-api/src/wiring/discovery.test.ts rename to packages/frontend-defaults/src/discovery.test.ts diff --git a/packages/frontend-app-api/src/wiring/discovery.ts b/packages/frontend-defaults/src/discovery.ts similarity index 89% rename from packages/frontend-app-api/src/wiring/discovery.ts rename to packages/frontend-defaults/src/discovery.ts index 4e50290350..e04960dc48 100644 --- a/packages/frontend-app-api/src/wiring/discovery.ts +++ b/packages/frontend-defaults/src/discovery.ts @@ -15,7 +15,7 @@ */ import { Config, ConfigReader } from '@backstage/config'; -import { FrontendFeature } from '@backstage/frontend-plugin-api'; +import { FrontendFeature } from '@backstage/frontend-app-api'; interface DiscoveryGlobal { modules: Array<{ name: string; export?: string; default: unknown }>; @@ -53,7 +53,7 @@ function readPackageDetectionConfig(config: Config) { } /** - * @public + * @internal */ export function getAvailableFeatures(config: Config): FrontendFeature[] { const discovered = ( @@ -84,6 +84,10 @@ export function getAvailableFeatures(config: Config): FrontendFeature[] { function isBackstageFeature(obj: unknown): obj is FrontendFeature { if (obj !== null && typeof obj === 'object' && '$$type' in obj) { return ( + obj.$$type === '@backstage/FrontendPlugin' || + obj.$$type === '@backstage/FrontendModule' || + // TODO: Remove this once the old plugin type and extension overrides + // are no longer supported obj.$$type === '@backstage/BackstagePlugin' || obj.$$type === '@backstage/ExtensionOverrides' ); diff --git a/packages/frontend-defaults/src/index.ts b/packages/frontend-defaults/src/index.ts new file mode 100644 index 0000000000..913cd3934b --- /dev/null +++ b/packages/frontend-defaults/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2024 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. + */ + +/** + * APIs for creating Backstage apps with a default setup. + * + * @packageDocumentation + */ + +export { createApp, type CreateAppFeatureLoader } from './createApp'; diff --git a/packages/backend-app-api/src/services/implementations/index.ts b/packages/frontend-defaults/src/setupTests.ts similarity index 84% rename from packages/backend-app-api/src/services/implementations/index.ts rename to packages/frontend-defaults/src/setupTests.ts index 5f9bb9479a..91af6695ac 100644 --- a/packages/backend-app-api/src/services/implementations/index.ts +++ b/packages/frontend-defaults/src/setupTests.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Backstage Authors + * Copyright 2024 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. @@ -14,5 +14,4 @@ * limitations under the License. */ -export * from './identity'; -export * from './tokenManager'; +import '@testing-library/jest-dom'; diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index cce56f0855..ff8776070b 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/frontend-plugin-api +## 0.8.0-next.0 + +### Minor Changes + +- 5446061: **BREAKING**: Removed support for "v1" extensions. This means that it is no longer possible to declare inputs and outputs as objects when using `createExtension`. In addition, all extension creators except for `createComponentExtension` have been removed, use the equivalent blueprint instead. See the [1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations/#130) for more information on this change. +- fec8b57: **BREAKING**: Updated the type parameters for `ExtensionDefinition` and `ExtensionBlueprint` to only have a single object parameter. The base type parameter is exported as `ExtensionDefinitionParameters` and `ExtensionBlueprintParameters` respectively. This is shipped as an immediate breaking change as we expect usage of these types to be rare, and it does not affect the runtime behavior of the API. + + This is a breaking change as it changes the type parameters. Existing usage can generally be updated as follows: + + - `ExtensionDefinition` -> `ExtensionDefinition` + - `ExtensionDefinition` -> `ExtensionDefinition` + - `ExtensionDefinition` -> `ExtensionDefinition<{ config: TConfig }>` + - `ExtensionDefinition` -> `ExtensionDefinition<{ config: TConfig, configInput: TConfigInput }>` + + If you need to infer the parameter you can use `ExtensionDefinitionParameters`, for example: + + ```ts + import { + ExtensionDefinition, + ExtensionDefinitionParameters, + } from '@backstage/frontend-plugin-api'; + + function myUtility( + ext: ExtensionDefinition, + ): T['config'] { + // ... + } + ``` + + The same patterns apply to `ExtensionBlueprint`. + + This change is made to improve the readability of API references and ability to evolve the type parameters in the future. + +### Patch Changes + +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. +- f3a2b91: Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the `app` extension to the respective API extensions. For example, extensions created with `ThemeBlueprint` are now attached to the `themes` input of `api:app-theme` rather than the `app` extension. +- 98850de: Added support for defining `replaces` in `createExtensionInput` which will allow extensions to redirect missing `attachTo` points to an input of the created extension. + + ```ts + export const AppThemeApi = ApiBlueprint.makeWithOverrides({ + name: 'app-theme', + inputs: { + themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { + // attachTo: { id: 'app', input: 'themes'} will be redirected to this input instead + replaces: [{ id: 'app', input: 'themes' }], + }), + }, + factory: () { + ... + } + }); + ``` + +- 4a66456: A new `apis` parameter has been added to `factory` for extensions. This is a way to access utility APIs without being coupled to the React context. +- Updated dependencies + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 0.7.0 ### Minor Changes diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 384c14fb17..b42efddffa 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -172,27 +172,25 @@ export type AnyRoutes = { }; // @public -export const ApiBlueprint: ExtensionBlueprint< - { - kind: 'api'; - namespace: undefined; - name: undefined; - }, - { +export const ApiBlueprint: ExtensionBlueprint<{ + kind: 'api'; + namespace: undefined; + name: undefined; + params: { factory: AnyApiFactory; - }, - ConfigurableExtensionDataRef, - {}, - {}, - {}, - { + }; + output: ConfigurableExtensionDataRef; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { factory: ConfigurableExtensionDataRef< AnyApiFactory, 'core.api.factory', {} >; - } ->; + }; +}>; export { ApiFactory }; @@ -246,43 +244,39 @@ export interface AppNodeSpec { } // @public -export const AppRootElementBlueprint: ExtensionBlueprint< - { - kind: 'app-root-element'; - namespace: undefined; - name: undefined; - }, - { +export const AppRootElementBlueprint: ExtensionBlueprint<{ + kind: 'app-root-element'; + namespace: undefined; + name: undefined; + params: { element: JSX.Element | (() => JSX.Element); - }, - ConfigurableExtensionDataRef, - {}, - {}, - {}, - never ->; + }; + output: ConfigurableExtensionDataRef; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: never; +}>; // @public -export const AppRootWrapperBlueprint: ExtensionBlueprint< - { - kind: 'app-root-wrapper'; - namespace: undefined; - name: undefined; - }, - { +export const AppRootWrapperBlueprint: ExtensionBlueprint<{ + kind: 'app-root-wrapper'; + namespace: undefined; + name: undefined; + params: { Component: ComponentType>; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< React_2.ComponentType<{ children?: React_2.ReactNode; }>, 'app.root.wrapper', {} - >, - {}, - {}, - {}, - { + >; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { component: ConfigurableExtensionDataRef< React_2.ComponentType<{ children?: React_2.ReactNode; @@ -290,8 +284,8 @@ export const AppRootWrapperBlueprint: ExtensionBlueprint< 'app.root.wrapper', {} >; - } ->; + }; +}>; export { AppTheme }; @@ -326,29 +320,14 @@ export { BackstageIdentityApi }; export { BackstageIdentityResponse }; -// @public (undocumented) -export interface BackstagePlugin< +// @public @deprecated (undocumented) +export type BackstagePlugin< TRoutes extends AnyRoutes = AnyRoutes, TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, TExtensionMap extends { - [id in string]: ExtensionDefinition; + [id in string]: ExtensionDefinition; } = {}, -> { - // (undocumented) - readonly $$type: '@backstage/BackstagePlugin'; - // (undocumented) - readonly externalRoutes: TExternalRoutes; - // (undocumented) - getExtension(id: TId): TExtensionMap[TId]; - // (undocumented) - readonly id: string; - // (undocumented) - readonly routes: TRoutes; - // (undocumented) - withOverrides(options: { - extensions: Array>; - }): BackstagePlugin; -} +> = FrontendPlugin; export { BackstageUserIdentity }; @@ -454,22 +433,18 @@ export function createComponentExtension(options: { | { sync: () => ComponentType; }; -}): ExtensionDefinition< - { - [x: string]: any; - }, - { - [x: string]: any; - }, - ConfigurableExtensionDataRef< +}): ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { ref: ComponentRef; impl: ComponentType; }, 'core.component.component', {} - >, - { + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -477,13 +452,11 @@ export function createComponentExtension(options: { singleton: boolean; } >; - }, - { - kind: 'component'; - namespace: string; - name: string; - } ->; + }; + kind: 'component'; + namespace: string; + name: string; +}>; // @public (undocumented) export namespace createComponentExtension { @@ -532,27 +505,29 @@ export function createExtension< TConfigSchema, UFactoryOutput >, -): ExtensionDefinition< - { - [key in keyof TConfigSchema]: z.infer>; - }, - z.input< - z.ZodObject<{ - [key in keyof TConfigSchema]: ReturnType; - }> - >, - UOutput, - TInputs, - { - kind: string | undefined extends TKind ? undefined : TKind; - namespace: string | undefined extends TNamespace ? undefined : TNamespace; - name: string | undefined extends TName ? undefined : TName; - } ->; +): ExtensionDefinition<{ + config: string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + output: UOutput; + inputs: TInputs; + kind: string | undefined extends TKind ? undefined : TKind; + namespace: string | undefined extends TNamespace ? undefined : TNamespace; + name: string | undefined extends TName ? undefined : TName; +}>; // @public export function createExtensionBlueprint< - TParams, + TParams extends object, UOutput extends AnyExtensionDataRef, TInputs extends { [inputName in string]: ExtensionInput< @@ -585,29 +560,27 @@ export function createExtensionBlueprint< UFactoryOutput, TDataRefs >, -): ExtensionBlueprint< - { - kind: TKind; - namespace: TNamespace; - name: TName; - }, - TParams, - UOutput, - string extends keyof TInputs ? {} : TInputs, - string extends keyof TConfigSchema +): ExtensionBlueprint<{ + kind: TKind; + namespace: TNamespace; + name: TName; + params: TParams; + output: UOutput; + inputs: string extends keyof TInputs ? {} : TInputs; + config: string extends keyof TConfigSchema ? {} : { [key in keyof TConfigSchema]: z.infer>; - }, - string extends keyof TConfigSchema + }; + configInput: string extends keyof TConfigSchema ? {} : z.input< z.ZodObject<{ [key in keyof TConfigSchema]: ReturnType; }> - >, - TDataRefs ->; + >; + dataRefs: TDataRefs; +}>; // @public (undocumented) export type CreateExtensionBlueprintOptions< @@ -650,6 +623,7 @@ export type CreateExtensionBlueprintOptions< params: TParams, context: { node: AppNode; + apis: ApiHolder; config: { [key in keyof TConfigSchema]: z.infer>; }; @@ -686,7 +660,12 @@ export function createExtensionInput< }, >( extensionData: Array, - config?: TConfig, + config?: TConfig & { + replaces?: Array<{ + id: string; + input: string; + }>; + }, ): ExtensionInput< UExtensionData, { @@ -730,6 +709,7 @@ export type CreateExtensionOptions< }; factory(context: { node: AppNode; + apis: ApiHolder; config: { [key in keyof TConfigSchema]: z.infer>; }; @@ -737,7 +717,7 @@ export type CreateExtensionOptions< }): Iterable; } & VerifyExtensionFactoryOutput; -// @public (undocumented) +// @public @deprecated (undocumented) export function createExtensionOverrides( options: ExtensionOverridesOptions, ): ExtensionOverrides; @@ -765,15 +745,34 @@ export function createExternalRouteRef< } >; +// @public (undocumented) +export function createFrontendModule< + TId extends string, + TExtensions extends readonly ExtensionDefinition[] = [], +>(options: CreateFrontendModuleOptions): FrontendModule; + +// @public (undocumented) +export interface CreateFrontendModuleOptions< + TPluginId extends string, + TExtensions extends readonly ExtensionDefinition[], +> { + // (undocumented) + extensions?: TExtensions; + // (undocumented) + featureFlags?: FeatureFlagConfig[]; + // (undocumented) + pluginId: TPluginId; +} + // @public (undocumented) export function createFrontendPlugin< TId extends string, TRoutes extends AnyRoutes = {}, TExternalRoutes extends AnyExternalRoutes = {}, - TExtensions extends readonly ExtensionDefinition[] = [], + TExtensions extends readonly ExtensionDefinition[] = [], >( options: PluginOptions, -): BackstagePlugin< +): FrontendPlugin< TRoutes, TExternalRoutes, { @@ -853,34 +852,10 @@ export interface Extension { // @public (undocumented) export interface ExtensionBlueprint< - TIdParts extends { - kind: string; - namespace?: string; - name?: string; - }, - TParams, - UOutput extends AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; - }, - TConfig extends { - [key in string]: unknown; - }, - TConfigInput extends { - [key in string]: unknown; - }, - TDataRefs extends { - [name in string]: AnyExtensionDataRef; - }, + T extends ExtensionBlueprintParameters = ExtensionBlueprintParameters, > { // (undocumented) - dataRefs: TDataRefs; + dataRefs: T['dataRefs']; // (undocumented) make< TNewNamespace extends string | undefined, @@ -893,20 +868,18 @@ export interface ExtensionBlueprint< input: string; }; disabled?: boolean; - params: TParams; - }): ExtensionDefinition< - TConfig, - TConfigInput, - UOutput, - TInputs, - { - kind: TIdParts['kind']; - namespace: string | undefined extends TNewNamespace - ? TIdParts['namespace'] - : TNewNamespace; - name: string | undefined extends TNewName ? TIdParts['name'] : TNewName; - } - >; + params: T['params']; + }): ExtensionDefinition<{ + kind: T['kind']; + namespace: string | undefined extends TNewNamespace + ? T['namespace'] + : TNewNamespace; + name: string | undefined extends TNewName ? T['name'] : TNewName; + config: T['config']; + configInput: T['configInput']; + output: T['output']; + inputs: T['inputs']; + }>; makeWithOverrides< TNewNamespace extends string | undefined, TNewName extends string | undefined, @@ -933,64 +906,97 @@ export interface ExtensionBlueprint< }; disabled?: boolean; inputs?: TExtraInputs & { - [KName in keyof TInputs]?: `Error: Input '${KName & + [KName in keyof T['inputs']]?: `Error: Input '${KName & string}' is already defined in parent definition`; }; output?: Array; config?: { schema: TExtensionConfigSchema & { - [KName in keyof TConfig]?: `Error: Config key '${KName & + [KName in keyof T['config']]?: `Error: Config key '${KName & string}' is already defined in parent schema`; }; }; factory( originalFactory: ( - params: TParams, + params: T['params'], context?: { - config?: TConfig; - inputs?: ResolveInputValueOverrides; + config?: T['config']; + inputs?: ResolveInputValueOverrides>; }, - ) => ExtensionDataContainer, + ) => ExtensionDataContainer>, context: { node: AppNode; - config: TConfig & { + apis: ApiHolder; + config: T['config'] & { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; }; - inputs: Expand>; + inputs: Expand>; }, ): Iterable & VerifyExtensionFactoryOutput< - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, UFactoryOutput >; - }): ExtensionDefinition< - { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; - } & TConfig, - z.input< - z.ZodObject<{ - [key in keyof TExtensionConfigSchema]: ReturnType< - TExtensionConfigSchema[key] - >; - }> - > & - TConfigInput, - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, - TInputs & TExtraInputs, - { - kind: TIdParts['kind']; - namespace: string | undefined extends TNewNamespace - ? TIdParts['namespace'] - : TNewNamespace; - name: string | undefined extends TNewName ? TIdParts['name'] : TNewName; - } - >; + }): ExtensionDefinition<{ + config: (string extends keyof TExtensionConfigSchema + ? {} + : { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }) & + T['config']; + configInput: (string extends keyof TExtensionConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + >) & + T['configInput']; + output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput; + inputs: T['inputs'] & TExtraInputs; + kind: T['kind']; + namespace: string | undefined extends TNewNamespace + ? T['namespace'] + : TNewNamespace; + name: string | undefined extends TNewName ? T['name'] : TNewName; + }>; } +// @public (undocumented) +export type ExtensionBlueprintParameters = { + kind: string; + namespace?: string; + name?: string; + params?: object; + configInput?: { + [K in string]: any; + }; + config?: { + [K in string]: any; + }; + output?: AnyExtensionDataRef; + inputs?: { + [KName in string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }; + dataRefs?: { + [name in string]: AnyExtensionDataRef; + }; +}; + // @public (undocumented) export function ExtensionBoundary( props: ExtensionBoundaryProps, @@ -1064,47 +1070,11 @@ export type ExtensionDataValue = { }; // @public (undocumented) -export interface ExtensionDefinition< - TConfig, - TConfigInput = TConfig, - UOutput extends AnyExtensionDataRef = AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; - } = {}, - TIdParts extends { - kind?: string; - namespace?: string; - name?: string; - } = { - kind?: string; - namespace?: string; - name?: string; - }, -> { - // (undocumented) +export type ExtensionDefinition< + T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters, +> = { $$type: '@backstage/ExtensionDefinition'; - // (undocumented) - readonly attachTo: { - id: string; - input: string; - }; - // (undocumented) - readonly configSchema?: PortableSchema; - // (undocumented) - readonly disabled: boolean; - // (undocumented) - readonly kind?: TIdParts['kind']; - // (undocumented) - readonly name?: TIdParts['name']; - // (undocumented) - readonly namespace?: TIdParts['namespace']; - // (undocumented) + readonly T: T; override< TExtensionConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType; @@ -1128,58 +1098,86 @@ export interface ExtensionDefinition< }; disabled?: boolean; inputs?: TExtraInputs & { - [KName in keyof TInputs]?: `Error: Input '${KName & + [KName in keyof T['inputs']]?: `Error: Input '${KName & string}' is already defined in parent definition`; }; output?: Array; config?: { schema: TExtensionConfigSchema & { - [KName in keyof TConfig]?: `Error: Config key '${KName & + [KName in keyof T['config']]?: `Error: Config key '${KName & string}' is already defined in parent schema`; }; }; factory( originalFactory: (context?: { - config?: TConfig; - inputs?: ResolveInputValueOverrides; - }) => ExtensionDataContainer, + config?: T['config']; + inputs?: ResolveInputValueOverrides>; + }) => ExtensionDataContainer>, context: { node: AppNode; - config: TConfig & { + apis: ApiHolder; + config: T['config'] & { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; }; - inputs: Expand>; + inputs: Expand>; }, ): Iterable; } & VerifyExtensionFactoryOutput< - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, UFactoryOutput >, - ): ExtensionDefinition< - { + ): ExtensionDefinition<{ + kind: T['kind']; + namespace: T['namespace']; + name: T['name']; + output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput; + inputs: T['inputs'] & TExtraInputs; + config: T['config'] & { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; - } & TConfig, - z.input< - z.ZodObject<{ - [key in keyof TExtensionConfigSchema]: ReturnType< - TExtensionConfigSchema[key] - >; - }> - > & - TConfigInput, - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, - TInputs & TExtraInputs, - TIdParts - >; -} + }; + configInput: T['configInput'] & + z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + >; + }>; +}; + +// @public (undocumented) +export type ExtensionDefinitionParameters = { + kind?: string; + namespace?: string; + name?: string; + configInput?: { + [K in string]: any; + }; + config?: { + [K in string]: any; + }; + output?: AnyExtensionDataRef; + inputs?: { + [KName in string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }; +}; // @public (undocumented) export interface ExtensionInput< - TExtensionData extends ExtensionDataRef< + UExtensionData extends ExtensionDataRef< unknown, string, { @@ -1196,7 +1194,12 @@ export interface ExtensionInput< // (undocumented) config: TConfig; // (undocumented) - extensionData: Array; + extensionData: Array; + // (undocumented) + replaces?: Array<{ + id: string; + input: string; + }>; } // @public (undocumented) @@ -1205,10 +1208,10 @@ export interface ExtensionOverrides { readonly $$type: '@backstage/ExtensionOverrides'; } -// @public (undocumented) +// @public @deprecated (undocumented) export interface ExtensionOverridesOptions { // (undocumented) - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; // (undocumented) featureFlags?: FeatureFlagConfig[]; } @@ -1242,8 +1245,40 @@ export { FetchApi }; export { fetchApiRef }; +// @public @deprecated (undocumented) +export type FrontendFeature = FrontendPlugin | ExtensionOverrides; + // @public (undocumented) -export type FrontendFeature = BackstagePlugin | ExtensionOverrides; +export interface FrontendModule { + // (undocumented) + readonly $$type: '@backstage/FrontendModule'; + // (undocumented) + readonly pluginId: string; +} + +// @public (undocumented) +export interface FrontendPlugin< + TRoutes extends AnyRoutes = AnyRoutes, + TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, + TExtensionMap extends { + [id in string]: ExtensionDefinition; + } = {}, +> { + // (undocumented) + readonly $$type: '@backstage/FrontendPlugin'; + // (undocumented) + readonly externalRoutes: TExternalRoutes; + // (undocumented) + getExtension(id: TId): TExtensionMap[TId]; + // (undocumented) + readonly id: string; + // (undocumented) + readonly routes: TRoutes; + // (undocumented) + withOverrides(options: { + extensions: Array; + }): FrontendPlugin; +} export { githubAuthApiRef }; @@ -1252,34 +1287,32 @@ export { gitlabAuthApiRef }; export { googleAuthApiRef }; // @public (undocumented) -export const IconBundleBlueprint: ExtensionBlueprint< - { - kind: 'icon-bundle'; - namespace: 'app'; - name: undefined; - }, - { +export const IconBundleBlueprint: ExtensionBlueprint<{ + kind: 'icon-bundle'; + namespace: 'app'; + name: undefined; + params: { icons: { [x: string]: IconComponent; }; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< { [x: string]: IconComponent; }, 'core.icons', {} - >, - {}, - { + >; + inputs: {}; + config: { icons: string; test: string; - }, - { + }; + configInput: { test: string; icons?: string | undefined; - }, - { + }; + dataRefs: { icons: ConfigurableExtensionDataRef< { [x: string]: IconComponent; @@ -1287,8 +1320,8 @@ export const IconBundleBlueprint: ExtensionBlueprint< 'core.icons', {} >; - } ->; + }; +}>; // @public export type IconComponent = ComponentType< @@ -1318,18 +1351,16 @@ export { identityApiRef }; export { microsoftAuthApiRef }; // @public -export const NavItemBlueprint: ExtensionBlueprint< - { - kind: 'nav-item'; - namespace: undefined; - name: undefined; - }, - { +export const NavItemBlueprint: ExtensionBlueprint<{ + kind: 'nav-item'; + namespace: undefined; + name: undefined; + params: { title: string; icon: IconComponent_2; routeRef: RouteRef; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< { title: string; icon: IconComponent_2; @@ -1337,11 +1368,11 @@ export const NavItemBlueprint: ExtensionBlueprint< }, 'core.nav-item.target', {} - >, - {}, - {}, - {}, - { + >; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { target: ConfigurableExtensionDataRef< { title: string; @@ -1351,32 +1382,30 @@ export const NavItemBlueprint: ExtensionBlueprint< 'core.nav-item.target', {} >; - } ->; + }; +}>; // @public -export const NavLogoBlueprint: ExtensionBlueprint< - { - kind: 'nav-logo'; - namespace: undefined; - name: undefined; - }, - { +export const NavLogoBlueprint: ExtensionBlueprint<{ + kind: 'nav-logo'; + namespace: undefined; + name: undefined; + params: { logoIcon: JSX.Element; logoFull: JSX.Element; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< { logoIcon?: JSX.Element | undefined; logoFull?: JSX.Element | undefined; }, 'core.nav-logo.logo-elements', {} - >, - {}, - {}, - {}, - { + >; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { logoElements: ConfigurableExtensionDataRef< { logoIcon?: JSX.Element | undefined; @@ -1385,8 +1414,8 @@ export const NavLogoBlueprint: ExtensionBlueprint< 'core.nav-logo.logo-elements', {} >; - } ->; + }; +}>; export { OAuthApi }; @@ -1407,35 +1436,34 @@ export { oneloginAuthApiRef }; export { OpenIdConnectApi }; // @public -export const PageBlueprint: ExtensionBlueprint< - { - kind: 'page'; - namespace: undefined; - name: undefined; - }, - { +export const PageBlueprint: ExtensionBlueprint<{ + kind: 'page'; + namespace: undefined; + name: undefined; + params: { defaultPath: string; loader: () => Promise; routeRef?: RouteRef | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - never ->; + }; + dataRefs: never; +}>; export { PendingOAuthRequest }; @@ -1444,7 +1472,7 @@ export interface PluginOptions< TId extends string, TRoutes extends AnyRoutes, TExternalRoutes extends AnyExternalRoutes, - TExtensions extends readonly ExtensionDefinition[], + TExtensions extends readonly ExtensionDefinition[], > { // (undocumented) extensions?: TExtensions; @@ -1565,26 +1593,24 @@ export type RouteFunc = ( ) => string; // @public (undocumented) -export const RouterBlueprint: ExtensionBlueprint< - { - kind: 'app-router-component'; - namespace: undefined; - name: undefined; - }, - { +export const RouterBlueprint: ExtensionBlueprint<{ + kind: 'app-router-component'; + namespace: undefined; + name: undefined; + params: { Component: ComponentType>; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< ComponentType<{ children?: ReactNode; }>, 'app.router.wrapper', {} - >, - {}, - {}, - {}, - { + >; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { component: ConfigurableExtensionDataRef< ComponentType<{ children?: ReactNode; @@ -1592,8 +1618,8 @@ export const RouterBlueprint: ExtensionBlueprint< 'app.router.wrapper', {} >; - } ->; + }; +}>; // @public export interface RouteRef< @@ -1630,31 +1656,29 @@ export { SessionApi }; export { SessionState }; // @public -export const SignInPageBlueprint: ExtensionBlueprint< - { - kind: 'sign-in-page'; - namespace: undefined; - name: undefined; - }, - { +export const SignInPageBlueprint: ExtensionBlueprint<{ + kind: 'sign-in-page'; + namespace: undefined; + name: undefined; + params: { loader: () => Promise>; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< React_2.ComponentType, 'core.sign-in-page.component', {} - >, - {}, - {}, - {}, - { + >; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { component: ConfigurableExtensionDataRef< React_2.ComponentType, 'core.sign-in-page.component', {} >; - } ->; + }; +}>; export { StorageApi }; @@ -1675,35 +1699,31 @@ export interface SubRouteRef< } // @public -export const ThemeBlueprint: ExtensionBlueprint< - { - kind: 'theme'; - namespace: 'app'; - name: undefined; - }, - { +export const ThemeBlueprint: ExtensionBlueprint<{ + kind: 'theme'; + namespace: 'app'; + name: undefined; + params: { theme: AppTheme; - }, - ConfigurableExtensionDataRef, - {}, - {}, - {}, - { + }; + output: ConfigurableExtensionDataRef; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { theme: ConfigurableExtensionDataRef; - } ->; + }; +}>; // @public -export const TranslationBlueprint: ExtensionBlueprint< - { - kind: 'translation'; - namespace: undefined; - name: undefined; - }, - { +export const TranslationBlueprint: ExtensionBlueprint<{ + kind: 'translation'; + namespace: undefined; + name: undefined; + params: { resource: TranslationResource | TranslationMessages; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< | TranslationResource | TranslationMessages< string, @@ -1714,11 +1734,11 @@ export const TranslationBlueprint: ExtensionBlueprint< >, 'core.translation.translation', {} - >, - {}, - {}, - {}, - { + >; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { translation: ConfigurableExtensionDataRef< | TranslationResource | TranslationMessages< @@ -1731,8 +1751,8 @@ export const TranslationBlueprint: ExtensionBlueprint< 'core.translation.translation', {} >; - } ->; + }; +}>; export { TranslationMessages }; diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index c76bae094e..540a8b54fe 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.7.0", + "version": "0.8.0-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts index c9dd7811b6..800eb5d7b1 100644 --- a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts @@ -37,8 +37,9 @@ describe('ApiBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { - "id": "app", + "id": "root", "input": "apis", }, "configSchema": undefined, @@ -86,8 +87,9 @@ describe('ApiBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { - "id": "app", + "id": "root", "input": "apis", }, "configSchema": { @@ -116,6 +118,7 @@ describe('ApiBlueprint', () => { "extensionData": [ [Function], ], + "replaces": undefined, }, }, "kind": "api", diff --git a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.ts index 73eec80b81..1f28a3cf54 100644 --- a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.ts @@ -28,7 +28,7 @@ const factoryDataRef = createExtensionDataRef().with({ */ export const ApiBlueprint = createExtensionBlueprint({ kind: 'api', - attachTo: { id: 'app', input: 'apis' }, + attachTo: { id: 'root', input: 'apis' }, output: [factoryDataRef], dataRefs: { factory: factoryDataRef, diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootElementBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/AppRootElementBlueprint.test.tsx index e76bed8155..0f4d0b2092 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootElementBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/AppRootElementBlueprint.test.tsx @@ -27,6 +27,7 @@ describe('AppRootElementBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/root", "input": "elements", diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx index 5cbb96fbbf..cda0085fec 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx @@ -16,14 +16,13 @@ import React from 'react'; import { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint'; -import { createExtensionTester } from '@backstage/frontend-test-utils'; -import { PageBlueprint } from './PageBlueprint'; -import { waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import { coreExtensionData, createExtension, createExtensionInput, } from '../wiring'; +import { renderInTestApp } from '@backstage/frontend-test-utils'; describe('AppRootWrapperBlueprint', () => { it('should return an extension with sensible defaults', () => { @@ -36,6 +35,7 @@ describe('AppRootWrapperBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/root", "input": "wrappers", @@ -59,28 +59,19 @@ describe('AppRootWrapperBlueprint', () => { it('should render the simple component wrapper', async () => { const extension = AppRootWrapperBlueprint.make({ + name: 'test', params: { Component: () =>
Hello
, }, }); - const { getByText } = createExtensionTester( - PageBlueprint.make({ - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ) - .add(extension) - .render(); + renderInTestApp(
, { extensions: [extension] }); - await waitFor(() => expect(getByText('Hello')).toBeInTheDocument()); + await waitFor(() => expect(screen.getByText('Hello')).toBeInTheDocument()); }); it('should render the complex component wrapper', async () => { const extension = AppRootWrapperBlueprint.makeWithOverrides({ - namespace: 'ns', name: 'test', config: { schema: { @@ -104,28 +95,30 @@ describe('AppRootWrapperBlueprint', () => { }, }); - const { getByText, getByTestId } = createExtensionTester( - PageBlueprint.make({ - params: { - defaultPath: '/', - loader: async () =>
Hi
, - }, - }), - ) - .add(extension, { config: { name: 'Robin' } }) - .add( + renderInTestApp(
, { + extensions: [ + extension, createExtension({ - attachTo: { id: 'app-root-wrapper:ns/test', input: 'children' }, + name: 'test-child', + attachTo: { id: 'app-root-wrapper:test', input: 'children' }, output: [coreExtensionData.reactElement], factory: () => [coreExtensionData.reactElement(
Its Me
)], }), - ) - .render(); + ], + config: { + app: { + extensions: [ + { + 'app-root-wrapper:test': { config: { name: 'Robin' } }, + }, + ], + }, + }, + }); await waitFor(() => { - expect(getByText('Hi')).toBeInTheDocument(); - expect(getByTestId('Robin-1')).toBeInTheDocument(); - expect(getByText('Its Me')).toBeInTheDocument(); + expect(screen.getByTestId('Robin-1')).toBeInTheDocument(); + expect(screen.getByText('Its Me')).toBeInTheDocument(); }); }); }); diff --git a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts index 52d6e2482d..369ac052f5 100644 --- a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts @@ -25,7 +25,7 @@ const iconsDataRef = createExtensionDataRef<{ export const IconBundleBlueprint = createExtensionBlueprint({ kind: 'icon-bundle', namespace: 'app', - attachTo: { id: 'app', input: 'icons' }, + attachTo: { id: 'api:app/icons', input: 'icons' }, output: [iconsDataRef], config: { schema: { diff --git a/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx index daa21f88bb..ddde2ff47d 100644 --- a/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/NavItemBlueprint.test.tsx @@ -33,6 +33,7 @@ describe('NavItemBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/nav", "input": "items", diff --git a/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx index 621dca72ba..a08dda1201 100644 --- a/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/NavLogoBlueprint.test.tsx @@ -30,6 +30,7 @@ describe('NavLogoBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/nav", "input": "logos", diff --git a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx index dedc5454bd..c586ed8c04 100644 --- a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.test.tsx @@ -43,6 +43,7 @@ describe('PageBlueprint', () => { expect(myPage).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/routes", "input": "routes", @@ -103,7 +104,7 @@ describe('PageBlueprint', () => { expect(tester.get(coreExtensionData.routeRef)).toBe(mockRouteRef); - const { getByTestId } = tester.render(); + const { getByTestId } = renderInTestApp(tester.reactElement()); await waitFor(() => expect(getByTestId('test')).toBeInTheDocument()); }); diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index fe40aaa8d7..3062d6d9de 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -17,15 +17,12 @@ import React from 'react'; import { RouterBlueprint } from './RouterBlueprint'; import { MemoryRouter } from 'react-router-dom'; import { render, waitFor } from '@testing-library/react'; -import { createSpecializedApp } from '@backstage/frontend-app-api'; import { coreExtensionData, createExtension, createExtensionInput, - createExtensionOverrides, } from '../wiring'; -import { MockConfigApi } from '@backstage/test-utils'; -import { PageBlueprint } from './PageBlueprint'; +import { createExtensionTester } from '@backstage/frontend-test-utils'; describe('RouterBlueprint', () => { it('should return an extension when calling make with sensible defaults', () => { @@ -38,6 +35,7 @@ describe('RouterBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/root", "input": "router", @@ -71,24 +69,14 @@ describe('RouterBlueprint', () => { }, }); - const app = createSpecializedApp({ - features: [ - createExtensionOverrides({ - extensions: [ - extension, - PageBlueprint.make({ - namespace: 'test', - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ], - }), - ], - }); + const tester = createExtensionTester(extension); + const Component = tester.get(RouterBlueprint.dataRefs.component); - const { getByTestId } = render(app.createRoot()); + const { getByTestId } = render( + +
+ , + ); await waitFor(() => { expect(getByTestId('test-contents')).toBeInTheDocument(); @@ -123,44 +111,28 @@ describe('RouterBlueprint', () => { }, }); - const app = createSpecializedApp({ - features: [ - createExtensionOverrides({ - extensions: [ - extension, - createExtension({ - namespace: 'test', - attachTo: { - id: 'app-router-component:test/test', - input: 'children', - }, - output: [coreExtensionData.reactElement], - *factory() { - yield coreExtensionData.reactElement(
); - }, - }), - PageBlueprint.make({ - namespace: 'test', - params: { - defaultPath: '/', - loader: async () =>
, - }, - }), - ], - }), - ], - config: new MockConfigApi({ - app: { - extensions: [ - { - 'app-router-component:test/test': { config: { name: 'Robin' } }, - }, - ], + const tester = createExtensionTester(extension, { + config: { name: 'Robin' }, + }).add( + createExtension({ + namespace: 'test', + attachTo: { + id: 'app-router-component:test/test', + input: 'children', + }, + output: [coreExtensionData.reactElement], + *factory() { + yield coreExtensionData.reactElement(
); }, }), - }); + ); + const Component = tester.get(RouterBlueprint.dataRefs.component); - const { getByTestId } = render(app.createRoot()); + const { getByTestId } = render( + +
+ , + ); await waitFor(() => { expect(getByTestId('test-contents')).toBeInTheDocument(); diff --git a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx index e6389da574..2637873930 100644 --- a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx @@ -31,6 +31,7 @@ describe('SignInPageBlueprint', () => { ).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "app/root", "input": "signInPage", diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts index e6daf510eb..6d3f6280a3 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts @@ -31,8 +31,9 @@ describe('ThemeBlueprint', () => { .toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { - "id": "app", + "id": "api:app/app-theme", "input": "themes", }, "configSchema": undefined, diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts index a4f550595f..ca33970125 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts @@ -29,7 +29,7 @@ const themeDataRef = createExtensionDataRef().with({ export const ThemeBlueprint = createExtensionBlueprint({ kind: 'theme', namespace: 'app', - attachTo: { id: 'app', input: 'themes' }, + attachTo: { id: 'api:app/app-theme', input: 'themes' }, output: [themeDataRef], dataRefs: { theme: themeDataRef, diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts index 3c7b271322..6bccd68c53 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts @@ -46,8 +46,9 @@ describe('TranslationBlueprint', () => { ).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { - "id": "app", + "id": "api:app/translations", "input": "translations", }, "configSchema": undefined, diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts index 7b01d0b28f..dd4a9ddc09 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts @@ -28,7 +28,7 @@ const translationDataRef = createExtensionDataRef< */ export const TranslationBlueprint = createExtensionBlueprint({ kind: 'translation', - attachTo: { id: 'app', input: 'translations' }, + attachTo: { id: 'api:app/translations', input: 'translations' }, output: [translationDataRef], dataRefs: { translation: translationDataRef, diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx index 403d591db1..3e260f0c9b 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx @@ -23,14 +23,12 @@ import { } from '@backstage/test-utils'; import { ExtensionBoundary } from './ExtensionBoundary'; import { coreExtensionData, createExtension } from '../wiring'; -import { - analyticsApiRef, - createApiFactory, - useAnalytics, -} from '@backstage/core-plugin-api'; +import { analyticsApiRef, useAnalytics } from '@backstage/core-plugin-api'; import { createRouteRef } from '../routing'; -import { createExtensionTester } from '@backstage/frontend-test-utils'; -import { ApiBlueprint } from '../blueprints'; +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; const wrapInBoundaryExtension = (element?: JSX.Element) => { const routeRef = createRouteRef(); @@ -60,7 +58,11 @@ describe('ExtensionBoundary', () => { const TextComponent = () => { return

{text}

; }; - createExtensionTester(wrapInBoundaryExtension()).render(); + renderInTestApp( + createExtensionTester( + wrapInBoundaryExtension(), + ).reactElement(), + ); await waitFor(() => expect(screen.getByText(text)).toBeInTheDocument()); }); @@ -70,9 +72,11 @@ describe('ExtensionBoundary', () => { throw new Error(errorMsg); }; const { error } = await withLogCollector(['error'], async () => { - createExtensionTester( - wrapInBoundaryExtension(), - ).render(); + renderInTestApp( + createExtensionTester( + wrapInBoundaryExtension(), + ).reactElement(), + ); await waitFor(() => expect(screen.getByText(errorMsg)).toBeInTheDocument(), ); @@ -99,13 +103,13 @@ describe('ExtensionBoundary', () => { return null; }; - createExtensionTester( - wrapInBoundaryExtension( - - - , - ), - ).render(); + renderInTestApp( + + {createExtensionTester( + wrapInBoundaryExtension(), + ).reactElement()} + , + ); await waitFor(() => { const event = analyticsApiMock @@ -122,8 +126,9 @@ describe('ExtensionBoundary', () => { }); }); - // TODO(Rugvip): It's annoying to test the inverse of this currently, because the extension tester overrides the subject to always output a path - it('should emit analytics events if routable', async () => { + // TODO(Rugvip): Need a way to be able to override APIs in the app to be able to test this properly + // eslint-disable-next-line jest/no-disabled-tests + it.skip('should emit analytics events if routable', async () => { const Emitter = () => { const analytics = useAnalytics(); useEffect(() => { @@ -134,16 +139,12 @@ describe('ExtensionBoundary', () => { const analyticsApiMock = new MockAnalyticsApi(); await act(async () => { - createExtensionTester(wrapInBoundaryExtension()) - .add( - ApiBlueprint.make({ - namespace: analyticsApiRef.id, - params: { - factory: createApiFactory(analyticsApiRef, analyticsApiMock), - }, - }), - ) - .render(); + renderInTestApp( + createExtensionTester( + wrapInBoundaryExtension(), + ).reactElement(), + // { apis: [[analyticsApiRef, analyticsApiMock]] }, + ); }); expect(analyticsApiMock.getEvents()).toEqual([ diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx index 9561636d22..c8fa907bc8 100644 --- a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -35,7 +35,7 @@ export function createComponentExtension(options: { kind: 'component', namespace: options.ref.id, name: options.name, - attachTo: { id: 'app', input: 'components' }, + attachTo: { id: 'api:app/components', input: 'components' }, disabled: options.disabled, output: [createComponentExtension.componentDataRef], factory() { diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 5853f28004..68478a756e 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -18,6 +18,7 @@ import { createExtensionTester } from '@backstage/frontend-test-utils'; import { createExtension } from './createExtension'; import { createExtensionDataRef } from './createExtensionDataRef'; import { createExtensionInput } from './createExtensionInput'; +import { PortableSchema } from '../schema'; const stringDataRef = createExtensionDataRef().with({ id: 'string' }); const numberDataRef = createExtensionDataRef().with({ id: 'number' }); @@ -289,7 +290,12 @@ describe('createExtension', () => { ); expect( - extension.configSchema?.parse({ + ( + (extension as any).configSchema as PortableSchema< + (typeof extension.T)['config'], + (typeof extension.T)['configInput'] + > + )?.parse({ foo: 'x', bar: 'y', baz: 'z', @@ -302,7 +308,12 @@ describe('createExtension', () => { baz: 'z', }); expect( - extension.configSchema?.parse({ + ( + (extension as any).configSchema as PortableSchema< + (typeof extension.T)['config'], + (typeof extension.T)['configInput'] + > + )?.parse({ foo: 'x', }), ).toEqual({ diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 0c89ff1567..445bbd8a68 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { AppNode } from '../apis'; +import { ApiHolder, AppNode } from '../apis'; import { PortableSchema } from '../schema'; import { Expand } from '../types'; import { @@ -133,6 +133,7 @@ export type CreateExtensionOptions< }; factory(context: { node: AppNode; + apis: ApiHolder; config: { [key in keyof TConfigSchema]: z.infer>; }; @@ -141,33 +142,27 @@ export type CreateExtensionOptions< } & VerifyExtensionFactoryOutput; /** @public */ -export interface ExtensionDefinition< - TConfig, - TConfigInput = TConfig, - UOutput extends AnyExtensionDataRef = AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< +export type ExtensionDefinitionParameters = { + kind?: string; + namespace?: string; + name?: string; + configInput?: { [K in string]: any }; + config?: { [K in string]: any }; + output?: AnyExtensionDataRef; + inputs?: { + [KName in string]: ExtensionInput< AnyExtensionDataRef, { optional: boolean; singleton: boolean } >; - } = {}, - TIdParts extends { - kind?: string; - namespace?: string; - name?: string; - } = { - kind?: string; - namespace?: string; - name?: string; - }, -> { + }; +}; + +/** @public */ +export type ExtensionDefinition< + T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters, +> = { $$type: '@backstage/ExtensionDefinition'; - readonly kind?: TIdParts['kind']; - readonly namespace?: TIdParts['namespace']; - readonly name?: TIdParts['name']; - readonly attachTo: { id: string; input: string }; - readonly disabled: boolean; - readonly configSchema?: PortableSchema; + readonly T: T; override< TExtensionConfigSchema extends { @@ -186,77 +181,71 @@ export interface ExtensionDefinition< attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TExtraInputs & { - [KName in keyof TInputs]?: `Error: Input '${KName & + [KName in keyof T['inputs']]?: `Error: Input '${KName & string}' is already defined in parent definition`; }; output?: Array; config?: { schema: TExtensionConfigSchema & { - [KName in keyof TConfig]?: `Error: Config key '${KName & + [KName in keyof T['config']]?: `Error: Config key '${KName & string}' is already defined in parent schema`; }; }; factory( originalFactory: (context?: { - config?: TConfig; - inputs?: ResolveInputValueOverrides; - }) => ExtensionDataContainer, + config?: T['config']; + inputs?: ResolveInputValueOverrides>; + }) => ExtensionDataContainer>, context: { node: AppNode; - config: TConfig & { + apis: ApiHolder; + config: T['config'] & { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; }; - inputs: Expand>; + inputs: Expand>; }, ): Iterable; } & VerifyExtensionFactoryOutput< - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, UFactoryOutput >, - ): ExtensionDefinition< - { + ): ExtensionDefinition<{ + kind: T['kind']; + namespace: T['namespace']; + name: T['name']; + output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput; + inputs: T['inputs'] & TExtraInputs; + config: T['config'] & { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; - } & TConfig, - z.input< - z.ZodObject<{ - [key in keyof TExtensionConfigSchema]: ReturnType< - TExtensionConfigSchema[key] - >; - }> - > & - TConfigInput, - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, - TInputs & TExtraInputs, - TIdParts - >; -} + }; + configInput: T['configInput'] & + z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + >; + }>; +}; /** @internal */ export type InternalExtensionDefinition< - TConfig, - TConfigInput = TConfig, - UOutput extends AnyExtensionDataRef = AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - } = {}, - TIdParts extends { - kind?: string; - namespace?: string; - name?: string; - } = { - kind?: string; - namespace?: string; - name?: string; - }, -> = ExtensionDefinition & - ( + T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters, +> = ExtensionDefinition & { + readonly kind?: string; + readonly namespace?: string; + readonly name?: string; + readonly attachTo: { id: string; input: string }; + readonly disabled: boolean; + readonly configSchema?: PortableSchema; +} & ( | { readonly version: 'v1'; readonly inputs: { @@ -273,7 +262,8 @@ export type InternalExtensionDefinition< }; factory(context: { node: AppNode; - config: TConfig; + apis: ApiHolder; + config: object; inputs: { [inputName in string]: unknown; }; @@ -292,7 +282,8 @@ export type InternalExtensionDefinition< readonly output: Array; factory(context: { node: AppNode; - config: TConfig; + apis: ApiHolder; + config: object; inputs: ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput< AnyExtensionDataRef, @@ -304,13 +295,10 @@ export type InternalExtensionDefinition< ); /** @internal */ -export function toInternalExtensionDefinition( - overrides: ExtensionDefinition, -): InternalExtensionDefinition { - const internal = overrides as InternalExtensionDefinition< - TConfig, - TConfigInput - >; +export function toInternalExtensionDefinition< + T extends ExtensionDefinitionParameters, +>(overrides: ExtensionDefinition): InternalExtensionDefinition { + const internal = overrides as InternalExtensionDefinition; if (internal.$$type !== '@backstage/ExtensionDefinition') { throw new Error( `Invalid extension definition instance, bad type '${internal.$$type}'`, @@ -349,67 +337,45 @@ export function createExtension< TConfigSchema, UFactoryOutput >, -): ExtensionDefinition< - { - [key in keyof TConfigSchema]: z.infer>; - }, - z.input< - z.ZodObject<{ - [key in keyof TConfigSchema]: ReturnType; - }> - >, - UOutput, - TInputs, - { - kind: string | undefined extends TKind ? undefined : TKind; - namespace: string | undefined extends TNamespace ? undefined : TNamespace; - name: string | undefined extends TName ? undefined : TName; - } ->; -export function createExtension< - const TKind extends string | undefined, - const TNamespace extends string | undefined, - const TName extends string | undefined, - UOutput extends AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, - TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, - UFactoryOutput extends ExtensionDataValue, ->( - options: CreateExtensionOptions< - TKind, - TNamespace, - TName, - UOutput, - TInputs, - TConfigSchema, - UFactoryOutput - >, -): ExtensionDefinition< - string extends keyof TConfigSchema +): ExtensionDefinition<{ + config: string extends keyof TConfigSchema ? {} : { [key in keyof TConfigSchema]: z.infer>; - }, - string extends keyof TConfigSchema + }; + configInput: string extends keyof TConfigSchema ? {} : z.input< z.ZodObject<{ [key in keyof TConfigSchema]: ReturnType; }> - >, - UOutput, - TInputs, - { - kind: TKind; - namespace: TNamespace; - name: TName; - } -> { + >; + output: UOutput; + inputs: TInputs; + kind: string | undefined extends TKind ? undefined : TKind; + namespace: string | undefined extends TNamespace ? undefined : TNamespace; + name: string | undefined extends TName ? undefined : TName; +}> { + type T = { + config: string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + output: UOutput; + inputs: TInputs; + kind: string | undefined extends TKind ? undefined : TKind; + namespace: string | undefined extends TNamespace ? undefined : TNamespace; + name: string | undefined extends TName ? undefined : TName; + }; + const schemaDeclaration = options.config?.schema; const configSchema = schemaDeclaration && @@ -424,6 +390,7 @@ export function createExtension< return { $$type: '@backstage/ExtensionDefinition', version: 'v2', + T: undefined as unknown as T, kind: options.kind, namespace: options.namespace, name: options.name, @@ -447,76 +414,7 @@ export function createExtension< parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`); return `ExtensionDefinition{${parts.join(',')}}`; }, - override: < - TExtensionConfigSchema extends { - [key in string]: (zImpl: typeof z) => z.ZodType; - }, - UOverrideFactoryOutput extends ExtensionDataValue, - UNewOutput extends AnyExtensionDataRef, - TExtraInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, - >(overrideOptions: { - attachTo?: { id: string; input: string }; - disabled?: boolean; - inputs?: TExtraInputs; - output?: Array; - config?: { - schema: TExtensionConfigSchema; - }; - factory( - originalFactory: (context?: { - config?: { - [key in keyof TConfigSchema]: z.infer< - ReturnType - >; - }; - inputs?: ResolveInputValueOverrides; - }) => ExtensionDataContainer, - context: { - node: AppNode; - config: { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; - } & { - [key in keyof TConfigSchema]: z.infer< - ReturnType - >; - }; - inputs: Expand>; - }, - ): Iterable; - }): ExtensionDefinition< - { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; - } & { - [key in keyof TConfigSchema]: z.infer>; - }, - z.input< - z.ZodObject< - { - [key in keyof TExtensionConfigSchema]: ReturnType< - TExtensionConfigSchema[key] - >; - } & { - [key in keyof TConfigSchema]: ReturnType; - } - > - >, - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, - TInputs & TExtraInputs, - { - kind: TKind; - namespace: TNamespace; - name: TName; - } - > => { + override(overrideOptions) { if (!Array.isArray(options.output)) { throw new Error( 'Cannot override an extension that is not declared using the new format with outputs as an array', @@ -531,12 +429,6 @@ export function createExtension< TConfigSchema, UFactoryOutput >; - const overrideNewConfigSchema = overrideOptions.config?.schema; - - const schema = { - ...newOptions.config?.schema, - ...overrideNewConfigSchema, - } as TConfigSchema & TExtensionConfigSchema; return createExtension({ kind: newOptions.kind, @@ -545,75 +437,61 @@ export function createExtension< attachTo: overrideOptions.attachTo ?? newOptions.attachTo, disabled: overrideOptions.disabled ?? newOptions.disabled, inputs: { ...overrideOptions.inputs, ...newOptions.inputs }, - output: overrideOptions.output ?? newOptions.output, - config: Object.keys(schema).length === 0 ? undefined : { schema }, - factory: ({ node, config, inputs }) => { + output: (overrideOptions.output ?? + newOptions.output) as AnyExtensionDataRef[], + config: + newOptions.config || overrideOptions.config + ? { + schema: { + ...newOptions.config?.schema, + ...overrideOptions.config?.schema, + }, + } + : undefined, + factory: ({ node, apis, config, inputs }) => { if (!overrideOptions.factory) { return newOptions.factory({ node, - config, - inputs: inputs as unknown as Expand< - ResolvedExtensionInputs - >, + apis, + config: config as any, + inputs: inputs as any, }); } const parentResult = overrideOptions.factory( - (innerContext?: { - config?: { - [key in keyof TConfigSchema]: z.infer< - ReturnType - >; - }; - inputs?: ResolveInputValueOverrides; - }): ExtensionDataContainer => { + (innerContext): ExtensionDataContainer => { return createExtensionDataContainer( newOptions.factory({ node, - config: innerContext?.config ?? config, + apis, + config: (innerContext?.config ?? config) as any, inputs: resolveInputOverrides( newOptions.inputs, inputs, innerContext?.inputs, - ) as any, // TODO: Might be able to improve this once legacy inputs are gone + ) as any, }) as Iterable, newOptions.output, ); }, { node, - config, - inputs, + apis, + config: config as any, + inputs: inputs as any, }, ); - const deduplicatedResult = new Map(); + const deduplicatedResult = new Map< + string, + ExtensionDataValue + >(); for (const item of parentResult) { deduplicatedResult.set(item.id, item); } - return deduplicatedResult.values() as Iterable; + return deduplicatedResult.values(); }, - } as CreateExtensionOptions); + }) as ExtensionDefinition; }, - } as InternalExtensionDefinition< - string extends keyof TConfigSchema - ? {} - : { - [key in keyof TConfigSchema]: z.infer>; - }, - string extends keyof TConfigSchema - ? {} - : z.input< - z.ZodObject<{ - [key in keyof TConfigSchema]: ReturnType; - }> - >, - UOutput, - TInputs, - { - kind: TKind; - namespace: TNamespace; - name: TName; - } - >; + } as InternalExtensionDefinition; } diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index f2f17079ca..fc77c63e97 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -17,7 +17,10 @@ import React from 'react'; import { coreExtensionData } from './coreExtensionData'; import { createExtensionBlueprint } from './createExtensionBlueprint'; -import { createExtensionTester } from '@backstage/frontend-test-utils'; +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; import { ExtensionDataValue, createExtensionDataRef, @@ -32,10 +35,7 @@ import { createExtensionDataContainer } from './createExtensionDataContainer'; function unused(..._any: any[]) {} -function factoryOutput( - ext: ExtensionDefinition, - inputs: unknown = undefined, -) { +function factoryOutput(ext: ExtensionDefinition, inputs: unknown = undefined) { const int = toInternalExtensionDefinition(ext); if (int.version !== 'v2') { throw new Error('Expected v2 extension'); @@ -80,7 +80,9 @@ describe('createExtensionBlueprint', () => { version: 'v2', }); - const { container } = createExtensionTester(extension).render(); + const { container } = renderInTestApp( + createExtensionTester(extension).reactElement(), + ); expect(container.querySelector('h1')).toHaveTextContent('Hello, world!'); }); @@ -120,7 +122,9 @@ describe('createExtensionBlueprint', () => { version: 'v2', }); - const { container } = createExtensionTester(extension).render(); + const { container } = renderInTestApp( + createExtensionTester(extension).reactElement(), + ); expect(container.querySelector('h1')).toHaveTextContent('Hello, world!'); }); @@ -145,7 +149,9 @@ describe('createExtensionBlueprint', () => { expect(extension).toBeDefined(); - const { container } = createExtensionTester(extension).render(); + const { container } = renderInTestApp( + createExtensionTester(extension).reactElement(), + ); expect(container.querySelector('h1')).toHaveTextContent('Hello, world!'); }); @@ -216,13 +222,15 @@ describe('createExtensionBlueprint', () => { expect.assertions(4); - createExtensionTester(extension, { - config: { - something: 'something new!', - text: 'Hello, world!', - defaulted: 'lolz', - }, - }).render(); + renderInTestApp( + createExtensionTester(extension, { + config: { + something: 'something new!', + text: 'Hello, world!', + defaulted: 'lolz', + }, + }).reactElement(), + ); }); it('should not allow overlapping config keys', () => { @@ -291,12 +299,14 @@ describe('createExtensionBlueprint', () => { expect.assertions(2); - createExtensionTester(extension, { - config: { - something: 'something new!', - defaulted: 'lolz', - }, - }).render(); + renderInTestApp( + createExtensionTester(extension, { + config: { + something: 'something new!', + defaulted: 'lolz', + }, + }).reactElement(), + ); }); it('should allow getting inputs properly', () => { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index c27893489d..77d078f257 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { AppNode } from '../apis'; +import { ApiHolder, AppNode } from '../apis'; import { Expand } from '../types'; import { - CreateExtensionOptions, ExtensionDefinition, ResolvedExtensionInputs, VerifyExtensionFactoryOutput, @@ -71,6 +70,7 @@ export type CreateExtensionBlueprintOptions< params: TParams, context: { node: AppNode; + apis: ApiHolder; config: { [key in keyof TConfigSchema]: z.infer>; }; @@ -81,28 +81,31 @@ export type CreateExtensionBlueprintOptions< dataRefs?: TDataRefs; } & VerifyExtensionFactoryOutput; +/** @public */ +export type ExtensionBlueprintParameters = { + kind: string; + namespace?: string; + name?: string; + params?: object; + configInput?: { [K in string]: any }; + config?: { [K in string]: any }; + output?: AnyExtensionDataRef; + inputs?: { + [KName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }; + dataRefs?: { [name in string]: AnyExtensionDataRef }; +}; + /** * @public */ export interface ExtensionBlueprint< - TIdParts extends { - kind: string; - namespace?: string; - name?: string; - }, - TParams, - UOutput extends AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, - TConfig extends { [key in string]: unknown }, - TConfigInput extends { [key in string]: unknown }, - TDataRefs extends { [name in string]: AnyExtensionDataRef }, + T extends ExtensionBlueprintParameters = ExtensionBlueprintParameters, > { - dataRefs: TDataRefs; + dataRefs: T['dataRefs']; make< TNewNamespace extends string | undefined, @@ -112,20 +115,18 @@ export interface ExtensionBlueprint< name?: TNewName; attachTo?: { id: string; input: string }; disabled?: boolean; - params: TParams; - }): ExtensionDefinition< - TConfig, - TConfigInput, - UOutput, - TInputs, - { - kind: TIdParts['kind']; - namespace: string | undefined extends TNewNamespace - ? TIdParts['namespace'] - : TNewNamespace; - name: string | undefined extends TNewName ? TIdParts['name'] : TNewName; - } - >; + params: T['params']; + }): ExtensionDefinition<{ + kind: T['kind']; + namespace: string | undefined extends TNewNamespace + ? T['namespace'] + : TNewNamespace; + name: string | undefined extends TNewName ? T['name'] : TNewName; + config: T['config']; + configInput: T['configInput']; + output: T['output']; + inputs: T['inputs']; + }>; /** * Creates a new extension from the blueprint. @@ -153,249 +154,68 @@ export interface ExtensionBlueprint< attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TExtraInputs & { - [KName in keyof TInputs]?: `Error: Input '${KName & + [KName in keyof T['inputs']]?: `Error: Input '${KName & string}' is already defined in parent definition`; }; output?: Array; config?: { schema: TExtensionConfigSchema & { - [KName in keyof TConfig]?: `Error: Config key '${KName & + [KName in keyof T['config']]?: `Error: Config key '${KName & string}' is already defined in parent schema`; }; }; factory( originalFactory: ( - params: TParams, + params: T['params'], context?: { - config?: TConfig; - inputs?: ResolveInputValueOverrides; + config?: T['config']; + inputs?: ResolveInputValueOverrides>; }, - ) => ExtensionDataContainer, + ) => ExtensionDataContainer>, context: { node: AppNode; - config: TConfig & { + apis: ApiHolder; + config: T['config'] & { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; }; - inputs: Expand>; + inputs: Expand>; }, ): Iterable & VerifyExtensionFactoryOutput< - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, UFactoryOutput >; - }): ExtensionDefinition< - { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; - } & TConfig, - z.input< - z.ZodObject<{ - [key in keyof TExtensionConfigSchema]: ReturnType< - TExtensionConfigSchema[key] - >; - }> - > & - TConfigInput, - AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, - TInputs & TExtraInputs, - { - kind: TIdParts['kind']; - namespace: string | undefined extends TNewNamespace - ? TIdParts['namespace'] - : TNewNamespace; - name: string | undefined extends TNewName ? TIdParts['name'] : TNewName; - } - >; -} - -/** - * @internal - */ -class ExtensionBlueprintImpl< - TKind extends string, - TNamespace extends string | undefined, - TName extends string | undefined, - TParams, - UOutput extends AnyExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, - TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, - TDataRefs extends { [name in string]: AnyExtensionDataRef }, -> { - constructor( - private readonly options: CreateExtensionBlueprintOptions< - TKind, - TNamespace, - TName, - TParams, - UOutput, - TInputs, - TConfigSchema, - any, - TDataRefs - >, - ) { - this.dataRefs = options.dataRefs!; - } - - dataRefs: TDataRefs; - - public makeWithOverrides< - TExtensionConfigSchema extends { - [key in string]: (zImpl: typeof z) => z.ZodType; - }, - UFactoryOutput extends ExtensionDataValue, - UNewOutput extends AnyExtensionDataRef, - TExtraInputs extends { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, - TNewNamespace extends string | undefined = undefined, - TNewName extends string | undefined = undefined, - >(args: { - namespace?: TNewNamespace; - name?: TNewName; - attachTo?: { id: string; input: string }; - disabled?: boolean; - inputs?: TExtraInputs; - output?: Array; - config?: { - schema: TExtensionConfigSchema; - }; - factory( - originalFactory: ( - params: TParams, - context?: { - config?: { - [key in keyof TConfigSchema]: z.infer< - ReturnType - >; - }; - inputs?: ResolveInputValueOverrides; - }, - ) => ExtensionDataContainer, - context: { - node: AppNode; - config: { + }): ExtensionDefinition<{ + config: (string extends keyof TExtensionConfigSchema + ? {} + : { [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; - } & { - [key in keyof TConfigSchema]: z.infer>; - }; - inputs: Expand>; - }, - ): Iterable; - }): ExtensionDefinition< - { - [key in keyof TExtensionConfigSchema]: z.infer< - ReturnType - >; - } & { - [key in keyof TConfigSchema]: z.infer>; - }, - z.input< - z.ZodObject< - { - [key in keyof TExtensionConfigSchema]: ReturnType< - TExtensionConfigSchema[key] - >; - } & { - [key in keyof TConfigSchema]: ReturnType; - } - > - > - > { - const schema = { - ...this.options.config?.schema, - ...args.config?.schema, - } as TConfigSchema & TExtensionConfigSchema; - - return createExtension({ - kind: this.options.kind, - namespace: args.namespace ?? this.options.namespace, - name: args.name ?? this.options.name, - attachTo: args.attachTo ?? this.options.attachTo, - disabled: args.disabled ?? this.options.disabled, - inputs: { ...args.inputs, ...this.options.inputs }, - output: args.output ?? this.options.output, - config: Object.keys(schema).length === 0 ? undefined : { schema }, - factory: ({ node, config, inputs }) => { - return args.factory( - ( - innerParams: TParams, - innerContext?: { - config?: { - [key in keyof TConfigSchema]: z.infer< - ReturnType - >; - }; - inputs?: ResolveInputValueOverrides; - }, - ): ExtensionDataContainer => { - return createExtensionDataContainer( - this.options.factory(innerParams, { - node, - config: innerContext?.config ?? config, - inputs: resolveInputOverrides( - this.options.inputs, - inputs, - innerContext?.inputs, - ) as any, // TODO: Might be able to improve this once legacy inputs are gone - }), - this.options.output, - ); - }, - { - node, - config, - inputs, - }, - ); - }, - } as CreateExtensionOptions); - } - - public make< - TNewNamespace extends string | undefined = undefined, - TNewName extends string | undefined = undefined, - >(args: { - namespace?: TNewNamespace; - name?: TNewName; - attachTo?: { id: string; input: string }; - disabled?: boolean; - params: TParams; - }): ExtensionDefinition< - { - [key in keyof TConfigSchema]: z.infer>; - }, - z.input< - z.ZodObject<{ - [key in keyof TConfigSchema]: ReturnType; - }> - > - > { - return createExtension({ - kind: this.options.kind, - namespace: args.namespace ?? this.options.namespace, - name: args.name ?? this.options.name, - attachTo: args.attachTo ?? this.options.attachTo, - disabled: args.disabled ?? this.options.disabled, - inputs: this.options.inputs, - output: this.options.output, - config: this.options.config, - factory: ctx => this.options.factory(args.params, ctx), - } as CreateExtensionOptions); - } + }) & + T['config']; + configInput: (string extends keyof TExtensionConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + >) & + T['configInput']; + output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput; + inputs: T['inputs'] & TExtraInputs; + kind: T['kind']; + namespace: string | undefined extends TNewNamespace + ? T['namespace'] + : TNewNamespace; + name: string | undefined extends TNewName ? T['name'] : TNewName; + }>; } /** @@ -405,7 +225,7 @@ class ExtensionBlueprintImpl< * @public */ export function createExtensionBlueprint< - TParams, + TParams extends object, UOutput extends AnyExtensionDataRef, TInputs extends { [inputName in string]: ExtensionInput< @@ -431,48 +251,107 @@ export function createExtensionBlueprint< UFactoryOutput, TDataRefs >, -): ExtensionBlueprint< - { - kind: TKind; - namespace: TNamespace; - name: TName; - }, - TParams, - UOutput, - string extends keyof TInputs ? {} : TInputs, - string extends keyof TConfigSchema +): ExtensionBlueprint<{ + kind: TKind; + namespace: TNamespace; + name: TName; + params: TParams; + output: UOutput; + inputs: string extends keyof TInputs ? {} : TInputs; + config: string extends keyof TConfigSchema ? {} - : { [key in keyof TConfigSchema]: z.infer> }, - string extends keyof TConfigSchema + : { [key in keyof TConfigSchema]: z.infer> }; + configInput: string extends keyof TConfigSchema ? {} : z.input< z.ZodObject<{ [key in keyof TConfigSchema]: ReturnType; }> - >, - TDataRefs -> { - return new ExtensionBlueprintImpl(options) as ExtensionBlueprint< - { - kind: TKind; - namespace: TNamespace; - name: TName; + >; + dataRefs: TDataRefs; +}> { + return { + dataRefs: options.dataRefs, + make(args) { + return createExtension({ + kind: options.kind, + namespace: args.namespace ?? options.namespace, + name: args.name ?? options.name, + attachTo: args.attachTo ?? options.attachTo, + disabled: args.disabled ?? options.disabled, + inputs: options.inputs, + output: options.output as AnyExtensionDataRef[], + config: options.config, + factory: ctx => + options.factory(args.params, ctx) as Iterable< + ExtensionDataValue + >, + }) as ExtensionDefinition; }, - TParams, - UOutput, - string extends keyof TInputs ? {} : TInputs, - string extends keyof TConfigSchema + makeWithOverrides(args) { + return createExtension({ + kind: options.kind, + namespace: args.namespace ?? options.namespace, + name: args.name ?? options.name, + attachTo: args.attachTo ?? options.attachTo, + disabled: args.disabled ?? options.disabled, + inputs: { ...args.inputs, ...options.inputs }, + output: (args.output ?? options.output) as AnyExtensionDataRef[], + config: + options.config || args.config + ? { + schema: { + ...options.config?.schema, + ...args.config?.schema, + }, + } + : undefined, + factory: ({ node, config, inputs, apis }) => { + return args.factory( + (innerParams, innerContext) => { + return createExtensionDataContainer( + options.factory(innerParams, { + apis, + node, + config: (innerContext?.config ?? config) as any, + inputs: resolveInputOverrides( + options.inputs, + inputs, + innerContext?.inputs, + ) as any, + }) as Iterable, + options.output, + ); + }, + { + apis, + node, + config: config as any, + inputs: inputs as any, + }, + ) as Iterable>; + }, + }) as ExtensionDefinition; + }, + } as ExtensionBlueprint<{ + kind: TKind; + namespace: TNamespace; + name: TName; + params: TParams; + output: UOutput; + inputs: string extends keyof TInputs ? {} : TInputs; + config: string extends keyof TConfigSchema ? {} : { [key in keyof TConfigSchema]: z.infer>; - }, - string extends keyof TConfigSchema + }; + configInput: string extends keyof TConfigSchema ? {} : z.input< z.ZodObject<{ [key in keyof TConfigSchema]: ReturnType; }> - >, - TDataRefs - >; + >; + dataRefs: TDataRefs; + }>; } diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts index 3651eb3ee5..7209e6f647 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts @@ -18,12 +18,13 @@ import { ExtensionDataRef } from './createExtensionDataRef'; /** @public */ export interface ExtensionInput< - TExtensionData extends ExtensionDataRef, + UExtensionData extends ExtensionDataRef, TConfig extends { singleton: boolean; optional: boolean }, > { $$type: '@backstage/ExtensionInput'; - extensionData: Array; + extensionData: Array; config: TConfig; + replaces?: Array<{ id: string; input: string }>; } /** @public */ @@ -32,26 +33,13 @@ export function createExtensionInput< TConfig extends { singleton?: boolean; optional?: boolean }, >( extensionData: Array, - config?: TConfig, + config?: TConfig & { replaces?: Array<{ id: string; input: string }> }, ): ExtensionInput< UExtensionData, { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; } ->; -export function createExtensionInput< - TExtensionData extends ExtensionDataRef, - TConfig extends { singleton?: boolean; optional?: boolean }, ->( - extensionData: Array, - config?: TConfig, -): ExtensionInput< - TExtensionData, - { - singleton: TConfig['singleton'] extends true ? true : false; - optional: TConfig['optional'] extends true ? true : false; - } > { if (process.env.NODE_ENV !== 'production') { if (Array.isArray(extensionData)) { @@ -84,8 +72,9 @@ export function createExtensionInput< ? true : false, }, + replaces: config?.replaces, } as ExtensionInput< - TExtensionData, + UExtensionData, { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts index 2219f963ed..a3595fb168 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts @@ -65,6 +65,7 @@ describe('createExtensionOverrides', () => { "extensions": [ { "$$type": "@backstage/Extension", + "T": undefined, "attachTo": { "id": "app", "input": "apis", @@ -80,6 +81,7 @@ describe('createExtensionOverrides', () => { }, { "$$type": "@backstage/Extension", + "T": undefined, "attachTo": { "id": "app", "input": "apis", @@ -95,6 +97,7 @@ describe('createExtensionOverrides', () => { }, { "$$type": "@backstage/Extension", + "T": undefined, "attachTo": { "id": "app", "input": "apis", diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts index 75c43a36d2..887f9b8dab 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts @@ -21,9 +21,12 @@ import { } from './resolveExtensionDefinition'; import { ExtensionOverrides, FeatureFlagConfig } from './types'; -/** @public */ +/** + * @deprecated Use {@link createFrontendModule} instead. + * @public + */ export interface ExtensionOverridesOptions { - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; featureFlags?: FeatureFlagConfig[]; } @@ -34,7 +37,10 @@ export interface InternalExtensionOverrides extends ExtensionOverrides { readonly featureFlags: FeatureFlagConfig[]; } -/** @public */ +/** + * @deprecated Use {@link createFrontendModule} instead. + * @public + */ export function createExtensionOverrides( options: ExtensionOverridesOptions, ): ExtensionOverrides { diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendModule.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendModule.test.ts new file mode 100644 index 0000000000..d895f6de0f --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/createFrontendModule.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2024 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 { createExtension } from './createExtension'; +import { createFrontendModule } from './createFrontendModule'; + +describe('createFrontendModule', () => { + it('should create a frontend module', () => { + expect( + createFrontendModule({ + pluginId: 'test', + extensions: [ + createExtension({ + kind: 'route', + name: 'test', + output: [], + attachTo: { id: 'ignored', input: 'ignored' }, + factory: () => [], + }), + ], + }), + ).toMatchInlineSnapshot(` + { + "$$type": "@backstage/FrontendModule", + "extensions": [ + { + "$$type": "@backstage/Extension", + "T": undefined, + "attachTo": { + "id": "ignored", + "input": "ignored", + }, + "configSchema": undefined, + "disabled": false, + "factory": [Function], + "id": "route:test/test", + "inputs": {}, + "output": [], + "toString": [Function], + "version": "v2", + }, + ], + "featureFlags": [], + "pluginId": "test", + "toString": [Function], + "version": "v1", + } + `); + }); +}); diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts b/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts new file mode 100644 index 0000000000..aacc34ccf0 --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2023 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 { + ExtensionDefinition, + InternalExtensionDefinition, + toInternalExtensionDefinition, +} from './createExtension'; +import { + Extension, + resolveExtensionDefinition, +} from './resolveExtensionDefinition'; +import { FeatureFlagConfig } from './types'; + +/** @public */ +export interface CreateFrontendModuleOptions< + TPluginId extends string, + TExtensions extends readonly ExtensionDefinition[], +> { + pluginId: TPluginId; + extensions?: TExtensions; + featureFlags?: FeatureFlagConfig[]; +} + +/** @public */ +export interface FrontendModule { + readonly $$type: '@backstage/FrontendModule'; + readonly pluginId: string; +} + +/** @internal */ +export interface InternalFrontendModule extends FrontendModule { + readonly version: 'v1'; + readonly extensions: Extension[]; + readonly featureFlags: FeatureFlagConfig[]; +} + +/** @public */ +export function createFrontendModule< + TId extends string, + TExtensions extends readonly ExtensionDefinition[] = [], +>(options: CreateFrontendModuleOptions): FrontendModule { + const { pluginId } = options; + + const extensions = new Array>(); + const extensionDefinitionsById = new Map< + string, + InternalExtensionDefinition + >(); + + for (const def of options.extensions ?? []) { + const internal = toInternalExtensionDefinition(def); + const ext = resolveExtensionDefinition(def, { namespace: pluginId }); + extensions.push(ext); + extensionDefinitionsById.set(ext.id, { + ...internal, + namespace: pluginId, + }); + } + + if (extensions.length !== extensionDefinitionsById.size) { + const extensionIds = extensions.map(e => e.id); + const duplicates = Array.from( + new Set( + extensionIds.filter((id, index) => extensionIds.indexOf(id) !== index), + ), + ); + // TODO(Rugvip): This could provide some more information about the kind + name of the extensions + throw new Error( + `Plugin '${pluginId}' provided duplicate extensions: ${duplicates.join( + ', ', + )}`, + ); + } + + return { + $$type: '@backstage/FrontendModule', + version: 'v1', + pluginId, + featureFlags: options.featureFlags ?? [], + extensions, + toString() { + return `Module{pluginId=${pluginId}}`; + }, + } as InternalFrontendModule; +} + +/** @internal */ +export function isInternalFrontendModule(opaque: { + $$type: string; +}): opaque is InternalFrontendModule { + if (opaque.$$type === '@backstage/FrontendModule') { + // Make sure we throw if invalid + toInternalFrontendModule(opaque as FrontendModule); + return true; + } + return false; +} + +/** @internal */ +export function toInternalFrontendModule( + plugin: FrontendModule, +): InternalFrontendModule { + const internal = plugin as InternalFrontendModule; + if (internal.$$type !== '@backstage/FrontendModule') { + throw new Error(`Invalid plugin instance, bad type '${internal.$$type}'`); + } + if (internal.version !== 'v1') { + throw new Error( + `Invalid plugin instance, bad version '${internal.version}'`, + ); + } + return internal; +} diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts index 3615ba29e5..c461d6fb20 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts @@ -17,14 +17,13 @@ import React from 'react'; import { createApp } from '@backstage/frontend-app-api'; import { screen } from '@testing-library/react'; -import { createFrontendPlugin } from './createFrontendPlugin'; +import { FrontendPlugin, createFrontendPlugin } from './createFrontendPlugin'; import { JsonObject } from '@backstage/types'; import { createExtension } from './createExtension'; import { createExtensionDataRef } from './createExtensionDataRef'; import { coreExtensionData } from './coreExtensionData'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import { createExtensionInput } from './createExtensionInput'; -import { BackstagePlugin } from './types'; const nameExtensionDataRef = createExtensionDataRef().with({ id: 'name', @@ -123,11 +122,11 @@ function createTestAppRoot({ features, config = {}, }: { - features: BackstagePlugin[]; + features: FrontendPlugin[]; config: JsonObject; }) { return createApp({ - features, + features: [...features], configLoader: async () => ({ config: new MockConfigApi(config) }), }).createRoot(); } @@ -150,6 +149,7 @@ describe('createFrontendPlugin', () => { expect(plugin.getExtension('test/1')).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "test/output", "input": "names", @@ -281,6 +281,7 @@ describe('createFrontendPlugin', () => { expect(plugin.getExtension('test/1')).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "test/output", "input": "names", diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index bab09c631e..03c843543d 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -14,25 +14,49 @@ * limitations under the License. */ -import { ExtensionDefinition } from './createExtension'; +import { + ExtensionDefinition, + InternalExtensionDefinition, + toInternalExtensionDefinition, +} from './createExtension'; import { Extension, ResolveExtensionId, resolveExtensionDefinition, } from './resolveExtensionDefinition'; -import { - AnyExternalRoutes, - AnyRoutes, - BackstagePlugin, - FeatureFlagConfig, -} from './types'; +import { AnyExternalRoutes, AnyRoutes, FeatureFlagConfig } from './types'; +/** @public */ +export interface FrontendPlugin< + TRoutes extends AnyRoutes = AnyRoutes, + TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, + TExtensionMap extends { [id in string]: ExtensionDefinition } = {}, +> { + readonly $$type: '@backstage/FrontendPlugin'; + readonly id: string; + readonly routes: TRoutes; + readonly externalRoutes: TExternalRoutes; + getExtension(id: TId): TExtensionMap[TId]; + withOverrides(options: { + extensions: Array; + }): FrontendPlugin; +} + +/** + * @public + * @deprecated Use {@link FrontendPlugin} instead. + */ +export type BackstagePlugin< + TRoutes extends AnyRoutes = AnyRoutes, + TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, + TExtensionMap extends { [id in string]: ExtensionDefinition } = {}, +> = FrontendPlugin; /** @public */ export interface PluginOptions< TId extends string, TRoutes extends AnyRoutes, TExternalRoutes extends AnyExternalRoutes, - TExtensions extends readonly ExtensionDefinition[], + TExtensions extends readonly ExtensionDefinition[], > { id: TId; routes?: TRoutes; @@ -42,10 +66,10 @@ export interface PluginOptions< } /** @public */ -export interface InternalBackstagePlugin< +export interface InternalFrontendPlugin< TRoutes extends AnyRoutes = AnyRoutes, TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, -> extends BackstagePlugin { +> extends FrontendPlugin { readonly version: 'v1'; readonly extensions: Extension[]; readonly featureFlags: FeatureFlagConfig[]; @@ -56,10 +80,10 @@ export function createFrontendPlugin< TId extends string, TRoutes extends AnyRoutes = {}, TExternalRoutes extends AnyExternalRoutes = {}, - TExtensions extends readonly ExtensionDefinition[] = [], + TExtensions extends readonly ExtensionDefinition[] = [], >( options: PluginOptions, -): BackstagePlugin< +): FrontendPlugin< TRoutes, TExternalRoutes, { @@ -69,16 +93,20 @@ export function createFrontendPlugin< >]: KExtension; } > { - const extensions = new Array>(); + const extensions = new Array>(); const extensionDefinitionsById = new Map< string, - ExtensionDefinition + InternalExtensionDefinition >(); for (const def of options.extensions ?? []) { + const internal = toInternalExtensionDefinition(def); const ext = resolveExtensionDefinition(def, { namespace: options.id }); extensions.push(ext); - extensionDefinitionsById.set(ext.id, { ...def, namespace: options.id }); + extensionDefinitionsById.set(ext.id, { + ...internal, + namespace: options.id, + }); } if (extensions.length !== extensionDefinitionsById.size) { @@ -97,7 +125,7 @@ export function createFrontendPlugin< } return { - $$type: '@backstage/BackstagePlugin', + $$type: '@backstage/FrontendPlugin', version: 'v1', id: options.id, routes: options.routes ?? ({} as TRoutes), @@ -127,15 +155,33 @@ export function createFrontendPlugin< extensions: [...nonOverriddenExtensions, ...overrides.extensions], }); }, - } as InternalBackstagePlugin; + } as InternalFrontendPlugin; } /** @internal */ -export function toInternalBackstagePlugin( - plugin: BackstagePlugin, -): InternalBackstagePlugin { - const internal = plugin as InternalBackstagePlugin; - if (internal.$$type !== '@backstage/BackstagePlugin') { +export function isInternalFrontendPlugin(opaque: { + $$type: string; +}): opaque is InternalFrontendPlugin { + if ( + opaque.$$type === '@backstage/FrontendPlugin' || + opaque.$$type === '@backstage/BackstagePlugin' + ) { + // Make sure we throw if invalid + toInternalFrontendPlugin(opaque as FrontendPlugin); + return true; + } + return false; +} + +/** @internal */ +export function toInternalFrontendPlugin( + plugin: FrontendPlugin, +): InternalFrontendPlugin { + const internal = plugin as InternalFrontendPlugin; + if ( + internal.$$type !== '@backstage/FrontendPlugin' && + internal.$$type !== '@backstage/BackstagePlugin' + ) { throw new Error(`Invalid plugin instance, bad type '${internal.$$type}'`); } if (internal.version !== 'v1') { diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 3862c0e178..f8ad362bac 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -18,6 +18,7 @@ export { coreExtensionData } from './coreExtensionData'; export { createExtension, type ExtensionDefinition, + type ExtensionDefinitionParameters, type CreateExtensionOptions, type ResolvedExtensionInput, type ResolvedExtensionInputs, @@ -38,8 +39,15 @@ export { export { createPlugin, createFrontendPlugin, + type FrontendPlugin, + type BackstagePlugin, type PluginOptions, } from './createFrontendPlugin'; +export { + createFrontendModule, + type FrontendModule, + type CreateFrontendModuleOptions, +} from './createFrontendModule'; export { createExtensionOverrides, type ExtensionOverridesOptions, @@ -48,7 +56,6 @@ export { type Extension } from './resolveExtensionDefinition'; export { type AnyRoutes, type AnyExternalRoutes, - type BackstagePlugin, type ExtensionOverrides, type FeatureFlagConfig, type FrontendFeature, @@ -56,6 +63,7 @@ export { export { type CreateExtensionBlueprintOptions, type ExtensionBlueprint, + type ExtensionBlueprintParameters, createExtensionBlueprint, } from './createExtensionBlueprint'; export { type ResolveInputValueOverrides } from './resolveInputOverrides'; diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts index 3b43cd6518..09c7b9e9a9 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts @@ -23,10 +23,11 @@ import { describe('resolveExtensionDefinition', () => { const baseDef = { $$type: '@backstage/ExtensionDefinition', + T: undefined as any, version: 'v2', attachTo: { id: '', input: '' }, disabled: false, - override: () => ({} as ExtensionDefinition), + override: () => ({} as ExtensionDefinition), }; it.each([ @@ -39,7 +40,7 @@ describe('resolveExtensionDefinition', () => { const resolved = resolveExtensionDefinition({ ...baseDef, ...definition, - } as ExtensionDefinition); + } as ExtensionDefinition); expect(resolved.id).toBe(expected); expect(String(resolved)).toBe(`Extension{id=${expected}}`); }); @@ -49,12 +50,12 @@ describe('resolveExtensionDefinition', () => { resolveExtensionDefinition({ ...baseDef, kind: 'k', - } as ExtensionDefinition), + } as ExtensionDefinition), ).toThrow( 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=k namespace=undefined name=undefined', ); expect(() => - resolveExtensionDefinition(baseDef as ExtensionDefinition), + resolveExtensionDefinition(baseDef as ExtensionDefinition), ).toThrow( 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=undefined namespace=undefined name=undefined', ); @@ -64,10 +65,11 @@ describe('resolveExtensionDefinition', () => { describe('old resolveExtensionDefinition', () => { const baseDef = { $$type: '@backstage/ExtensionDefinition', + T: undefined as any, version: 'v1', attachTo: { id: '', input: '' }, disabled: false, - override: () => ({} as ExtensionDefinition), + override: () => ({} as ExtensionDefinition), }; it.each([ @@ -80,7 +82,7 @@ describe('old resolveExtensionDefinition', () => { const resolved = resolveExtensionDefinition({ ...baseDef, ...definition, - } as ExtensionDefinition); + } as ExtensionDefinition); expect(resolved.id).toBe(expected); expect(String(resolved)).toBe(`Extension{id=${expected}}`); }); @@ -90,12 +92,12 @@ describe('old resolveExtensionDefinition', () => { resolveExtensionDefinition({ ...baseDef, kind: 'k', - } as ExtensionDefinition), + } as ExtensionDefinition), ).toThrow( 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=k namespace=undefined name=undefined', ); expect(() => - resolveExtensionDefinition(baseDef as ExtensionDefinition), + resolveExtensionDefinition(baseDef as ExtensionDefinition), ).toThrow( 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=undefined namespace=undefined name=undefined', ); @@ -108,13 +110,12 @@ describe('ResolveExtensionId', () => { TKind extends string | undefined, TNamespace extends string | undefined, TName extends string | undefined, - > = ExtensionDefinition< - any, - any, - any, - any, - { kind: TKind; namespace: TNamespace; name: TName } - >; + > = ExtensionDefinition<{ + kind: TKind; + namespace: TNamespace; + name: TName; + output: any; + }>; const id1: 'k:ns' = {} as ResolveExtensionId< NamedExtension<'k', 'ns', undefined>, diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index 3cbc28f8ff..870fb258f5 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -14,9 +14,10 @@ * limitations under the License. */ -import { AppNode } from '../apis'; +import { ApiHolder, AppNode } from '../apis'; import { ExtensionDefinition, + ExtensionDefinitionParameters, ResolvedExtensionInputs, toInternalExtensionDefinition, } from './createExtension'; @@ -57,6 +58,7 @@ export type InternalExtension = Extension< [name in string]: AnyExtensionDataRef; }; factory(context: { + apis: ApiHolder; node: AppNode; config: TConfig; inputs: { @@ -76,6 +78,7 @@ export type InternalExtension = Extension< }; readonly output: Array; factory(options: { + apis: ApiHolder; node: AppNode; config: TConfig; inputs: ResolvedExtensionInputs<{ @@ -107,19 +110,13 @@ export function toInternalExtension( /** @ignore */ export type ResolveExtensionId< - TExtension extends ExtensionDefinition, + TExtension extends ExtensionDefinition, TDefaultNamespace extends string | undefined, -> = TExtension extends ExtensionDefinition< - any, - any, - any, - any, - { - kind: infer IKind extends string | undefined; - namespace: infer INamespace extends string | undefined; - name: infer IName extends string | undefined; - } -> +> = TExtension extends ExtensionDefinition<{ + kind: infer IKind extends string | undefined; + namespace: infer INamespace extends string | undefined; + name: infer IName extends string | undefined; +}> ? [string | undefined] extends [IKind | INamespace | IName] ? never : ( @@ -138,10 +135,12 @@ export type ResolveExtensionId< : never; /** @internal */ -export function resolveExtensionDefinition( - definition: ExtensionDefinition, +export function resolveExtensionDefinition< + T extends ExtensionDefinitionParameters, +>( + definition: ExtensionDefinition, context?: { namespace?: string }, -): Extension { +): Extension { const internalDefinition = toInternalExtensionDefinition(definition); const { name, @@ -170,5 +169,5 @@ export function resolveExtensionDefinition( toString() { return `Extension{id=${id}}`; }, - } as InternalExtension; + } as InternalExtension & Object; } diff --git a/packages/frontend-plugin-api/src/wiring/types.ts b/packages/frontend-plugin-api/src/wiring/types.ts index 4a7d972b37..b52bbf446c 100644 --- a/packages/frontend-plugin-api/src/wiring/types.ts +++ b/packages/frontend-plugin-api/src/wiring/types.ts @@ -16,6 +16,7 @@ import { ExternalRouteRef, RouteRef, SubRouteRef } from '../routing'; import { ExtensionDefinition } from './createExtension'; +import { FrontendPlugin } from './createFrontendPlugin'; /** * Feature flag configuration. @@ -35,31 +36,18 @@ export type AnyExternalRoutes = { [name in string]: ExternalRouteRef }; /** @public */ export type ExtensionMap< - TExtensionMap extends { [id in string]: ExtensionDefinition }, + TExtensionMap extends { [id in string]: ExtensionDefinition }, > = { get(id: TId): TExtensionMap[TId]; }; -/** @public */ -export interface BackstagePlugin< - TRoutes extends AnyRoutes = AnyRoutes, - TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, - TExtensionMap extends { [id in string]: ExtensionDefinition } = {}, -> { - readonly $$type: '@backstage/BackstagePlugin'; - readonly id: string; - readonly routes: TRoutes; - readonly externalRoutes: TExternalRoutes; - getExtension(id: TId): TExtensionMap[TId]; - withOverrides(options: { - extensions: Array>; - }): BackstagePlugin; -} - /** @public */ export interface ExtensionOverrides { readonly $$type: '@backstage/ExtensionOverrides'; } -/** @public */ -export type FrontendFeature = BackstagePlugin | ExtensionOverrides; +/** + * @public + * @deprecated import from {@link @backstage/frontend-app-api#FrontendFeature} instead + */ +export type FrontendFeature = FrontendPlugin | ExtensionOverrides; diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index a35ec8203b..04cc025a74 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/frontend-test-utils +## 0.2.0-next.0 + +### Minor Changes + +- 5446061: Removed support for testing "v1" extensions, where outputs are defined as an object rather than an array. +- e6e488c: **BREAKING**: The deprecated `.render()` method has been removed from the extension tester. + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 4a66456: Internal update to add support for passing an `ApiRegistry` when creating the node tree +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. +- f6d1874: Added the ability to provide additional `extensions` and `features` to `renderInTestApp` +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/frontend-app-api@0.9.0-next.0 + - @backstage/plugin-app@0.1.0-next.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + ## 0.1.12 ### Patch Changes diff --git a/packages/frontend-test-utils/api-report.md b/packages/frontend-test-utils/api-report.md index 164c222d84..10b84b0981 100644 --- a/packages/frontend-test-utils/api-report.md +++ b/packages/frontend-test-utils/api-report.md @@ -13,6 +13,8 @@ import { AppNodeInstance } from '@backstage/frontend-plugin-api'; import { ErrorWithContext } from '@backstage/test-utils'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinitionParameters } from '@backstage/frontend-plugin-api'; +import { FrontendFeature } from '@backstage/frontend-app-api'; import { JsonObject } from '@backstage/types'; import { MockConfigApi } from '@backstage/test-utils'; import { MockErrorApi } from '@backstage/test-utils'; @@ -31,16 +33,12 @@ import { TestApiRegistry } from '@backstage/test-utils'; import { withLogCollector } from '@backstage/test-utils'; // @public (undocumented) -export function createExtensionTester< - TConfig, - TConfigInput, - UOutput extends AnyExtensionDataRef, ->( - subject: ExtensionDefinition, +export function createExtensionTester( + subject: ExtensionDefinition, options?: { - config?: TConfigInput; + config?: T['configInput']; }, -): ExtensionTester; +): ExtensionTester>; export { ErrorWithContext }; @@ -64,10 +62,10 @@ export class ExtensionQuery { // @public (undocumented) export class ExtensionTester { // (undocumented) - add( - extension: ExtensionDefinition, + add( + extension: ExtensionDefinition, options?: { - config?: TConfigInput; + config?: T['configInput']; }, ): ExtensionTester; // (undocumented) @@ -79,13 +77,11 @@ export class ExtensionTester { : IData : never; // (undocumented) - query( - extension: ExtensionDefinition, - ): ExtensionQuery; + query( + extension: ExtensionDefinition, + ): ExtensionQuery>; // (undocumented) reactElement(): JSX.Element; - // @deprecated (undocumented) - render(options?: { config?: JsonObject }): RenderResult; } // @public @@ -139,6 +135,8 @@ export type TestAppOptions = { [path: string]: RouteRef; }; config?: JsonObject; + extensions?: ExtensionDefinition[]; + features?: FrontendFeature[]; }; export { withLogCollector }; diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 526e8b1713..a84d296c55 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.1.12", + "version": "0.2.0-next.0", "backstage": { "role": "web-library" }, @@ -34,6 +34,7 @@ "@backstage/config": "workspace:^", "@backstage/frontend-app-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", "@backstage/types": "workspace:^" }, diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx index 9528562198..582bbeed00 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx @@ -14,22 +14,13 @@ * limitations under the License. */ -import React, { useCallback } from 'react'; -import { Link } from 'react-router-dom'; -import { fireEvent, screen, waitFor } from '@testing-library/react'; +import React from 'react'; import { - ApiBlueprint, - analyticsApiRef, - configApiRef, coreExtensionData, - createApiFactory, createExtension, createExtensionDataRef, createExtensionInput, - useAnalytics, - useApi, } from '@backstage/frontend-plugin-api'; -import { MockAnalyticsApi } from '../apis'; import { createExtensionTester } from './createExtensionTester'; const stringDataRef = createExtensionDataRef().with({ @@ -37,206 +28,6 @@ const stringDataRef = createExtensionDataRef().with({ }); describe('createExtensionTester', () => { - const defaultDefinition = { - namespace: 'test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: [coreExtensionData.reactElement], - factory: () => [coreExtensionData.reactElement(
test
)], - }; - - it('should render a simple extension', async () => { - const extension = createExtension(defaultDefinition); - const tester = createExtensionTester(extension); - tester.render(); - await expect(screen.findByText('test')).resolves.toBeInTheDocument(); - }); - - it('should render an extension even if disabled by default', async () => { - const extension = createExtension({ - ...defaultDefinition, - disabled: true, - }); - const tester = createExtensionTester(extension); - tester.render(); - await expect(screen.findByText('test')).resolves.toBeInTheDocument(); - }); - - it("should fail to render an extension that doesn't output a react element", async () => { - const extension = createExtension({ - ...defaultDefinition, - output: [coreExtensionData.routePath], - factory: () => [coreExtensionData.routePath('/foo')], - }); - const tester = createExtensionTester(extension); - expect(() => tester.render()).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'app/routes', extension 'test' could not be attached because its output data ('core.routing.path') does not match what the input 'routes' requires ('core.routing.path', 'core.reactElement')"`, - ); - }); - - it('should render multiple extensions', async () => { - const indexPageExtension = createExtension({ - ...defaultDefinition, - factory: () => [ - coreExtensionData.reactElement( -
- Index page See details -
, - ), - ], - }); - const detailsPageExtension = createExtension({ - ...defaultDefinition, - name: 'details', - attachTo: { id: 'app/routes', input: 'routes' }, - output: [coreExtensionData.routePath, coreExtensionData.reactElement], - factory: () => [ - coreExtensionData.routePath('/details'), - coreExtensionData.reactElement(
Details page
), - ], - }); - - const tester = createExtensionTester(indexPageExtension); - tester.add(detailsPageExtension); - tester.render(); - - await expect(screen.findByText('Index page')).resolves.toBeInTheDocument(); - - fireEvent.click(screen.getByRole('link', { name: 'See details' })); - - await expect( - screen.findByText('Details page'), - ).resolves.toBeInTheDocument(); - }); - - it('should accepts a custom config', async () => { - const indexPageExtension = createExtension({ - ...defaultDefinition, - config: { - schema: { - title: z => z.string().optional(), - }, - }, - factory: ({ config }) => { - const Component = () => { - const configApi = useApi(configApiRef); - const appTitle = configApi.getOptionalString('app.title'); - return ( -
-

{appTitle ?? 'Backstage app'}

-

{config.title ?? 'Index page'}

- See details -
- ); - }; - - return [coreExtensionData.reactElement()]; - }, - }); - - const detailsPageExtension = createExtension({ - ...defaultDefinition, - name: 'details', - attachTo: { id: 'app/routes', input: 'routes' }, - config: { - schema: { - title: z => z.string().optional(), - }, - }, - output: [coreExtensionData.routePath, coreExtensionData.reactElement], - factory: ({ config }) => [ - coreExtensionData.routePath('/details'), - coreExtensionData.reactElement( -
{config.title ?? 'Details page'}
, - ), - ], - }); - - const tester = createExtensionTester(indexPageExtension, { - config: { title: 'Custom index' }, - }); - - tester.add(detailsPageExtension, { - config: { title: 'Custom details' }, - }); - - tester.render({ - config: { - app: { - title: 'Custom app', - }, - }, - }); - - await expect( - screen.findByRole('heading', { name: 'Custom app' }), - ).resolves.toBeInTheDocument(); - - await expect( - screen.findByRole('heading', { name: 'Custom index' }), - ).resolves.toBeInTheDocument(); - - fireEvent.click(screen.getByRole('link', { name: 'See details' })); - - await expect( - screen.findByText('Custom details'), - ).resolves.toBeInTheDocument(); - }); - - it('should capture click events in analytics', async () => { - // Mocking the analytics api implementation - const analyticsApiMock = new MockAnalyticsApi(); - - const analyticsApiOverride = ApiBlueprint.make({ - params: { - factory: createApiFactory({ - api: analyticsApiRef, - deps: {}, - factory: () => analyticsApiMock, - }), - }, - }); - - const indexPageExtension = createExtension({ - ...defaultDefinition, - factory: () => { - const Component = () => { - const analyticsApi = useAnalytics(); - const handleClick = useCallback(() => { - analyticsApi.captureEvent('click', 'See details'); - }, [analyticsApi]); - return ( -
- Index Page - -
- ); - }; - - return [coreExtensionData.reactElement()]; - }, - }); - - const tester = createExtensionTester(indexPageExtension); - - // Overriding the analytics api extension - tester.add(analyticsApiOverride); - - tester.render(); - - fireEvent.click(await screen.findByRole('button', { name: 'See details' })); - - await waitFor(() => - expect(analyticsApiMock.getEvents()).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - action: 'click', - subject: 'See details', - }), - ]), - ), - ); - }); - it('should return the correct dataRef when called', () => { const extension = createExtension({ namespace: 'test', diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index 63de46a76e..f9c1b3cb89 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -14,10 +14,6 @@ * limitations under the License. */ -import React from 'react'; -import { MemoryRouter, Link } from 'react-router-dom'; -import { RenderResult, render } from '@testing-library/react'; -import { createSpecializedApp } from '@backstage/frontend-app-api'; import { AnyExtensionDataRef, AppNode, @@ -25,15 +21,8 @@ import { Extension, ExtensionDataRef, ExtensionDefinition, - IconComponent, - NavItemBlueprint, - RouteRef, - RouterBlueprint, + ExtensionDefinitionParameters, coreExtensionData, - createExtension, - createExtensionInput, - createExtensionOverrides, - useRouteRef, } from '@backstage/frontend-plugin-api'; import { Config, ConfigReader } from '@backstage/config'; import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; @@ -49,56 +38,7 @@ import { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveA import { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/instantiateAppNodeTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig'; - -const NavItem = (props: { - routeRef: RouteRef; - title: string; - icon: IconComponent; -}) => { - const { routeRef, title, icon: Icon } = props; - const link = useRouteRef(routeRef); - if (!link) { - return null; - } - return ( -
  • - - {title} - -
  • - ); -}; - -const TestAppNavExtension = createExtension({ - namespace: 'app', - name: 'nav', - attachTo: { id: 'app/layout', input: 'nav' }, - inputs: { - items: createExtensionInput([NavItemBlueprint.dataRefs.target]), - }, - output: [coreExtensionData.reactElement], - factory({ inputs }) { - return [ - coreExtensionData.reactElement( - , - ), - ]; - }, -}); +import { TestApiRegistry } from '@backstage/test-utils'; /** @public */ export class ExtensionQuery { @@ -138,12 +78,12 @@ export class ExtensionQuery { /** @public */ export class ExtensionTester { /** @internal */ - static forSubject( - subject: ExtensionDefinition, - options?: { config?: TConfigInput }, - ): ExtensionTester { + static forSubject( + subject: ExtensionDefinition, + options?: { config?: T['configInput'] }, + ): ExtensionTester> { const tester = new ExtensionTester(); - tester.add(subject, options as TConfigInput & {}); + tester.add(subject, options as T['configInput'] & {}); return tester; } @@ -152,13 +92,13 @@ export class ExtensionTester { readonly #extensions = new Array<{ id: string; extension: Extension; - definition: ExtensionDefinition; + definition: ExtensionDefinition; config?: JsonValue; }>(); - add( - extension: ExtensionDefinition, - options?: { config?: TConfigInput }, + add( + extension: ExtensionDefinition, + options?: { config?: T['configInput'] }, ): ExtensionTester { if (this.#tree) { throw new Error( @@ -166,7 +106,7 @@ export class ExtensionTester { ); } - const { name, namespace } = extension; + const { name, namespace } = toInternalExtensionDefinition(extension); const definition = { ...extension, @@ -198,12 +138,18 @@ export class ExtensionTester { return new ExtensionQuery(tree.root).get(ref); } - query( - extension: ExtensionDefinition, - ): ExtensionQuery { + query( + extension: ExtensionDefinition, + ): ExtensionQuery> { const tree = this.#resolveTree(); - const actualId = resolveExtensionDefinition(extension).id; + // Same fallback logic as in .add + const { name, namespace } = toInternalExtensionDefinition(extension); + const definition = { + ...extension, + name: !namespace && !name ? 'test' : name, + }; + const actualId = resolveExtensionDefinition(definition).id; const node = tree.nodes.get(actualId); @@ -235,70 +181,6 @@ export class ExtensionTester { return element; } - /** - * @deprecated Switch to using `renderInTestApp` directly and using `.reactElement()` or `.get(...)` to get the component you w - */ - render(options?: { config?: JsonObject }): RenderResult { - const { config = {} } = options ?? {}; - - const [subject] = this.#extensions; - if (!subject) { - throw new Error( - 'No subject found. At least one extension should be added to the tester.', - ); - } - - const subjectInternal = toInternalExtensionDefinition(subject.definition); - let subjectOverride; - // attaching to app/routes to render as index route - if (subjectInternal.version === 'v1') { - throw new Error('The extension tester does not support v1 extensions'); - } else if (subjectInternal.version === 'v2') { - subjectOverride = createExtension({ - ...subjectInternal, - attachTo: { id: 'app/routes', input: 'routes' }, - output: subjectInternal.output.find( - ref => ref.id === coreExtensionData.routePath.id, - ) - ? subjectInternal.output - : [...subjectInternal.output, coreExtensionData.routePath], - factory: params => { - const parentOutput = Array.from( - subjectInternal.factory(params as any), - ).filter(val => val.id !== coreExtensionData.routePath.id); - - return [...parentOutput, coreExtensionData.routePath('/')]; - }, - }); - (subjectOverride as any).configSchema = subjectInternal.configSchema; - } else { - throw new Error('Unsupported extension version'); - } - - const app = createSpecializedApp({ - features: [ - createExtensionOverrides({ - extensions: [ - subjectOverride, - ...this.#extensions.slice(1).map(extension => extension.definition), - TestAppNavExtension, - RouterBlueprint.make({ - namespace: 'test', - params: { - Component: ({ children }) => ( - {children} - ), - }, - }), - ], - }), - ], - config: this.#getConfig(config), - }); - - return render(app.createRoot()); - } - #resolveTree() { if (this.#tree) { return this.#tree; @@ -320,7 +202,7 @@ export class ExtensionTester { }), ); - instantiateAppNodeTree(tree.root); + instantiateAppNodeTree(tree.root, TestApiRegistry.from()); this.#tree = tree; @@ -359,13 +241,9 @@ export class ExtensionTester { } /** @public */ -export function createExtensionTester< - TConfig, - TConfigInput, - UOutput extends AnyExtensionDataRef, ->( - subject: ExtensionDefinition, - options?: { config?: TConfigInput }, -): ExtensionTester { +export function createExtensionTester( + subject: ExtensionDefinition, + options?: { config?: T['configInput'] }, +): ExtensionTester> { return ExtensionTester.forSubject(subject, options); } diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index 8bbe2d058c..fae67a260c 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -16,7 +16,10 @@ import React from 'react'; import { Link, MemoryRouter } from 'react-router-dom'; -import { createSpecializedApp } from '@backstage/frontend-app-api'; +import { + createSpecializedApp, + FrontendFeature, +} from '@backstage/frontend-app-api'; import { RenderResult, render } from '@testing-library/react'; import { ConfigReader } from '@backstage/config'; import { JsonObject } from '@backstage/types'; @@ -27,11 +30,11 @@ import { coreExtensionData, RouteRef, useRouteRef, - createExtensionInput, IconComponent, RouterBlueprint, NavItemBlueprint, } from '@backstage/frontend-plugin-api'; +import appPlugin from '@backstage/plugin-app'; /** * Options to customize the behavior of the test app. @@ -60,6 +63,16 @@ export type TestAppOptions = { * Additional configuration passed to the app when rendering elements inside it. */ config?: JsonObject; + + /** + * Additional extensions to add to the test app. + */ + extensions?: ExtensionDefinition[]; + + /** + * Additional features to add to the test app. + */ + features?: FrontendFeature[]; }; const NavItem = (props: { @@ -81,38 +94,36 @@ const NavItem = (props: { ); }; -export const TestAppNavExtension = createExtension({ - namespace: 'app', - name: 'nav', - attachTo: { id: 'app/layout', input: 'nav' }, - inputs: { - items: createExtensionInput([NavItemBlueprint.dataRefs.target]), - }, - output: [coreExtensionData.reactElement], - factory({ inputs }) { - return [ - coreExtensionData.reactElement( - , + ), + ]; + }, + }), + ], }); /** @@ -123,7 +134,7 @@ export function renderInTestApp( element: JSX.Element, options?: TestAppOptions, ): RenderResult { - const extensions: Array> = [ + const extensions: Array = [ createExtension({ namespace: 'test', attachTo: { id: 'app/routes', input: 'routes' }, @@ -141,7 +152,6 @@ export function renderInTestApp( Component: ({ children }) => {children}, }, }), - TestAppNavExtension, ]; if (options?.mountedRoutes) { @@ -167,12 +177,23 @@ export function renderInTestApp( } } + if (options?.extensions) { + extensions.push(...options.extensions); + } + + const features: FrontendFeature[] = [ + appPluginOverride, + createExtensionOverrides({ + extensions, + }), + ]; + + if (options?.features) { + features.push(...options.features); + } + const app = createSpecializedApp({ - features: [ - createExtensionOverrides({ - extensions, - }), - ], + features, config: ConfigReader.fromConfigs([ { context: 'render-config', data: options?.config ?? {} }, ]), diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index ffb51ea7a7..228886efc1 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/repo-tools +## 0.9.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + ## 0.9.5 ### Patch Changes diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index a06f426baf..6e0e4c9b85 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -18,6 +18,7 @@ Commands: knip-reports [options] [paths...] package [command] repo [command] + lint [command] help [command] ``` @@ -60,6 +61,28 @@ Options: -h, --help ``` +### `backstage-repo-tools lint` + +``` +Usage: backstage-repo-tools lint [options] [command] [command] + +Options: + -h, --help + +Commands: + legacy-backend-exports [paths...] + help [command] +``` + +### `backstage-repo-tools lint legacy-backend-exports` + +``` +Usage: backstage-repo-tools lint legacy-backend-exports [options] [paths...] + +Options: + -h, --help +``` + ### `backstage-repo-tools package` ``` diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index f337329537..5713659568 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.9.5", + "version": "0.9.7-next.0", "publishConfig": { "access": "public" }, @@ -73,6 +73,7 @@ "minimatch": "^9.0.0", "p-limit": "^3.0.2", "portfinder": "^1.0.32", + "ts-morph": "^23.0.0", "yaml-diff-patch": "^2.0.0" }, "devDependencies": { diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 24ec2af411..5116326eea 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -162,6 +162,23 @@ function registerRepoCommand(program: Command) { ); } +function registerLintCommand(program: Command) { + const lintCommand = program + .command('lint [command]') + .description('Tools for linting repository.'); + lintCommand + .command('legacy-backend-exports [paths...]') + .description( + 'Lint backend plugin packages for legacy exports and make sure it conforms to the new export pattern', + ) + .action( + lazy(() => + import( + './lint-legacy-backend-exports/lint-legacy-backend-exports' + ).then(m => m.lint), + ), + ); +} export function registerCommands(program: Command) { program .command('api-reports [paths...]') @@ -238,6 +255,7 @@ export function registerCommands(program: Command) { registerPackageCommand(program); registerRepoCommand(program); + registerLintCommand(program); } // Wraps an action function so that it always exits and handles errors diff --git a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts new file mode 100644 index 0000000000..6025defe31 --- /dev/null +++ b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2024 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 { Project } from 'ts-morph'; +import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node'; +import fs from 'fs-extra'; +import { paths as cliPaths } from '../../lib/paths'; +import path from 'path'; + +const project = new Project({ + tsConfigFilePath: cliPaths.resolveTargetRoot('tsconfig.json'), +}); + +function readPackageJson(pkg: string) { + return JSON.parse(fs.readFileSync(path.join(pkg, 'package.json'), 'utf-8')); +} + +export async function lint(paths: string[]) { + const pkgs = (await PackageGraph.listTargetPackages()).filter(pkg => { + return ( + pkg.packageJson.backstage?.role === 'backend-plugin' || + pkg.packageJson.backstage?.role === 'backend-plugin-module' + ); + }); + + if (paths.length > 0) { + paths.forEach(pkg => verifyIndex(pkg)); + return; + } + pkgs.forEach(pkg => verifyIndex(pkg.dir, pkg.packageJson)); +} + +function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) { + console.log(`Verifying ${pkg}`); + const tsPath = path.join(pkg, 'src/index.ts'); + const sourceFile = project.getSourceFile(tsPath); + if (!sourceFile) { + console.log(`Could not find ${tsPath}`); + process.exit(1); + } + const symbols = sourceFile?.getExportSymbols(); + + const exportCount = symbols?.length || 0; + if (exportCount > 1) { + console.log( + ` ⚠️ Warning: ${exportCount} exports found, ${symbols + .map(symbol => symbol.getName()) + .join(', ')}`, + ); + } + + const createRouterExport = symbols?.find( + symbol => symbol.getName() === 'createRouter', + ); + + if (!sourceFile.getDefaultExportSymbol()) { + console.log(' ❌ Missing default export'); + } + let createRouterDeprecated = undefined; + if (createRouterExport) { + createRouterDeprecated = createRouterExport + .getJsDocTags() + .find(tag => tag.getName() === 'deprecated'); + } + + if (createRouterExport) { + console.log(' ❌ createRouter is exported'); + if (!createRouterDeprecated) + console.log(' ❌ createRouter is NOT deprecated'); + } + + const pkgJson = packageJson ?? readPackageJson(pkg); + if ( + '@backstage/backend-common' in pkgJson.dependencies || + '@backstage/backend-common' in pkgJson.devDependencies + ) { + console.log(' ❌ Stop depending on @backstage/backend-common'); + } + + if ( + '@backstage/backend-tasks' in pkgJson.dependencies || + '@backstage/backend-tasks' in pkgJson.devDependencies + ) { + console.log(' ❌ Stop depending on @backstage/backend-tasks'); + } +} diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 3d0673f221..ea42ae4cc0 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,24 @@ # techdocs-cli-embedded-app +## 0.2.100-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/plugin-techdocs@1.10.9-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/cli@0.27.1-next.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/app-defaults@1.5.10 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + ## 0.2.99 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 64774310f8..54844b882d 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.99", + "version": "0.2.100-next.0", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 18b7b5150d..c3b4be91f8 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.8.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.11-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + ## 1.8.17 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index b14369cfb9..c0af6ceaed 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.8.17", + "version": "1.8.19-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index ef2aa614e8..29d38987d7 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/test-utils +## 1.6.0-next.0 + +### Minor Changes + +- d47be30: Added the icons option to the renderInTestApp function's TestAppOptions to be forwarded to the icons option of `createApp`. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-react@0.4.25 + ## 1.5.10 ### Patch Changes diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index cc1c645387..ae54c588ba 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -8,6 +8,7 @@ import { AnalyticsEvent } from '@backstage/core-plugin-api'; import { ApiHolder } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; import { AppComponents } from '@backstage/core-plugin-api'; +import { AppIcons } from '@backstage/core-app-api'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { ComponentType } from 'react'; import { Config } from '@backstage/config'; @@ -21,6 +22,7 @@ import { EvaluatePermissionRequest } from '@backstage/plugin-permission-common'; import { EvaluatePermissionResponse } from '@backstage/plugin-permission-common'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FetchApi } from '@backstage/core-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; import { IdentityApi } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; @@ -252,6 +254,9 @@ export type TestAppOptions = { [path: string]: RouteRef | ExternalRouteRef; }; components?: Partial; + icons?: Partial & { + [key in string]: IconComponent; + }; }; // @public diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 73089ddc54..16d1609c66 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.5.10", + "version": "1.6.0-next.0", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index f2c1ff592e..3a55389b74 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -23,13 +23,14 @@ import React, { import { MemoryRouter, Route } from 'react-router-dom'; import { themes, UnifiedThemeProvider } from '@backstage/theme'; import MockIcon from '@material-ui/icons/AcUnit'; -import { createSpecializedApp } from '@backstage/core-app-api'; +import { AppIcons, createSpecializedApp } from '@backstage/core-app-api'; import { AppComponents, attachComponentData, BootErrorPageProps, createRouteRef, ExternalRouteRef, + IconComponent, RouteRef, } from '@backstage/core-plugin-api'; import { MatcherFunction, RenderResult } from '@testing-library/react'; @@ -107,6 +108,13 @@ export type TestAppOptions = { * Components to be forwarded to the `components` option of `createApp`. */ components?: Partial; + + /** + * Icons to be forwarded to the `icons` option of `createApp`. + */ + icons?: Partial & { + [key in string]: IconComponent; + }; }; function isExternalRouteRef( @@ -145,7 +153,10 @@ export function createTestAppWrapper( ), ...options.components, }, - icons: mockIcons, + icons: { + ...mockIcons, + ...options.icons, + }, plugins: [], themes: [ { diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 211f073321..7c67bea51e 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-api-docs +## 0.11.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-catalog@1.22.1-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + ## 0.11.8 ### Patch Changes diff --git a/plugins/api-docs/api-report-alpha.md b/plugins/api-docs/api-report-alpha.md index c7ab4eb9e0..bb2da7bc0b 100644 --- a/plugins/api-docs/api-report-alpha.md +++ b/plugins/api-docs/api-report-alpha.md @@ -6,18 +6,18 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyExtensionDataRef } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { root: RouteRef; }, @@ -25,10 +25,13 @@ const _default: BackstagePlugin< registerApi: ExternalRouteRef; }, { - 'nav-item:api-docs': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef< + 'nav-item:api-docs': ExtensionDefinition<{ + kind: 'nav-item'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { title: string; icon: IconComponent; @@ -36,50 +39,48 @@ const _default: BackstagePlugin< }, 'core.nav-item.target', {} - >, - {}, - { - kind: 'nav-item'; - namespace: undefined; - name: undefined; - } - >; - 'api:api-docs/config': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'config'; - } - >; - 'page:api-docs': ExtensionDefinition< - { + >; + inputs: {}; + }>; + 'api:api-docs/config': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'config'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'page:api-docs': ExtensionDefinition<{ + config: { initiallySelectedFilter: 'all' | 'owned' | 'starred' | undefined; } & { path: string | undefined; - }, - { + }; + configInput: { initiallySelectedFilter?: 'all' | 'owned' | 'starred' | undefined; } & { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -87,303 +88,301 @@ const _default: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; - 'entity-card:api-docs/has-apis': ExtensionDefinition< - { + }; + kind: 'page'; + namespace: undefined; + name: undefined; + }>; + 'entity-card:api-docs/has-apis': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'has-apis'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'has-apis'; - } - >; - 'entity-card:api-docs/definition': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:api-docs/definition': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'definition'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'definition'; - } - >; - 'entity-card:api-docs/consumed-apis': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:api-docs/consumed-apis': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'consumed-apis'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'consumed-apis'; - } - >; - 'entity-card:api-docs/provided-apis': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:api-docs/provided-apis': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'provided-apis'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'provided-apis'; - } - >; - 'entity-card:api-docs/consuming-components': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:api-docs/consuming-components': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'consuming-components'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'consuming-components'; - } - >; - 'entity-card:api-docs/providing-components': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:api-docs/providing-components': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'providing-components'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'providing-components'; - } - >; - 'entity-content:api-docs/definition': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-content:api-docs/definition': ExtensionDefinition<{ + kind: 'entity-content'; + namespace: undefined; + name: 'definition'; + config: { path: string | undefined; title: string | undefined; filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; title?: string | undefined; path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-content'; - namespace: undefined; - name: 'definition'; - } - >; - 'entity-content:api-docs/apis': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-content-title', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-content:api-docs/apis': ExtensionDefinition<{ + kind: 'entity-content'; + namespace: undefined; + name: 'apis'; + config: { path: string | undefined; title: string | undefined; filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; title?: string | undefined; path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-content'; - namespace: undefined; - name: 'apis'; - } - >; + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-content-title', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 7564b858a9..7f27a4c6a2 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.11.8", + "version": "0.11.9-next.0", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 604ef6f305..c7e709c1ec 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-app-backend +## 0.3.74-next.0 + +### Patch Changes + +- d3f79d1: Fixing dependency metadata with the new `@backstage/plugin-app` package +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-app-node@0.1.25-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.3.72 ### Patch Changes diff --git a/plugins/app-backend/api-report-alpha.md b/plugins/app-backend/api-report-alpha.md index b51096b8f4..1c9b6b2902 100644 --- a/plugins/app-backend/api-report-alpha.md +++ b/plugins/app-backend/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const appPlugin: BackendFeatureCompat; +const appPlugin: BackendFeature; export default appPlugin; // (No @packageDocumentation comment for this package) diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index b7fbef8b46..90c85f8b63 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,11 +1,12 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.3.72", + "version": "0.3.74-next.0", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", "pluginId": "app", "pluginPackages": [ + "@backstage/plugin-app", "@backstage/plugin-app-backend", "@backstage/plugin-app-node" ] diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index 607aa68750..53a3d3967e 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-node +## 0.1.25-next.0 + +### Patch Changes + +- d3f79d1: Fixing dependency metadata with the new `@backstage/plugin-app` package +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/config-loader@1.9.0 + ## 0.1.23 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 4e28a853ed..5cab91886f 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,11 +1,12 @@ { "name": "@backstage/plugin-app-node", - "version": "0.1.23", + "version": "0.1.25-next.0", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", "pluginId": "app", "pluginPackages": [ + "@backstage/plugin-app", "@backstage/plugin-app-backend", "@backstage/plugin-app-node" ] diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index f7dad4bb44..44ec74ddd1 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + ## 0.1.9 ### Patch Changes diff --git a/plugins/app-visualizer/api-report.md b/plugins/app-visualizer/api-report.md index e01040a22a..e542674845 100644 --- a/plugins/app-visualizer/api-report.md +++ b/plugins/app-visualizer/api-report.md @@ -4,49 +4,51 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) -const visualizerPlugin: BackstagePlugin< +const visualizerPlugin: FrontendPlugin< {}, {}, { - 'page:app-visualizer': ExtensionDefinition< - { + 'page:app-visualizer': ExtensionDefinition<{ + kind: 'page'; + namespace: undefined; + name: undefined; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; - 'nav-item:app-visualizer': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef< + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + }>; + 'nav-item:app-visualizer': ExtensionDefinition<{ + kind: 'nav-item'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { title: string; icon: IconComponent; @@ -54,14 +56,9 @@ const visualizerPlugin: BackstagePlugin< }, 'core.nav-item.target', {} - >, - {}, - { - kind: 'nav-item'; - namespace: undefined; - name: undefined; - } - >; + >; + inputs: {}; + }>; } >; export default visualizerPlugin; diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 7ee42ef335..54a8adfbd7 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-visualizer", - "version": "0.1.9", + "version": "0.1.10-next.0", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/.eslintrc.js b/plugins/app/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/app/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md new file mode 100644 index 0000000000..c7523808e5 --- /dev/null +++ b/plugins/app/CHANGELOG.md @@ -0,0 +1,16 @@ +# @backstage/plugin-app + +## 0.1.0-next.0 + +### Minor Changes + +- 2bb9517: Introduce the `@backstage/plugin-app` package to hold all of the built-in extensions for easy consumption and overriding. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-permission-react@0.4.25 diff --git a/plugins/app/README.md b/plugins/app/README.md new file mode 100644 index 0000000000..f54a8e0a4c --- /dev/null +++ b/plugins/app/README.md @@ -0,0 +1,9 @@ +# app + +Welcome to the app plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/app](http://localhost:3000/app). diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md new file mode 100644 index 0000000000..ca76ecb91b --- /dev/null +++ b/plugins/app/api-report.md @@ -0,0 +1,681 @@ +## API Report File for "@backstage/plugin-app" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { AnyExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { AppTheme } from '@backstage/frontend-plugin-api'; +import { ComponentRef } from '@backstage/frontend-plugin-api'; +import { ComponentType } from 'react'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; +import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { ReactNode } from 'react'; +import { RouteRef } from '@backstage/frontend-plugin-api'; +import { SignInPageProps } from '@backstage/core-plugin-api'; +import { TranslationMessages } from '@backstage/frontend-plugin-api'; +import { TranslationResource } from '@backstage/frontend-plugin-api'; + +// @public (undocumented) +const appPlugin: FrontendPlugin< + {}, + {}, + { + [x: `component:app/${string}`]: ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + { + ref: ComponentRef; + impl: ComponentType; + }, + 'core.component.component', + {} + >; + inputs: { + [x: string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }; + kind: 'component'; + namespace: string; + name: string; + }>; + app: ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { + root: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + }; + kind: undefined; + namespace: 'app'; + name: undefined; + }>; + 'api:app/app-language': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'app-language'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'app/layout': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { + nav: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + content: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + }; + kind: undefined; + namespace: 'app'; + name: 'layout'; + }>; + 'app/nav': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { + items: ExtensionInput< + ConfigurableExtensionDataRef< + { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }, + 'core.nav-item.target', + {} + >, + { + singleton: false; + optional: false; + } + >; + logos: ExtensionInput< + ConfigurableExtensionDataRef< + { + logoIcon?: JSX.Element | undefined; + logoFull?: JSX.Element | undefined; + }, + 'core.nav-logo.logo-elements', + {} + >, + { + singleton: true; + optional: true; + } + >; + }; + kind: undefined; + namespace: 'app'; + name: 'nav'; + }>; + 'app/root': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { + router: ExtensionInput< + ConfigurableExtensionDataRef< + ComponentType<{ + children?: ReactNode; + }>, + 'app.router.wrapper', + {} + >, + { + singleton: true; + optional: true; + } + >; + signInPage: ExtensionInput< + ConfigurableExtensionDataRef< + ComponentType, + 'core.sign-in-page.component', + {} + >, + { + singleton: true; + optional: true; + } + >; + children: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: true; + optional: false; + } + >; + elements: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: false; + optional: false; + } + >; + wrappers: ExtensionInput< + ConfigurableExtensionDataRef< + ComponentType<{ + children?: ReactNode; + }>, + 'app.root.wrapper', + {} + >, + { + singleton: false; + optional: false; + } + >; + }; + kind: undefined; + namespace: 'app'; + name: 'root'; + }>; + 'app/routes': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { + routes: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + } + >; + }; + kind: undefined; + namespace: 'app'; + name: 'routes'; + }>; + 'api:app/app-theme': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { + themes: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: false; + optional: false; + } + >; + }; + kind: 'api'; + namespace: undefined; + name: 'app-theme'; + }>; + 'theme:app/light': ExtensionDefinition<{ + kind: 'theme'; + namespace: 'app'; + name: 'light'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef; + inputs: {}; + }>; + 'theme:app/dark': ExtensionDefinition<{ + kind: 'theme'; + namespace: 'app'; + name: 'dark'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef; + inputs: {}; + }>; + 'api:app/components': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { + components: ExtensionInput< + ConfigurableExtensionDataRef< + { + ref: ComponentRef; + impl: ComponentType; + }, + 'core.component.component', + {} + >, + { + singleton: false; + optional: false; + } + >; + }; + kind: 'api'; + namespace: undefined; + name: 'components'; + }>; + 'api:app/icons': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { + icons: ExtensionInput< + ConfigurableExtensionDataRef< + { + [x: string]: IconComponent_2; + }, + 'core.icons', + {} + >, + { + singleton: false; + optional: false; + } + >; + }; + kind: 'api'; + namespace: undefined; + name: 'icons'; + }>; + 'api:app/feature-flags': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'feature-flags'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/translations': ExtensionDefinition<{ + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: { + translations: ExtensionInput< + ConfigurableExtensionDataRef< + | TranslationResource + | TranslationMessages< + string, + { + [x: string]: string; + }, + boolean + >, + 'core.translation.translation', + {} + >, + { + singleton: false; + optional: false; + } + >; + }; + kind: 'api'; + namespace: undefined; + name: 'translations'; + }>; + 'app-root-element:app/oauth-request-dialog': ExtensionDefinition<{ + kind: 'app-root-element'; + namespace: 'app'; + name: 'oauth-request-dialog'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'app-root-element:app/alert-display': ExtensionDefinition<{ + config: { + transientTimeoutMs: number; + anchorOrigin: { + horizontal: 'center' | 'left' | 'right'; + vertical: 'top' | 'bottom'; + }; + }; + configInput: { + anchorOrigin?: + | { + horizontal?: 'center' | 'left' | 'right' | undefined; + vertical?: 'top' | 'bottom' | undefined; + } + | undefined; + transientTimeoutMs?: number | undefined; + }; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { + [x: string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }; + kind: 'app-root-element'; + namespace: 'app'; + name: 'alert-display'; + }>; + 'api:app/discovery': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'discovery'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/alert': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'alert'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/analytics': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'analytics'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/error': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'error'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/storage': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'storage'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/fetch': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'fetch'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/oauth-request': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'oauth-request'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/google-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'google-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/microsoft-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'microsoft-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/github-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'github-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/okta-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'okta-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/gitlab-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'gitlab-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/onelogin-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'onelogin-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/bitbucket-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'bitbucket-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/bitbucket-server-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'bitbucket-server-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/atlassian-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'atlassian-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/vmware-cloud-auth': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'vmware-cloud-auth'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:app/permission': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'permission'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + } +>; +export default appPlugin; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/app/catalog-info.yaml b/plugins/app/catalog-info.yaml new file mode 100644 index 0000000000..5bae243ea7 --- /dev/null +++ b/plugins/app/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-app + title: '@backstage/plugin-app' +spec: + lifecycle: experimental + type: backstage-frontend-plugin + owner: maintainers diff --git a/plugins/app/package.json b/plugins/app/package.json new file mode 100644 index 0000000000..b6289ca5a2 --- /dev/null +++ b/plugins/app/package.json @@ -0,0 +1,65 @@ +{ + "name": "@backstage/plugin-app", + "version": "0.1.0-next.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" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/app" + }, + "backstage": { + "role": "frontend-plugin", + "pluginId": "app", + "pluginPackages": [ + "@backstage/plugin-app", + "@backstage/plugin-app-backend", + "@backstage/plugin-app-node" + ] + }, + "sideEffects": false, + "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/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-permission-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.61", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/frontend-test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^15.0.0", + "@testing-library/user-event": "^14.0.0", + "msw": "^1.0.0", + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/app/src/defaultApis.ts b/plugins/app/src/defaultApis.ts new file mode 100644 index 0000000000..fd46ecf596 --- /dev/null +++ b/plugins/app/src/defaultApis.ts @@ -0,0 +1,382 @@ +/* + * 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. + */ + +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { + AlertApiForwarder, + NoOpAnalyticsApi, + ErrorApiForwarder, + ErrorAlerter, + GoogleAuth, + GithubAuth, + OktaAuth, + GitlabAuth, + MicrosoftAuth, + BitbucketAuth, + BitbucketServerAuth, + OAuthRequestManager, + WebStorage, + UrlPatternDiscovery, + OneLoginAuth, + UnhandledErrorForwarder, + AtlassianAuth, + createFetchApi, + FetchMiddlewares, + VMwareCloudAuth, +} from '../../../packages/core-app-api/src/apis/implementations'; + +import { + createApiFactory, + alertApiRef, + analyticsApiRef, + errorApiRef, + discoveryApiRef, + fetchApiRef, + identityApiRef, + oauthRequestApiRef, + googleAuthApiRef, + githubAuthApiRef, + oktaAuthApiRef, + gitlabAuthApiRef, + microsoftAuthApiRef, + storageApiRef, + configApiRef, + oneloginAuthApiRef, + bitbucketAuthApiRef, + bitbucketServerAuthApiRef, + atlassianAuthApiRef, + vmwareCloudAuthApiRef, +} from '@backstage/core-plugin-api'; +import { ApiBlueprint } from '@backstage/frontend-plugin-api'; +import { + permissionApiRef, + IdentityPermissionApi, +} from '@backstage/plugin-permission-react'; + +export const apis = [ + ApiBlueprint.make({ + name: 'discovery', + params: { + factory: createApiFactory({ + api: discoveryApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => + UrlPatternDiscovery.compile( + `${configApi.getString('backend.baseUrl')}/api/{{ pluginId }}`, + ), + }), + }, + }), + ApiBlueprint.make({ + name: 'alert', + params: { + factory: createApiFactory({ + api: alertApiRef, + deps: {}, + factory: () => new AlertApiForwarder(), + }), + }, + }), + ApiBlueprint.make({ + name: 'analytics', + params: { + factory: createApiFactory({ + api: analyticsApiRef, + deps: {}, + factory: () => new NoOpAnalyticsApi(), + }), + }, + }), + ApiBlueprint.make({ + name: 'error', + params: { + factory: createApiFactory({ + api: errorApiRef, + deps: { alertApi: alertApiRef }, + factory: ({ alertApi }) => { + const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); + UnhandledErrorForwarder.forward(errorApi, { hidden: false }); + return errorApi; + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'storage', + params: { + factory: createApiFactory({ + api: storageApiRef, + deps: { errorApi: errorApiRef }, + factory: ({ errorApi }) => WebStorage.create({ errorApi }), + }), + }, + }), + ApiBlueprint.make({ + name: 'fetch', + params: { + factory: createApiFactory({ + api: fetchApiRef, + deps: { + configApi: configApiRef, + identityApi: identityApiRef, + discoveryApi: discoveryApiRef, + }, + factory: ({ configApi, identityApi, discoveryApi }) => { + return createFetchApi({ + middleware: [ + FetchMiddlewares.resolvePluginProtocol({ + discoveryApi, + }), + FetchMiddlewares.injectIdentityAuth({ + identityApi, + config: configApi, + }), + ], + }); + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'oauth-request', + params: { + factory: createApiFactory({ + api: oauthRequestApiRef, + deps: {}, + factory: () => new OAuthRequestManager(), + }), + }, + }), + ApiBlueprint.make({ + name: 'google-auth', + params: { + factory: createApiFactory({ + api: googleAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GoogleAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'microsoft-auth', + params: { + factory: createApiFactory({ + api: microsoftAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + MicrosoftAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'github-auth', + params: { + factory: createApiFactory({ + api: githubAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GithubAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + defaultScopes: ['read:user'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'okta-auth', + params: { + factory: createApiFactory({ + api: oktaAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OktaAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'gitlab-auth', + params: { + factory: createApiFactory({ + api: gitlabAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + GitlabAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'onelogin-auth', + params: { + factory: createApiFactory({ + api: oneloginAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OneLoginAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'bitbucket-auth', + params: { + factory: createApiFactory({ + api: bitbucketAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + BitbucketAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + defaultScopes: ['account'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'bitbucket-server-auth', + params: { + factory: createApiFactory({ + api: bitbucketServerAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + BitbucketServerAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + defaultScopes: ['REPO_READ'], + }), + }), + }, + }), + ApiBlueprint.make({ + name: 'atlassian-auth', + params: { + factory: createApiFactory({ + api: atlassianAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return AtlassianAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'vmware-cloud-auth', + params: { + factory: createApiFactory({ + api: vmwareCloudAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return VMwareCloudAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), + }, + }), + ApiBlueprint.make({ + name: 'permission', + params: { + factory: createApiFactory({ + api: permissionApiRef, + deps: { + discovery: discoveryApiRef, + identity: identityApiRef, + config: configApiRef, + }, + factory: ({ config, discovery, identity }) => + IdentityPermissionApi.create({ config, discovery, identity }), + }), + }, + }), +] as const; diff --git a/packages/frontend-app-api/src/extensions/App.tsx b/plugins/app/src/extensions/App.tsx similarity index 56% rename from packages/frontend-app-api/src/extensions/App.tsx rename to plugins/app/src/extensions/App.tsx index ba6c5f65f1..5c6d95bfbc 100644 --- a/packages/frontend-app-api/src/extensions/App.tsx +++ b/plugins/app/src/extensions/App.tsx @@ -18,38 +18,34 @@ import React from 'react'; import { ExtensionBoundary, coreExtensionData, - createComponentExtension, createExtension, createExtensionInput, - IconBundleBlueprint, - ThemeBlueprint, - ApiBlueprint, - TranslationBlueprint, } from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { ApiProvider } from '../../../../packages/core-app-api/src'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppThemeProvider } from '../../../../packages/core-app-api/src/app/AppThemeProvider'; export const App = createExtension({ namespace: 'app', - attachTo: { id: 'root', input: 'default' }, // ignored + attachTo: { id: 'root', input: 'app' }, inputs: { - apis: createExtensionInput([ApiBlueprint.dataRefs.factory]), - themes: createExtensionInput([ThemeBlueprint.dataRefs.theme]), - components: createExtensionInput([ - createComponentExtension.componentDataRef, - ]), - translations: createExtensionInput([ - TranslationBlueprint.dataRefs.translation, - ]), - icons: createExtensionInput([IconBundleBlueprint.dataRefs.icons]), root: createExtensionInput([coreExtensionData.reactElement], { singleton: true, }), }, output: [coreExtensionData.reactElement], - factory: ({ node, inputs }) => [ - coreExtensionData.reactElement( - - {inputs.root.get(coreExtensionData.reactElement)} - , - ), - ], + factory: ({ node, apis, inputs }) => { + return [ + coreExtensionData.reactElement( + + + + {inputs.root.get(coreExtensionData.reactElement)} + + + , + ), + ]; + }, }); diff --git a/plugins/app/src/extensions/AppLanguageApi.ts b/plugins/app/src/extensions/AppLanguageApi.ts new file mode 100644 index 0000000000..7a6c10db96 --- /dev/null +++ b/plugins/app/src/extensions/AppLanguageApi.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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. + */ + +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppLanguageSelector } from '../../../../packages/core-app-api/src/apis/implementations/AppLanguageApi'; +import { appLanguageApiRef } from '@backstage/core-plugin-api/alpha'; +import { ApiBlueprint, createApiFactory } from '@backstage/frontend-plugin-api'; + +export const AppLanguageApi = ApiBlueprint.make({ + name: 'app-language', + params: { + factory: createApiFactory( + appLanguageApiRef, + AppLanguageSelector.createWithStorage(), + ), + }, +}); diff --git a/packages/frontend-app-api/src/extensions/AppLayout.tsx b/plugins/app/src/extensions/AppLayout.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/AppLayout.tsx rename to plugins/app/src/extensions/AppLayout.tsx diff --git a/packages/frontend-app-api/src/extensions/AppNav.tsx b/plugins/app/src/extensions/AppNav.tsx similarity index 95% rename from packages/frontend-app-api/src/extensions/AppNav.tsx rename to plugins/app/src/extensions/AppNav.tsx index b80a537ef6..3659f474b2 100644 --- a/packages/frontend-app-api/src/extensions/AppNav.tsx +++ b/plugins/app/src/extensions/AppNav.tsx @@ -33,9 +33,9 @@ import { SidebarItem, } from '@backstage/core-components'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import LogoIcon from '../../../app/src/components/Root/LogoIcon'; +import LogoIcon from '../../../../packages/app/src/components/Root/LogoIcon'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import LogoFull from '../../../app/src/components/Root/LogoFull'; +import LogoFull from '../../../../packages/app/src/components/Root/LogoFull'; const useSidebarLogoStyles = makeStyles({ root: { diff --git a/packages/frontend-app-api/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx similarity index 69% rename from packages/frontend-app-api/src/extensions/AppRoot.tsx rename to plugins/app/src/extensions/AppRoot.tsx index a158386d04..bd786765f9 100644 --- a/packages/frontend-app-api/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -19,7 +19,6 @@ import React, { Fragment, PropsWithChildren, ReactNode, - useContext, useState, } from 'react'; import { @@ -27,21 +26,31 @@ import { RouterBlueprint, SignInPageBlueprint, coreExtensionData, + discoveryApiRef, + fetchApiRef, + errorApiRef, createExtension, createExtensionInput, + routeResolutionApiRef, } from '@backstage/frontend-plugin-api'; import { + DiscoveryApi, + ErrorApi, + FetchApi, IdentityApi, + ProfileInfo, SignInPageProps, configApiRef, + identityApiRef, useApi, } from '@backstage/core-plugin-api'; -import { InternalAppContext } from '../wiring/InternalAppContext'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; +import { isProtectedApp } from '../../../../packages/core-app-api/src/app/isProtectedApp'; import { BrowserRouter } from 'react-router-dom'; -import { RouteTracker } from '../routing/RouteTracker'; -import { getBasePath } from '../routing/getBasePath'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { RouteTracker } from '../../../../packages/frontend-app-api/src/routing/RouteTracker'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { getBasePath } from '../../../../packages/frontend-app-api/src/routing/getBasePath'; export const AppRoot = createExtension({ namespace: 'app', @@ -65,7 +74,28 @@ export const AppRoot = createExtension({ ]), }, output: [coreExtensionData.reactElement], - factory({ inputs }) { + factory({ inputs, apis }) { + if (isProtectedApp()) { + const identityApi = apis.get(identityApiRef); + if (!identityApi) { + throw new Error('App requires an Identity API implementation'); + } + const appIdentityProxy = toAppIdentityProxy(identityApi); + const discoveryApi = apis.get(discoveryApiRef); + const errorApi = apis.get(errorApiRef); + const fetchApi = apis.get(fetchApiRef); + if (!discoveryApi || !errorApi || !fetchApi) { + throw new Error( + 'App is running in protected mode but missing required APIs', + ); + } + appIdentityProxy.enableCookieAuth({ + discoveryApi, + errorApi, + fetchApi, + }); + } + let content: React.ReactNode = ( <> {inputs.elements.map(el => ( @@ -123,6 +153,33 @@ function SignInPageWrapper({ return <>{children}; } +type AppIdentityProxy = IdentityApi & { + enableCookieAuth(ctx: { + errorApi: ErrorApi; + fetchApi: FetchApi; + discoveryApi: DiscoveryApi; + }): void; + setTarget( + impl: IdentityApi & /* backwards compat stuff */ { + getUserId?(): string; + getIdToken?(): Promise; + getProfile?(): ProfileInfo; + }, + options: { signOutTargetUrl: string }, + ): void; +}; + +function toAppIdentityProxy(identityApi: IdentityApi): AppIdentityProxy { + if (!('enableCookieAuth' in identityApi)) { + throw new Error('Unexpected Identity API implementation'); + } + return identityApi as AppIdentityProxy; +} + +type RouteResolverProxy = { + getRouteObjects(): any[]; +}; + /** * Props for the {@link AppRouter} component. * @public @@ -157,12 +214,17 @@ export function AppRouter(props: AppRouterProps) { } = props; const configApi = useApi(configApiRef); + const appIdentityProxy = toAppIdentityProxy(useApi(identityApiRef)); + const routeResolutionsApi = useApi(routeResolutionApiRef); const basePath = getBasePath(configApi); - const internalAppContext = useContext(InternalAppContext); - if (!internalAppContext) { - throw new Error('AppRouter must be rendered within the AppProvider'); + + // TODO: Private access for now, probably replace with path -> node lookup method on the API + if (!('getRouteObjects' in routeResolutionsApi)) { + throw new Error('Unexpected route resolution API implementation'); } - const { routeObjects, appIdentityProxy } = internalAppContext; + const routeObjects = ( + routeResolutionsApi as RouteResolverProxy + ).getRouteObjects(); // If the app hasn't configured a sign-in page, we just continue as guest. if (!SignInPageComponent) { diff --git a/packages/frontend-app-api/src/extensions/AppRoutes.tsx b/plugins/app/src/extensions/AppRoutes.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/AppRoutes.tsx rename to plugins/app/src/extensions/AppRoutes.tsx diff --git a/packages/frontend-app-api/src/extensions/themes.tsx b/plugins/app/src/extensions/AppThemeApi.tsx similarity index 61% rename from packages/frontend-app-api/src/extensions/themes.tsx rename to plugins/app/src/extensions/AppThemeApi.tsx index a4fd0b253e..a31e29e58b 100644 --- a/packages/frontend-app-api/src/extensions/themes.tsx +++ b/plugins/app/src/extensions/AppThemeApi.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -21,7 +21,37 @@ import { } from '@backstage/theme'; import DarkIcon from '@material-ui/icons/Brightness2'; import LightIcon from '@material-ui/icons/WbSunny'; -import { ThemeBlueprint } from '@backstage/frontend-plugin-api'; +import { + createExtensionInput, + ThemeBlueprint, + ApiBlueprint, + createApiFactory, + appThemeApiRef, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { AppThemeSelector } from '../../../../packages/core-app-api/src/apis/implementations'; + +/** + * Contains the themes installed into the app. + */ +export const AppThemeApi = ApiBlueprint.makeWithOverrides({ + name: 'app-theme', + inputs: { + themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { + replaces: [{ id: 'app', input: 'themes' }], + }), + }, + factory: (originalFactory, { inputs }) => { + return originalFactory({ + factory: createApiFactory( + appThemeApiRef, + AppThemeSelector.createWithStorage( + inputs.themes.map(i => i.get(ThemeBlueprint.dataRefs.theme)), + ), + ), + }); + }, +}); export const LightTheme = ThemeBlueprint.make({ name: 'light', diff --git a/plugins/app/src/extensions/ComponentsApi.tsx b/plugins/app/src/extensions/ComponentsApi.tsx new file mode 100644 index 0000000000..b578429253 --- /dev/null +++ b/plugins/app/src/extensions/ComponentsApi.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2024 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 { + createComponentExtension, + createExtensionInput, + ApiBlueprint, + createApiFactory, + componentsApiRef, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { DefaultComponentsApi } from '../../../../packages/frontend-app-api/src/apis/implementations/ComponentsApi'; + +/** + * Contains the shareable components installed into the app. + */ +export const ComponentsApi = ApiBlueprint.makeWithOverrides({ + name: 'components', + inputs: { + components: createExtensionInput( + [createComponentExtension.componentDataRef], + { replaces: [{ id: 'app', input: 'components' }] }, + ), + }, + factory: (originalFactory, { inputs }) => { + return originalFactory({ + factory: createApiFactory( + componentsApiRef, + DefaultComponentsApi.fromComponents( + inputs.components.map(i => + i.get(createComponentExtension.componentDataRef), + ), + ), + ), + }); + }, +}); diff --git a/plugins/app/src/extensions/FeatureFlagsApi.ts b/plugins/app/src/extensions/FeatureFlagsApi.ts new file mode 100644 index 0000000000..40a44d1348 --- /dev/null +++ b/plugins/app/src/extensions/FeatureFlagsApi.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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 { + ApiBlueprint, + createApiFactory, + featureFlagsApiRef, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { LocalStorageFeatureFlags } from '../../../../packages/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags'; + +/** + * Contains the shareable icons installed into the app. + */ +export const FeatureFlagsApi = ApiBlueprint.make({ + name: 'feature-flags', + params: { + // TODO: properly discovery feature flags, maybe rework the whole thing + factory: createApiFactory({ + api: featureFlagsApiRef, + deps: {}, + factory: () => new LocalStorageFeatureFlags(), + }), + }, +}); diff --git a/plugins/app/src/extensions/IconsApi.ts b/plugins/app/src/extensions/IconsApi.ts new file mode 100644 index 0000000000..e73a74132a --- /dev/null +++ b/plugins/app/src/extensions/IconsApi.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2024 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 { + createExtensionInput, + IconBundleBlueprint, + ApiBlueprint, + createApiFactory, + iconsApiRef, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { DefaultIconsApi } from '../../../../packages/frontend-app-api/src/apis/implementations/IconsApi'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { icons as defaultIcons } from '../../../../packages/app-defaults/src/defaults'; + +/** + * Contains the shareable icons installed into the app. + */ +export const IconsApi = ApiBlueprint.makeWithOverrides({ + name: 'icons', + inputs: { + icons: createExtensionInput([IconBundleBlueprint.dataRefs.icons], { + replaces: [{ id: 'app', input: 'icons' }], + }), + }, + factory: (originalFactory, { inputs }) => { + return originalFactory({ + factory: createApiFactory( + iconsApiRef, + new DefaultIconsApi( + inputs.icons + .map(i => i.get(IconBundleBlueprint.dataRefs.icons)) + .reduce((acc, bundle) => ({ ...acc, ...bundle }), defaultIcons), + ), + ), + }); + }, +}); diff --git a/plugins/app/src/extensions/TranslationsApi.tsx b/plugins/app/src/extensions/TranslationsApi.tsx new file mode 100644 index 0000000000..ed7ab1abdb --- /dev/null +++ b/plugins/app/src/extensions/TranslationsApi.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2024 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 { + ApiBlueprint, + TranslationBlueprint, + createApiFactory, + createExtensionInput, +} from '@backstage/frontend-plugin-api'; +import { + appLanguageApiRef, + translationApiRef, +} from '@backstage/core-plugin-api/alpha'; + +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { I18nextTranslationApi } from '../../../../packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi'; + +/** + * Contains translations that are installed in the app. + */ +export const TranslationsApi = ApiBlueprint.makeWithOverrides({ + name: 'translations', + inputs: { + translations: createExtensionInput( + [TranslationBlueprint.dataRefs.translation], + { replaces: [{ id: 'app', input: 'translations' }] }, + ), + }, + factory: (originalFactory, { inputs }) => { + return originalFactory({ + factory: createApiFactory({ + api: translationApiRef, + deps: { languageApi: appLanguageApiRef }, + factory: ({ languageApi }) => + I18nextTranslationApi.create({ + languageApi, + resources: inputs.translations.map(i => + i.get(TranslationBlueprint.dataRefs.translation), + ), + }), + }), + }); + }, +}); diff --git a/packages/frontend-app-api/src/extensions/components.tsx b/plugins/app/src/extensions/components.tsx similarity index 92% rename from packages/frontend-app-api/src/extensions/components.tsx rename to plugins/app/src/extensions/components.tsx index 4f6574ab3e..829cb99ea2 100644 --- a/packages/frontend-app-api/src/extensions/components.tsx +++ b/plugins/app/src/extensions/components.tsx @@ -24,8 +24,7 @@ import { } from '@backstage/frontend-plugin-api'; import { ErrorPanel } from '@backstage/core-components'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { components as defaultComponents } from '../../../app-defaults/src/defaults'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { components as defaultComponents } from '../../../../packages/app-defaults/src/defaults'; export const DefaultProgressComponent = createComponentExtension({ ref: coreComponentRefs.progress, diff --git a/packages/frontend-app-api/src/extensions/elements.tsx b/plugins/app/src/extensions/elements.tsx similarity index 100% rename from packages/frontend-app-api/src/extensions/elements.tsx rename to plugins/app/src/extensions/elements.tsx diff --git a/plugins/app/src/extensions/index.ts b/plugins/app/src/extensions/index.ts new file mode 100644 index 0000000000..157f7ddb62 --- /dev/null +++ b/plugins/app/src/extensions/index.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 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 { App } from './App'; +export { AppLanguageApi } from './AppLanguageApi'; +export { AppLayout } from './AppLayout'; +export { AppNav } from './AppNav'; +export { AppRoot } from './AppRoot'; +export { AppRoutes } from './AppRoutes'; +export { AppThemeApi, DarkTheme, LightTheme } from './AppThemeApi'; +export { ComponentsApi } from './ComponentsApi'; +export { IconsApi } from './IconsApi'; +export { FeatureFlagsApi } from './FeatureFlagsApi'; +export { TranslationsApi } from './TranslationsApi'; +export { + DefaultProgressComponent, + DefaultErrorBoundaryComponent, + DefaultNotFoundErrorPageComponent, +} from './components'; +export { + oauthRequestDialogAppRootElement, + alertDisplayAppRootElement, +} from './elements'; diff --git a/packages/frontend-app-api/src/tree/index.ts b/plugins/app/src/index.ts similarity index 86% rename from packages/frontend-app-api/src/tree/index.ts rename to plugins/app/src/index.ts index 4b5ad5e867..24f65df618 100644 --- a/packages/frontend-app-api/src/tree/index.ts +++ b/plugins/app/src/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,4 +14,4 @@ * limitations under the License. */ -export { createAppTree } from './createAppTree'; +export { appPlugin as default } from './plugin'; diff --git a/packages/backend-app-api/src/services/implementations/identity/index.ts b/plugins/app/src/plugin.test.ts similarity index 75% rename from packages/backend-app-api/src/services/implementations/identity/index.ts rename to plugins/app/src/plugin.test.ts index bf2f4436dd..80588710a5 100644 --- a/packages/backend-app-api/src/services/implementations/identity/index.ts +++ b/plugins/app/src/plugin.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -13,6 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { appPlugin } from './plugin'; -export { identityServiceFactory } from './identityServiceFactory'; -export type { IdentityFactoryOptions } from './identityServiceFactory'; +describe('app', () => { + it('should export plugin', () => { + expect(appPlugin).toBeDefined(); + }); +}); diff --git a/plugins/app/src/plugin.ts b/plugins/app/src/plugin.ts new file mode 100644 index 0000000000..ff0c69faec --- /dev/null +++ b/plugins/app/src/plugin.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2024 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 { createFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { + App, + AppLanguageApi, + AppLayout, + AppNav, + AppRoot, + AppRoutes, + AppThemeApi, + DarkTheme, + LightTheme, + ComponentsApi, + IconsApi, + FeatureFlagsApi, + TranslationsApi, + DefaultProgressComponent, + DefaultNotFoundErrorPageComponent, + DefaultErrorBoundaryComponent, + oauthRequestDialogAppRootElement, + alertDisplayAppRootElement, +} from './extensions'; +import { apis } from './defaultApis'; + +/** @public */ +export const appPlugin = createFrontendPlugin({ + id: 'app', + extensions: [ + ...apis, + App, + AppLanguageApi, + AppLayout, + AppNav, + AppRoot, + AppRoutes, + AppThemeApi, + DarkTheme, + LightTheme, + ComponentsApi, + IconsApi, + FeatureFlagsApi, + TranslationsApi, + DefaultProgressComponent, + DefaultNotFoundErrorPageComponent, + DefaultErrorBoundaryComponent, + oauthRequestDialogAppRootElement, + alertDisplayAppRootElement, + ], +}); diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/index.ts b/plugins/app/src/setupTests.ts similarity index 82% rename from packages/backend-app-api/src/services/implementations/tokenManager/index.ts rename to plugins/app/src/setupTests.ts index 3ae3d45d2f..658016ffdd 100644 --- a/packages/backend-app-api/src/services/implementations/tokenManager/index.ts +++ b/plugins/app/src/setupTests.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export { tokenManagerServiceFactory } from './tokenManagerServiceFactory'; +import '@testing-library/jest-dom'; diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 779a96f093..1f053979f2 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/api-report.md b/plugins/auth-backend-module-atlassian-provider/api-report.md index 080206cdf7..7abdf7ee70 100644 --- a/plugins/auth-backend-module-atlassian-provider/api-report.md +++ b/plugins/auth-backend-module-atlassian-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -25,6 +25,6 @@ export namespace atlassianSignInResolvers { } // @public (undocumented) -const authModuleAtlassianProvider: BackendFeatureCompat; +const authModuleAtlassianProvider: BackendFeature; export default authModuleAtlassianProvider; ``` diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index 4be0473a17..cbdf2da557 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", - "version": "0.2.4", + "version": "0.3.0-next.0", "description": "The atlassian-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index 6f2dec2347..fc3193783b 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- ecbc47e: Fix a bug where the signer was checked from the payload instead of the header +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + ## 0.1.15 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/api-report.md b/plugins/auth-backend-module-aws-alb-provider/api-report.md index 78e7522069..b3a9ce4517 100644 --- a/plugins/auth-backend-module-aws-alb-provider/api-report.md +++ b/plugins/auth-backend-module-aws-alb-provider/api-report.md @@ -5,7 +5,7 @@ ```ts /// -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { JWTHeaderParameters } from 'jose'; import { KeyObject } from 'crypto'; import type { PassportProfile } from '@backstage/plugin-auth-node/'; @@ -13,7 +13,7 @@ import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleAwsAlbProvider: BackendFeatureCompat; +const authModuleAwsAlbProvider: BackendFeature; export { authModuleAwsAlbProvider }; export default authModuleAwsAlbProvider; diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index 7b3498ceb2..d4df833840 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", - "version": "0.1.15", + "version": "0.2.0-next.0", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts index c0e32d37a3..61c80c4b3c 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts @@ -35,7 +35,6 @@ describe('AwsAlbProvider', () => { email: 'user.name@email.test', exp: Date.now() + 10000, iss: 'ISSUER_URL', - signer: 'SIGNER_ARN', }; const signingKey = new TextEncoder().encode('signingKey'); let mockJwt: string; @@ -78,7 +77,7 @@ describe('AwsAlbProvider', () => { beforeEach(async () => { mockJwt = await new SignJWT(mockClaims) - .setProtectedHeader({ alg: 'HS256' }) + .setProtectedHeader({ alg: 'HS256', signer: 'SIGNER_ARN' }) .sign(signingKey); }); @@ -206,8 +205,8 @@ describe('AwsAlbProvider', () => { }); it('signer is invalid', async () => { - const jwt = await new SignJWT({ signer: 'INVALID_SIGNER_ARN' }) - .setProtectedHeader({ alg: 'HS256' }) + const jwt = await new SignJWT({}) + .setProtectedHeader({ alg: 'HS256', signer: 'INVALID_SIGNER_ARN' }) .sign(signingKey); const req = { header: jest.fn(name => { diff --git a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts index f62fe4175b..8a32b05a92 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts @@ -15,7 +15,7 @@ */ import { AuthenticationError } from '@backstage/errors'; -import { AwsAlbClaims, AwsAlbResult } from './types'; +import { AwsAlbClaims, AwsAlbResult, AwsAlbProtectedHeader } from './types'; import { jwtVerify } from 'jose'; import { PassportProfile, @@ -60,11 +60,12 @@ export const awsAlbAuthenticator = createProxyAuthenticator({ try { const verifyResult = await jwtVerify(jwt, getKey); + const header = verifyResult.protectedHeader as AwsAlbProtectedHeader; const claims = verifyResult.payload as AwsAlbClaims; if (claims?.iss !== issuer) { throw new AuthenticationError('Issuer mismatch on JWT token'); - } else if (signer && claims?.signer !== signer) { + } else if (signer && header?.signer !== signer) { throw new AuthenticationError('Signer mismatch on JWT token'); } diff --git a/plugins/auth-backend-module-aws-alb-provider/src/types.ts b/plugins/auth-backend-module-aws-alb-provider/src/types.ts index 679535804e..b4c792a5f9 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/types.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/types.ts @@ -38,5 +38,10 @@ export type AwsAlbClaims = { email: string; exp: number; iss: string; - signer: string; +}; +/** + * @internal + */ +export type AwsAlbProtectedHeader = { + signer?: string; }; diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index 3961bab52a..f9f72069bb 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + ## 0.1.6 ### Patch Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/api-report.md b/plugins/auth-backend-module-azure-easyauth-provider/api-report.md index cb198f54db..9d337e3f34 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/api-report.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/api-report.md @@ -3,13 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Profile } from 'passport'; import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleAzureEasyAuthProvider: BackendFeatureCompat; +const authModuleAzureEasyAuthProvider: BackendFeature; export default authModuleAzureEasyAuthProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index df83acff7b..920efa816f 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/package.json +++ b/plugins/auth-backend-module-azure-easyauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-azure-easyauth-provider", - "version": "0.1.6", + "version": "0.2.0-next.0", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index ff2c8725be..16f7b29526 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.1.6 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/api-report.md b/plugins/auth-backend-module-bitbucket-provider/api-report.md index 6b71f62c69..ceb4141fd9 100644 --- a/plugins/auth-backend-module-bitbucket-provider/api-report.md +++ b/plugins/auth-backend-module-bitbucket-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleBitbucketProvider: BackendFeatureCompat; +const authModuleBitbucketProvider: BackendFeature; export default authModuleBitbucketProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index ed8cfa981a..17869169d1 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-provider", - "version": "0.1.6", + "version": "0.2.0-next.0", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index 51f1ce5b17..28f18baf79 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.2.0 ### Minor Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/api-report.md b/plugins/auth-backend-module-cloudflare-access-provider/api-report.md index b728f52046..1547190d39 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/api-report.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/api-report.md @@ -3,13 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CacheService } from '@backstage/backend-plugin-api'; import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public -const authModuleCloudflareAccessProvider: BackendFeatureCompat; +const authModuleCloudflareAccessProvider: BackendFeature; export default authModuleCloudflareAccessProvider; // @public diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index 646bf720a2..7f5c8b97f4 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/package.json +++ b/plugins/auth-backend-module-cloudflare-access-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider", - "version": "0.2.0", + "version": "0.3.0-next.0", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 4099ad1c09..ea27e5f2dc 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.2.18 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/api-report.md b/plugins/auth-backend-module-gcp-iap-provider/api-report.md index ccde0b8809..1476694f99 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/api-report.md +++ b/plugins/auth-backend-module-gcp-iap-provider/api-report.md @@ -3,13 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { JsonPrimitive } from '@backstage/types'; import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleGcpIapProvider: BackendFeatureCompat; +const authModuleGcpIapProvider: BackendFeature; export default authModuleGcpIapProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 41c8ccc2b8..9ae551c9ce 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", - "version": "0.2.18", + "version": "0.3.0-next.0", "description": "A GCP IAP auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 46387e4421..cab470870e 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.1.20 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/api-report.md b/plugins/auth-backend-module-github-provider/api-report.md index ed8b615033..222305adfd 100644 --- a/plugins/auth-backend-module-github-provider/api-report.md +++ b/plugins/auth-backend-module-github-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleGithubProvider: BackendFeatureCompat; +const authModuleGithubProvider: BackendFeature; export default authModuleGithubProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 5e0f0d527b..75b6b39f0a 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", - "version": "0.1.20", + "version": "0.2.0-next.0", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 0a9186b385..cc50ac6618 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.1.20 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/api-report.md b/plugins/auth-backend-module-gitlab-provider/api-report.md index 664d04e856..44c5ffb214 100644 --- a/plugins/auth-backend-module-gitlab-provider/api-report.md +++ b/plugins/auth-backend-module-gitlab-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleGitlabProvider: BackendFeatureCompat; +const authModuleGitlabProvider: BackendFeature; export default authModuleGitlabProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 4c48fd014c..6fa019c9d2 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", - "version": "0.1.20", + "version": "0.2.0-next.0", "description": "The gitlab-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 3eb7e65ec6..3bfd7f2feb 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.1.20 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/api-report.md b/plugins/auth-backend-module-google-provider/api-report.md index 5c6735c117..eeefb8e837 100644 --- a/plugins/auth-backend-module-google-provider/api-report.md +++ b/plugins/auth-backend-module-google-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleGoogleProvider: BackendFeatureCompat; +const authModuleGoogleProvider: BackendFeature; export default authModuleGoogleProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index 307246514f..f81459cf1f 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", - "version": "0.1.20", + "version": "0.2.0-next.0", "description": "A Google auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index 26dabc7b5c..9c01cc51b4 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + ## 0.1.9 ### Patch Changes diff --git a/plugins/auth-backend-module-guest-provider/api-report.md b/plugins/auth-backend-module-guest-provider/api-report.md index 85a067affa..b0ba5386a2 100644 --- a/plugins/auth-backend-module-guest-provider/api-report.md +++ b/plugins/auth-backend-module-guest-provider/api-report.md @@ -3,9 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public (undocumented) -const authModuleGuestProvider: BackendFeatureCompat; +const authModuleGuestProvider: BackendFeature; export default authModuleGuestProvider; ``` diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index d1b8caf3eb..70ee3926da 100644 --- a/plugins/auth-backend-module-guest-provider/package.json +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-guest-provider", - "version": "0.1.9", + "version": "0.2.0-next.0", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 2c3e4f6d33..0a50d8c7a6 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 3c2d690: Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.1.18 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/api-report.md b/plugins/auth-backend-module-microsoft-provider/api-report.md index 71a97579d5..6aef395d7c 100644 --- a/plugins/auth-backend-module-microsoft-provider/api-report.md +++ b/plugins/auth-backend-module-microsoft-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,10 +11,10 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public @deprecated (undocumented) -export const authModuleMicrosoftProvider: BackendFeatureCompat; +export const authModuleMicrosoftProvider: BackendFeature; // @public (undocumented) -const authModuleMicrosoftProvider_2: BackendFeatureCompat; +const authModuleMicrosoftProvider_2: BackendFeature; export default authModuleMicrosoftProvider_2; // @public (undocumented) @@ -32,5 +32,9 @@ export namespace microsoftSignInResolvers { OAuthAuthenticatorResult, unknown >; + const userIdMatchingUserEntityAnnotation: SignInResolverFactory< + OAuthAuthenticatorResult, + unknown + >; } ``` diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 8a1537771b..bd956fca14 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", - "version": "0.1.18", + "version": "0.2.0-next.0", "description": "The microsoft-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts b/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts index 7ef4a57175..db7aa4f7f3 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts @@ -28,7 +28,7 @@ import { */ export namespace microsoftSignInResolvers { /** - * Looks up the user by matching their Microsoft username to the entity name. + * Looks up the user by matching their Microsoft email to the email entity annotation. */ export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ create() { @@ -50,4 +50,31 @@ export namespace microsoftSignInResolvers { }; }, }); + /** + * Looks up the user by matching their Microsoft user id to the user id entity annotation. + */ + export const userIdMatchingUserEntityAnnotation = createSignInResolverFactory( + { + create() { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { result } = info; + + const id = result.fullProfile.id; + + if (!id) { + throw new Error('Microsoft profile contained no id'); + } + + return ctx.signInWithCatalogUser({ + annotations: { + 'graph.microsoft.com/user-id': id, + }, + }); + }; + }, + }, + ); } diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 7762658a12..590aa37742 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/api-report.md b/plugins/auth-backend-module-oauth2-provider/api-report.md index 31bfddf18e..632591ec04 100644 --- a/plugins/auth-backend-module-oauth2-provider/api-report.md +++ b/plugins/auth-backend-module-oauth2-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleOauth2Provider: BackendFeatureCompat; +const authModuleOauth2Provider: BackendFeature; export default authModuleOauth2Provider; // @public (undocumented) diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index d37a00fccc..ce8cab68c3 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", - "version": "0.2.4", + "version": "0.3.0-next.0", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index a10e533977..47ad278b68 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + ## 0.1.16 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md b/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md index e7295bc65b..5c5cefd734 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md @@ -5,12 +5,12 @@ ```ts /// -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { IncomingHttpHeaders } from 'http'; import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleOauth2ProxyProvider: BackendFeatureCompat; +const authModuleOauth2ProxyProvider: BackendFeature; export default authModuleOauth2ProxyProvider; // @public diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index 2235784779..63f13f5844 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", - "version": "0.1.16", + "version": "0.2.0-next.0", "description": "The oauth2-proxy-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 36fbf7e8c4..51ac80e309 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-backend@0.23.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/api-report.md b/plugins/auth-backend-module-oidc-provider/api-report.md index 0e6f807be4..cd0a99e7fc 100644 --- a/plugins/auth-backend-module-oidc-provider/api-report.md +++ b/plugins/auth-backend-module-oidc-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { BaseClient } from 'openid-client'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -13,7 +13,7 @@ import { TokenSet } from 'openid-client'; import { UserinfoResponse } from 'openid-client'; // @public (undocumented) -const authModuleOidcProvider: BackendFeatureCompat; +const authModuleOidcProvider: BackendFeature; export default authModuleOidcProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index a90908c491..bf7091547b 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", - "version": "0.2.4", + "version": "0.3.0-next.0", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index b66256f002..c588e5b98d 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.1.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.0.16 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/api-report.md b/plugins/auth-backend-module-okta-provider/api-report.md index 7676d420a8..a142265100 100644 --- a/plugins/auth-backend-module-okta-provider/api-report.md +++ b/plugins/auth-backend-module-okta-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleOktaProvider: BackendFeatureCompat; +const authModuleOktaProvider: BackendFeature; export default authModuleOktaProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index d1330f2457..1659bc3824 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", - "version": "0.0.16", + "version": "0.1.0-next.0", "description": "The okta-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md index 7302199487..8ac77804b1 100644 --- a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-onelogin-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/auth-backend-module-onelogin-provider/api-report.md b/plugins/auth-backend-module-onelogin-provider/api-report.md index be4a88dba1..1991f00e0b 100644 --- a/plugins/auth-backend-module-onelogin-provider/api-report.md +++ b/plugins/auth-backend-module-onelogin-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -11,7 +11,7 @@ import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) -const authModuleOneLoginProvider: BackendFeatureCompat; +const authModuleOneLoginProvider: BackendFeature; export default authModuleOneLoginProvider; // @public (undocumented) diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index 2e0a997e77..bd3e332633 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-onelogin-provider", - "version": "0.1.4", + "version": "0.2.0-next.0", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 79ab8db9d5..5fb39c0962 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + ## 0.1.17 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/api-report.md b/plugins/auth-backend-module-pinniped-provider/api-report.md index 8d1380daff..e15f4ee324 100644 --- a/plugins/auth-backend-module-pinniped-provider/api-report.md +++ b/plugins/auth-backend-module-pinniped-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { BaseClient } from 'openid-client'; import { Config } from '@backstage/config'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; @@ -11,10 +11,10 @@ import { Strategy } from 'openid-client'; import { TokenSet } from 'openid-client'; // @public @deprecated (undocumented) -export const authModulePinnipedProvider: BackendFeatureCompat; +export const authModulePinnipedProvider: BackendFeature; // @public (undocumented) -const authModulePinnipedProvider_2: BackendFeatureCompat; +const authModulePinnipedProvider_2: BackendFeature; export default authModulePinnipedProvider_2; // @public (undocumented) diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index bafbd82855..88612c827b 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", - "version": "0.1.17", + "version": "0.2.0-next.0", "description": "The pinniped-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index c7eefd4d0e..3083f74262 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/catalog-model@1.6.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/api-report.md b/plugins/auth-backend-module-vmware-cloud-provider/api-report.md index 32d706f130..2a27d8fc7c 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/api-report.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; @@ -12,7 +12,7 @@ import { SignInResolverFactory } from '@backstage/plugin-auth-node'; import { Strategy } from 'passport-oauth2'; // @public -const authModuleVmwareCloudProvider: BackendFeatureCompat; +const authModuleVmwareCloudProvider: BackendFeature; export default authModuleVmwareCloudProvider; // @public diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 7386056a3c..62330f1bed 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", - "version": "0.2.4", + "version": "0.3.0-next.0", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 925923d2fd..2e28c0c410 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-auth-backend +## 0.23.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- 3c2d690: Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. +- Updated dependencies + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.0-next.0 + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-google-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.0-next.0 + - @backstage/plugin-auth-backend-module-oidc-provider@0.3.0-next.0 + - @backstage/plugin-auth-backend-module-okta-provider@0.1.0-next.0 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.2.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.22.10 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index f801c92597..51f80024ae 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -12,7 +12,7 @@ import { AuthResolverContext as AuthResolverContext_2 } from '@backstage/plugin- import { AuthService } from '@backstage/backend-plugin-api'; import { AwsAlbResult as AwsAlbResult_2 } from '@backstage/plugin-auth-backend-module-aws-alb-provider'; import { AzureEasyAuthResult } from '@backstage/plugin-auth-backend-module-azure-easyauth-provider'; -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { BackstageSignInResult } from '@backstage/plugin-auth-node'; import { CacheService } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; @@ -41,7 +41,6 @@ import { RootConfigService } from '@backstage/backend-plugin-api'; import { SignInInfo as SignInInfo_2 } from '@backstage/plugin-auth-node'; import { SignInResolver as SignInResolver_2 } from '@backstage/plugin-auth-node'; import { TokenManager } from '@backstage/backend-common'; -import { TokenManagerService } from '@backstage/backend-plugin-api'; import { TokenParams as TokenParams_2 } from '@backstage/plugin-auth-node'; import { UserEntity } from '@backstage/catalog-model'; import { WebMessageResponse as WebMessageResponse_2 } from '@backstage/plugin-auth-node'; @@ -58,7 +57,7 @@ export type AuthHandlerResult = { }; // @public -const authPlugin: BackendFeatureCompat; +const authPlugin: BackendFeature; export default authPlugin; // @public @deprecated (undocumented) @@ -126,7 +125,7 @@ export type BitbucketServerOAuthResult = { export class CatalogIdentityClient { constructor(options: { catalogApi: CatalogApi; - tokenManager: TokenManager; + tokenManager?: TokenManager; discovery: DiscoveryService; auth?: AuthService; httpAuth?: HttpAuthService; @@ -534,6 +533,7 @@ export const providers: Readonly<{ resolvers: Readonly<{ emailMatchingUserEntityProfileEmail: () => SignInResolver_2; emailLocalPartMatchingUserEntityName: () => SignInResolver_2; + userIdMatchingUserEntityAnnotation: () => SignInResolver_2; emailMatchingUserEntityAnnotation: () => SignInResolver_2; }>; }>; @@ -673,7 +673,7 @@ export interface RouterOptions { // (undocumented) tokenFactoryAlgorithm?: string; // (undocumented) - tokenManager: TokenManagerService; + tokenManager?: TokenManager; } // @public (undocumented) diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 8983d0983e..177478fdb9 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.22.10", + "version": "0.23.0-next.0", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-backend/src/authPlugin.ts b/plugins/auth-backend/src/authPlugin.ts index 7687462d32..89b7e9a780 100644 --- a/plugins/auth-backend/src/authPlugin.ts +++ b/plugins/auth-backend/src/authPlugin.ts @@ -65,7 +65,6 @@ export const authPlugin = createBackendPlugin({ config: coreServices.rootConfig, database: coreServices.database, discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, auth: coreServices.auth, httpAuth: coreServices.httpAuth, catalogApi: catalogServiceRef, @@ -76,7 +75,6 @@ export const authPlugin = createBackendPlugin({ config, database, discovery, - tokenManager, auth, httpAuth, catalogApi, @@ -86,7 +84,6 @@ export const authPlugin = createBackendPlugin({ config, database, discovery, - tokenManager, auth, httpAuth, catalogApi, diff --git a/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts b/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts index ef3d0f6c8b..4d696aa8f2 100644 --- a/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts +++ b/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { resolvePackagePath } from '@backstage/backend-common'; +import { resolvePackagePath } from '@backstage/backend-plugin-api'; import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { Knex } from 'knex'; import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts index 51c72eeb63..e91108dbf2 100644 --- a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts @@ -45,7 +45,7 @@ export class CatalogIdentityClient { constructor(options: { catalogApi: CatalogApi; - tokenManager: TokenManager; + tokenManager?: TokenManager; discovery: DiscoveryService; auth?: AuthService; httpAuth?: HttpAuthService; diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts index a69c9c90d4..7fb759200c 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts @@ -34,7 +34,6 @@ describe('CatalogAuthResolverContext', () => { logger: mockServices.logger.mock(), catalogApi: mockCatalogApi as CatalogApi, tokenIssuer: {} as TokenIssuer, - tokenManager: mockServices.tokenManager(), discovery: {} as DiscoveryService, auth: mockServices.auth(), httpAuth: mockServices.httpAuth(), diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index c81ac4c355..51b374b468 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -66,7 +66,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { logger: LoggerService; catalogApi: CatalogApi; tokenIssuer: TokenIssuer; - tokenManager: TokenManager; + tokenManager?: TokenManager; discovery: DiscoveryService; auth: AuthService; httpAuth: HttpAuthService; diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index f6a3a83bcb..f0b019d1c7 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -65,5 +65,7 @@ export const microsoft = createAuthProviderIntegration({ commonSignInResolvers.emailMatchingUserEntityProfileEmail(), emailMatchingUserEntityAnnotation: microsoftSignInResolvers.emailMatchingUserEntityAnnotation(), + userIdMatchingUserEntityAnnotation: + microsoftSignInResolvers.userIdMatchingUserEntityAnnotation(), }), }); diff --git a/plugins/auth-backend/src/providers/router.ts b/plugins/auth-backend/src/providers/router.ts index d84133685c..e449372143 100644 --- a/plugins/auth-backend/src/providers/router.ts +++ b/plugins/auth-backend/src/providers/router.ts @@ -50,7 +50,7 @@ export function bindProviderRouters( discovery: PluginEndpointDiscovery; auth: AuthService; httpAuth: HttpAuthService; - tokenManager: TokenManager; + tokenManager?: TokenManager; tokenIssuer: TokenIssuer; ownershipResolver?: AuthOwnershipResolver; catalogApi?: CatalogApi; diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index b954eb4dbb..af34ffc310 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -24,11 +24,13 @@ import { HttpAuthService, LoggerService, RootConfigService, - TokenManagerService, } from '@backstage/backend-plugin-api'; import { defaultAuthProviderFactories } from '../providers'; import { AuthOwnershipResolver } from '@backstage/plugin-auth-node'; -import { createLegacyAuthAdapters } from '@backstage/backend-common'; +import { + TokenManager, + createLegacyAuthAdapters, +} from '@backstage/backend-common'; import { NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; import { @@ -56,7 +58,7 @@ export interface RouterOptions { database: DatabaseService; config: RootConfigService; discovery: DiscoveryService; - tokenManager: TokenManagerService; + tokenManager?: TokenManager; auth?: AuthService; httpAuth?: HttpAuthService; tokenFactoryAlgorithm?: string; diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 79c783d814..c3773c052e 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-auth-node +## 0.5.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.5.0 ### Minor Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 781d930c21..82ffbc4c7a 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.5.0", + "version": "0.5.2-next.0", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index dcad12d670..5c09b5789a 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.4.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-aws/api-report-alpha.md b/plugins/catalog-backend-module-aws/api-report-alpha.md index 1c01744215..495abb6b48 100644 --- a/plugins/catalog-backend-module-aws/api-report-alpha.md +++ b/plugins/catalog-backend-module-aws/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const catalogModuleAwsS3EntityProvider: BackendFeatureCompat; +const catalogModuleAwsS3EntityProvider: BackendFeature; export default catalogModuleAwsS3EntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 2de0cf81a7..66afec1f99 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.0", + "version": "0.4.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 1d9e799c20..2f750f48e8 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-azure/api-report-alpha.md b/plugins/catalog-backend-module-azure/api-report-alpha.md index 8f6c4f0317..fc48df304a 100644 --- a/plugins/catalog-backend-module-azure/api-report-alpha.md +++ b/plugins/catalog-backend-module-azure/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const catalogModuleAzureDevOpsEntityProvider: BackendFeatureCompat; +const catalogModuleAzureDevOpsEntityProvider: BackendFeature; export default catalogModuleAzureDevOpsEntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 186e12e8e4..8d27517cf5 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.2.0", + "version": "0.2.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index ccee7e4572..65d193523b 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.3.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/api-report.md b/plugins/catalog-backend-module-backstage-openapi/api-report.md index 247cc4ba8e..06334182cb 100644 --- a/plugins/catalog-backend-module-backstage-openapi/api-report.md +++ b/plugins/catalog-backend-module-backstage-openapi/api-report.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public (undocumented) -const catalogModuleInternalOpenApiSpec: BackendFeatureCompat; +const catalogModuleInternalOpenApiSpec: BackendFeature; export { catalogModuleInternalOpenApiSpec }; export default catalogModuleInternalOpenApiSpec; diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index ca1e220c62..97517b0b43 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.3.0", + "version": "0.4.0-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 05b0f40fb7..27ee19cb99 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.3.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.3.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/api-report-alpha.md b/plugins/catalog-backend-module-bitbucket-cloud/api-report-alpha.md index 8c91e1454f..34ec7a692a 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/api-report-alpha.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha (undocumented) -const catalogModuleBitbucketCloudEntityProvider: BackendFeatureCompat; +const catalogModuleBitbucketCloudEntityProvider: BackendFeature; export default catalogModuleBitbucketCloudEntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 13ee89e71c..59afec4970 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.3.0", + "version": "0.3.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.test.ts index b3da110323..12c747d090 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.test.ts @@ -58,7 +58,7 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => { [catalogProcessingExtensionPoint, catalogExtensionPointImpl], ], features: [ - eventsServiceFactory(), + eventsServiceFactory, catalogModuleBitbucketCloudEntityProvider, mockServices.rootConfig.factory({ data: { diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts index 21d86ce8e6..179e74b8c0 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts @@ -40,23 +40,13 @@ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({ events: eventsServiceRef, logger: coreServices.logger, scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, }, - async init({ - catalog, - catalogApi, - config, - events, - logger, - scheduler, - tokenManager, - }) { + async init({ catalog, catalogApi, config, events, logger, scheduler }) { const providers = BitbucketCloudEntityProvider.fromConfig(config, { catalogApi, events, logger, scheduler, - tokenManager, }); catalog.addEntityProvider(providers); diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 5946dace67..1260480994 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/api-report-alpha.md b/plugins/catalog-backend-module-bitbucket-server/api-report-alpha.md index 0f8da2c4d5..7edb314814 100644 --- a/plugins/catalog-backend-module-bitbucket-server/api-report-alpha.md +++ b/plugins/catalog-backend-module-bitbucket-server/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha (undocumented) -const catalogModuleBitbucketServerEntityProvider: BackendFeatureCompat; +const catalogModuleBitbucketServerEntityProvider: BackendFeature; export default catalogModuleBitbucketServerEntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index c53dd1a1c3..58719409ce 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.2.0", + "version": "0.2.2-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 7b6ca7f55e..606ead08be 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gcp/api-report.md b/plugins/catalog-backend-module-gcp/api-report.md index ea9a73f0e6..6260d07c88 100644 --- a/plugins/catalog-backend-module-gcp/api-report.md +++ b/plugins/catalog-backend-module-gcp/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import * as container from '@google-cloud/container'; import { EntityProvider } from '@backstage/plugin-catalog-node'; @@ -12,7 +12,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; // @public -const catalogModuleGcpGkeEntityProvider: BackendFeatureCompat; +const catalogModuleGcpGkeEntityProvider: BackendFeature; export default catalogModuleGcpGkeEntityProvider; // @public diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 4793e6b167..d48a43ad9a 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.2.0", + "version": "0.3.0-next.0", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 74977e4c9f..d19ce42b01 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gerrit/api-report-alpha.md b/plugins/catalog-backend-module-gerrit/api-report-alpha.md index 60167b95fa..02aceb07c6 100644 --- a/plugins/catalog-backend-module-gerrit/api-report-alpha.md +++ b/plugins/catalog-backend-module-gerrit/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha (undocumented) -const catalogModuleGerritEntityProvider: BackendFeatureCompat; +const catalogModuleGerritEntityProvider: BackendFeature; export default catalogModuleGerritEntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 6b6c9d1a5c..fa395efa22 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.2.0", + "version": "0.2.2-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index c130f00d2b..db1b68c801 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-backend-module-github@0.7.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/config@1.2.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github-org/api-report.md b/plugins/catalog-backend-module-github-org/api-report.md index c0309c9ccf..44fa408e35 100644 --- a/plugins/catalog-backend-module-github-org/api-report.md +++ b/plugins/catalog-backend-module-github-org/api-report.md @@ -3,14 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { GithubMultiOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-github'; import { TeamTransformer } from '@backstage/plugin-catalog-backend-module-github'; import { UserTransformer } from '@backstage/plugin-catalog-backend-module-github'; // @public -const catalogModuleGithubOrgEntityProvider: BackendFeatureCompat; +const catalogModuleGithubOrgEntityProvider: BackendFeature; export default catalogModuleGithubOrgEntityProvider; export { GithubMultiOrgEntityProvider }; diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index a902d80c47..b738010b4a 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.2.0", + "version": "0.3.0-next.0", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 4d7a38f5d7..502bc5879e 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-github +## 0.7.3-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.7.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github/api-report-alpha.md b/plugins/catalog-backend-module-github/api-report-alpha.md index 2f9e12d24a..faa5423d82 100644 --- a/plugins/catalog-backend-module-github/api-report-alpha.md +++ b/plugins/catalog-backend-module-github/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const githubCatalogModule: BackendFeatureCompat; +const githubCatalogModule: BackendFeature; export default githubCatalogModule; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 81cc498ce5..adc448cf27 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.7.0", + "version": "0.7.3-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index b6000e7a6d..321c7e1732 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend-module-gitlab@0.4.2-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gitlab-org/api-report.md b/plugins/catalog-backend-module-gitlab-org/api-report.md index d9c5c62b52..b3af579255 100644 --- a/plugins/catalog-backend-module-gitlab-org/api-report.md +++ b/plugins/catalog-backend-module-gitlab-org/api-report.md @@ -3,9 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public -const catalogModuleGitlabOrgDiscoveryEntityProvider: BackendFeatureCompat; +const catalogModuleGitlabOrgDiscoveryEntityProvider: BackendFeature; export default catalogModuleGitlabOrgDiscoveryEntityProvider; ``` diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index ba4fcd844b..a97b1bffb1 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.1.0", + "version": "0.2.0-next.0", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/src/catalogModuleGitlabOrgDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab-org/src/catalogModuleGitlabOrgDiscoveryEntityProvider.test.ts index 1710467d24..107de6b7b3 100644 --- a/plugins/catalog-backend-module-gitlab-org/src/catalogModuleGitlabOrgDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab-org/src/catalogModuleGitlabOrgDiscoveryEntityProvider.test.ts @@ -83,7 +83,7 @@ describe('catalogModuleGitlabOrgDiscoveryEntityProvider', () => { await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], features: [ - eventsServiceFactory(), + eventsServiceFactory, catalogModuleGitlabOrgDiscoveryEntityProvider, mockServices.rootConfig.factory({ data: config }), mockServices.logger.factory(), diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index e8e40a7391..6740abb6a6 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.4.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- b446954: Remove dependency on backend-common +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.4.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gitlab/api-report-alpha.md b/plugins/catalog-backend-module-gitlab/api-report-alpha.md index 1c27e1ee6f..0b1c7df3cc 100644 --- a/plugins/catalog-backend-module-gitlab/api-report-alpha.md +++ b/plugins/catalog-backend-module-gitlab/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const catalogModuleGitlabDiscoveryEntityProvider: BackendFeatureCompat; +const catalogModuleGitlabDiscoveryEntityProvider: BackendFeature; export default catalogModuleGitlabDiscoveryEntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 1d5000e7c5..4a890cf3bd 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.4.0", + "version": "0.4.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", @@ -51,7 +51,7 @@ "test": "backstage-cli package test" }, "dependencies": { - "@backstage/backend-common": "workspace:^", + "@backstage/backend-defaults": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts index b8e0cf3669..29914db705 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts @@ -14,11 +14,6 @@ * limitations under the License. */ -import { - CacheClient, - CacheManager, - PluginCacheManager, -} from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { ScmIntegrationRegistry, @@ -31,7 +26,11 @@ import { processingResult, } from '@backstage/plugin-catalog-node'; import { GitLabClient, GitLabProject, paginated } from './lib'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { CacheService, LoggerService } from '@backstage/backend-plugin-api'; +import { + CacheManager, + PluginCacheManager, +} from '@backstage/backend-defaults/cache'; /** * Extracts repositories out of an GitLab instance. @@ -40,7 +39,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; export class GitLabDiscoveryProcessor implements CatalogProcessor { private readonly integrations: ScmIntegrationRegistry; private readonly logger: LoggerService; - private readonly cache: CacheClient; + private readonly cache: CacheService; private readonly skipReposWithoutExactFileMatch: boolean; private readonly skipForkedRepos: boolean; diff --git a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts index 944019cc69..7c9fc068a0 100644 --- a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts @@ -82,7 +82,7 @@ describe('catalogModuleGitlabDiscoveryEntityProvider', () => { await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], features: [ - eventsServiceFactory(), + eventsServiceFactory, catalogModuleGitlabDiscoveryEntityProvider, mockServices.rootConfig.factory({ data: config }), mockServices.logger.factory(), diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index b46b77b0c1..80bbdc5e7d 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.5.3-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 4b28e39: Updated the README to include documentation for the new backend support +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + ## 0.5.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/README.md b/plugins/catalog-backend-module-incremental-ingestion/README.md index 0894d32031..3b847d4ad7 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/README.md +++ b/plugins/catalog-backend-module-incremental-ingestion/README.md @@ -42,57 +42,25 @@ The Incremental Entity Provider backend is designed for data sources that provid ## Installation 1. Install `@backstage/plugin-catalog-backend-module-incremental-ingestion` with `yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-incremental-ingestion` from the Backstage root directory. -2. In your catalog.ts, import `IncrementalCatalogBuilder` from `@backstage/plugin-catalog-backend-module-incremental-ingestion` and instantiate it with `await IncrementalCatalogBuilder.create(env, builder)`. You have to pass `builder` into `IncrementalCatalogBuilder.create` function because `IncrementalCatalogBuilder` will convert an `IncrementalEntityProvider` into an `EntityProvider` and call `builder.addEntityProvider`. -```ts -const builder = CatalogBuilder.create(env); -// incremental builder receives builder because it'll register -// incremental entity providers with the builder -const incrementalBuilder = await IncrementalCatalogBuilder.create(env, builder); -``` +2. Add the following code to the `packages/backend/src/index.ts` file: -3. After building the regular `CatalogBuilder`, build the incremental builder: +```diff ++ import { catalogModuleCustomIncrementalIngestionProvider } from './extensions/catalogCustomIncrementalIngestion'; -```ts -// Must be run first to ensure CatalogBuilder database migrations run before Incremental Entity Provider database migrations -const { processingEngine, router } = await builder.build(); -// Returns an optional - but highly recommended - set of administrative routes -const { incrementalAdminRouter } = await incrementBuilder.build(); -``` +const backend = createBackend(); -The final result should look something like this, ++ backend.add( ++ import( ++ '@backstage/plugin-catalog-backend-module-incremental-ingestion/alpha' ++ ), ++ ); -```ts -import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; -import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend'; -import { IncrementalCatalogBuilder } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; -import { Router } from 'express'; -import { Duration } from 'luxon'; -import { PluginEnvironment } from '../types'; +// We have created this in section **Adding an Incremental Entity Provider to the catalog** ++ backend.add(catalogModuleCustomIncrementalIngestionProvider); -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const builder = CatalogBuilder.create(env); - // incremental builder receives builder because it'll register - // incremental entity providers with the builder - const incrementalBuilder = await IncrementalCatalogBuilder.create( - env, - builder, - ); - - builder.addProcessor(new ScaffolderEntitiesProcessor()); - - const { processingEngine, router } = await builder.build(); - const { incrementalAdminRouter } = await incrementalBuilder.build(); - - router.use(incrementalAdminRouter); - - await processingEngine.start(); - - return router; -} +backend.start(); ``` ## Administrative Routes @@ -116,7 +84,7 @@ In all cases, `:provider` is the name of the incremental entity provider. ## Writing an Incremental Entity Provider -To create an Incremental Entity Provider, you need to know how to retrieve a single page of the data that you wish to ingest into the Backstage catalog. If the API has pagination and you know how to make a paginated request to that API, you'll be able to implement an Incremental Entity Provider for this API. For more information about compatibility, check out the requirements section of this page. +To create an Incremental Entity Provider, you need to know how to retrieve a single page of the data that you wish to ingest into the Backstage catalog. If the API has pagination and you know how to make a paginated request to that API, you'll be able to implement an Incremental Entity Provider for this API. For more information about compatibility, check out the [requirements](#requirements) section of this page. Here is the type definition for an Incremental Entity Provider. @@ -309,45 +277,73 @@ Now that you have your new Incremental Entity Provider, we can connect it to the ## Adding an Incremental Entity Provider to the catalog -We'll assume you followed the Installation instructions. After you create your `incrementalBuilder`, you can instantiate your Entity Provider and pass it to the `addIncrementalEntityProvider` method. +We'll assume you followed the [Installation](#installation) instructions. Now create a module inside `packages/backend/src/extensions/catalogCustomIncrementalIngestion.ts`. ```ts -const incrementalBuilder = await IncrementalCatalogBuilder.create(env, builder); +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { incrementalIngestionProvidersExtensionPoint } from '@backstage/plugin-catalog-backend-module-incremental-ingestion/alpha'; -// Assuming the token for the API comes from config -const token = config.getString('myApiClient.token'); +export const catalogModuleCustomIncrementalIngestionProvider = + createBackendModule({ + pluginId: 'catalog', + moduleId: 'custom-incremental-ingestion-provider', + register(env) { + env.registerInit({ + deps: { + incrementalBuilder: incrementalIngestionProvidersExtensionPoint, + config: coreServices.rootConfig, + }, + async init({ incrementalBuilder, config }) { + // Assuming the token for the API comes from config + const token = config.getString('myApiClient.token'); + const myEntityProvider = new MyIncrementalEntityProvider(token); -const myEntityProvider = new MyIncrementalEntityProvider(token); + const options = { + // How long should it attempt to read pages from the API in a + // single burst? Keep this short. The Incremental Entity Provider + // will attempt to read as many pages as it can in this time + burstLength: { seconds: 3 }, -incrementalBuilder.addIncrementalEntityProvider(myEntityProvider, { - // How long should it attempt to read pages from the API in a - // single burst? Keep this short. The Incremental Entity Provider - // will attempt to read as many pages as it can in this time - burstLength: { seconds: 3 }, + // How long should it wait between bursts? + burstInterval: { seconds: 3 }, - // How long should it wait between bursts? - burstInterval: { seconds: 3 }, + // How long should it rest before re-ingesting again? + restLength: { day: 1 }, - // How long should it rest before re-ingesting again? - restLength: { day: 1 }, + // Optional back-off configuration - how long should it wait to retry + // in the event of an error? + backoff: [ + { seconds: 5 }, + { seconds: 30 }, + { minutes: 10 }, + { hours: 3 }, + ], - // Optional back-off configuration - how long should it wait to retry - // in the event of an error? - backoff: [{ seconds: 5 }, { seconds: 30 }, { minutes: 10 }, { hours: 3 }], + // Optional. Use this to prevent removal of entities above a given + // percentage. This can be helpful if a data source is flaky and + // sometimes returns a successful status, but fewer than expected + // assets to add or maintain in the catalog. + rejectRemovalsAbovePercentage: 5, - // Optional. Use this to prevent removal of entities above a given - // percentage. This can be helpful if a data source is flaky and - // sometimes returns a successful status, but fewer than expected - // assets to add or maintain in the catalog. - rejectRemovalsAbovePercentage: 5, + // Optional. Similar to rejectRemovalsAbovePercentage, except it + // applies to complete, 100% failure of a data source. If true, + // a data source that returns a successful status but does not + // provide any assets to turn into entities will have its empty + // data set rejected. + rejectEmptySourceCollections: true, + }; - // Optional. Similar to rejectRemovalsAbovePercentage, except it - // applies to complete, 100% failure of a data source. If true, - // a data source that returns a successful status but does not - // provide any assets to turn into entities will have its empty - // data set rejected. - rejectEmptySourceCollections: true, -}); + incrementalBuilder.addProvider({ + provider: myEntityProvider, + options, + }); + }, + }); + }, + }); ``` That's it!!! diff --git a/plugins/catalog-backend-module-incremental-ingestion/api-report-alpha.md b/plugins/catalog-backend-module-incremental-ingestion/api-report-alpha.md index f6ee4c1414..cfc4bca6f2 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/api-report-alpha.md +++ b/plugins/catalog-backend-module-incremental-ingestion/api-report-alpha.md @@ -3,13 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { IncrementalEntityProvider } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; import { IncrementalEntityProviderOptions } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; // @alpha -const catalogModuleIncrementalIngestionEntityProvider: BackendFeatureCompat; +const catalogModuleIncrementalIngestionEntityProvider: BackendFeature; export default catalogModuleIncrementalIngestionEntityProvider; // @alpha diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 466aca22ec..644a533b6b 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.5.0", + "version": "0.5.3-next.0", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 4abf2a3d3b..7255cb6b68 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.9.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.8.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-ldap/api-report.md b/plugins/catalog-backend-module-ldap/api-report.md index 2ee3c1ec79..40bb9d81ae 100644 --- a/plugins/catalog-backend-module-ldap/api-report.md +++ b/plugins/catalog-backend-module-ldap/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node'; import { Client } from 'ldapjs'; @@ -31,7 +31,7 @@ export type BindConfig = { }; // @public -const catalogModuleLdapOrgEntityProvider: BackendFeatureCompat; +const catalogModuleLdapOrgEntityProvider: BackendFeature; export default catalogModuleLdapOrgEntityProvider; // @public diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index abb11f8096..653104dfd1 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.8.0", + "version": "0.9.0-next.0", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index a95b43851b..f8d48f5f68 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + ## 0.0.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/api-report.md b/plugins/catalog-backend-module-logs/api-report.md index 0be2197c9a..fe68c7a560 100644 --- a/plugins/catalog-backend-module-logs/api-report.md +++ b/plugins/catalog-backend-module-logs/api-report.md @@ -3,9 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public -const catalogModuleLogs: BackendFeatureCompat; +const catalogModuleLogs: BackendFeature; export default catalogModuleLogs; ``` diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index 43afb06dde..412ffe60d3 100644 --- a/plugins/catalog-backend-module-logs/package.json +++ b/plugins/catalog-backend-module-logs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-logs", - "version": "0.0.2", + "version": "0.1.0-next.0", "description": "A module that subscribes to catalog releated events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/src/module.test.ts b/plugins/catalog-backend-module-logs/src/module.test.ts index 79f1b8c295..037523a902 100644 --- a/plugins/catalog-backend-module-logs/src/module.test.ts +++ b/plugins/catalog-backend-module-logs/src/module.test.ts @@ -34,8 +34,8 @@ describe('catalogModuleLogs', () => { await startTestBackend({ features: [ mockServices.logger.factory(), - eventsServiceFactory(), - catalogModuleLogs(), + eventsServiceFactory, + catalogModuleLogs, ], }); diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index a394664d88..5424f8e00e 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.6.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 3c2d690: Allow users without defined email to be ingested by the `msgraph` catalog plugin and add `userIdMatchingUserEntityAnnotation` sign-in resolver for the Microsoft auth provider to support sign-in for users without defined email. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.6.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-msgraph/api-report-alpha.md b/plugins/catalog-backend-module-msgraph/api-report-alpha.md index 84f56a99bf..8129aec50f 100644 --- a/plugins/catalog-backend-module-msgraph/api-report-alpha.md +++ b/plugins/catalog-backend-module-msgraph/api-report-alpha.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { GroupTransformer } from '@backstage/plugin-catalog-backend-module-msgraph'; import { OrganizationTransformer } from '@backstage/plugin-catalog-backend-module-msgraph'; @@ -11,7 +11,7 @@ import { ProviderConfigTransformer } from '@backstage/plugin-catalog-backend-mod import { UserTransformer } from '@backstage/plugin-catalog-backend-module-msgraph'; // @alpha -const catalogModuleMicrosoftGraphOrgEntityProvider: BackendFeatureCompat; +const catalogModuleMicrosoftGraphOrgEntityProvider: BackendFeature; export default catalogModuleMicrosoftGraphOrgEntityProvider; // @alpha diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 68e67c1cfc..49a0813462 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.6.0", + "version": "0.6.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.test.ts index f123b75995..62aa758fed 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.test.ts @@ -90,4 +90,31 @@ describe('defaultTransformers', () => { }, }); }); + + it('tests defaultUserTransformer with no email', async () => { + const user: MicrosoftGraph.User = { + id: 'foo', + displayName: 'BAR', + userPrincipalName: 'test@upn', + }; + const userPhoto = 'test_photo'; + const result = await defaultUserTransformer(user, userPhoto); + expect(result).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + annotations: { + 'graph.microsoft.com/user-id': 'foo', + }, + name: 'test_upn', + }, + spec: { + memberOf: [], + profile: { + displayName: 'BAR', + picture: 'test_photo', + }, + }, + }); + }); }); diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.ts index 7a8c4ec463..910d5a1d40 100644 --- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.ts +++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/defaultTransformers.ts @@ -122,25 +122,25 @@ export async function defaultUserTransformer( user: MicrosoftGraph.User, userPhoto?: string, ): Promise { - if (!user.id || !user.displayName || !user.mail) { + if (!user.id || !user.displayName) { return undefined; } - const name = normalizeEntityName(user.mail); + const name = user.mail + ? normalizeEntityName(user.mail) + : normalizeEntityName(user.userPrincipalName!); const entity: UserEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'User', metadata: { name, annotations: { - [MICROSOFT_EMAIL_ANNOTATION]: user.mail!, [MICROSOFT_GRAPH_USER_ID_ANNOTATION]: user.id!, }, }, spec: { profile: { displayName: user.displayName!, - email: user.mail!, // TODO: Additional fields? // jobTitle: user.jobTitle || undefined, @@ -151,6 +151,11 @@ export async function defaultUserTransformer( }, }; + if (user.mail) { + entity.metadata.annotations![MICROSOFT_EMAIL_ANNOTATION] = user.mail; + entity.spec.profile!.email = user.mail; + } + if (userPhoto) { entity.spec.profile!.picture = userPhoto; } diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index c24a7bb60b..95bd5f212e 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.1.41 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/api-report.md b/plugins/catalog-backend-module-openapi/api-report.md index 05ab2e0561..d79d852387 100644 --- a/plugins/catalog-backend-module-openapi/api-report.md +++ b/plugins/catalog-backend-module-openapi/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -15,7 +15,7 @@ import { ScmIntegrations } from '@backstage/integration'; import { UrlReaderService } from '@backstage/backend-plugin-api'; // @public -const catalogModuleJsonSchemaRefPlaceholderResolver: BackendFeatureCompat; +const catalogModuleJsonSchemaRefPlaceholderResolver: BackendFeature; export default catalogModuleJsonSchemaRefPlaceholderResolver; // @public (undocumented) diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 4bdfe575e2..cf5dd7a52f 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.1.41", + "version": "0.2.0-next.0", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 0bb7df6890..49a82b1575 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-puppetdb/api-report-alpha.md b/plugins/catalog-backend-module-puppetdb/api-report-alpha.md index 41af9e4eca..211add3895 100644 --- a/plugins/catalog-backend-module-puppetdb/api-report-alpha.md +++ b/plugins/catalog-backend-module-puppetdb/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const catalogModulePuppetDbEntityProvider: BackendFeatureCompat; +const catalogModulePuppetDbEntityProvider: BackendFeature; export default catalogModulePuppetDbEntityProvider; // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 3a12da0e66..83913a20b7 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.2.0", + "version": "0.2.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 11075a73cf..4fbe07a9a4 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 0.1.21 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/api-report.md b/plugins/catalog-backend-module-scaffolder-entity-model/api-report.md index 1a0eabbec4..49542b0396 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/api-report.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/api-report.md @@ -3,14 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node'; import { Entity } from '@backstage/catalog-model'; import { LocationSpec } from '@backstage/plugin-catalog-common'; // @public -const catalogModuleScaffolderEntityModel: BackendFeatureCompat; +const catalogModuleScaffolderEntityModel: BackendFeature; export default catalogModuleScaffolderEntityModel; // @public diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index fdb0027016..444a9ab2af 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.1.21", + "version": "0.2.0-next.0", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 0d7ab37983..a606984ef1 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.4 + - @backstage/plugin-permission-common@0.8.1 + ## 0.4.10 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/api-report.md b/plugins/catalog-backend-module-unprocessed/api-report.md index 044d6d3501..493082ac7c 100644 --- a/plugins/catalog-backend-module-unprocessed/api-report.md +++ b/plugins/catalog-backend-module-unprocessed/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import { HttpAuthService } from '@backstage/backend-plugin-api'; import { HttpRouterService } from '@backstage/backend-plugin-api'; @@ -11,7 +11,7 @@ import { Knex } from 'knex'; import { PermissionsService } from '@backstage/backend-plugin-api'; // @public -const catalogModuleUnprocessedEntities: BackendFeatureCompat; +const catalogModuleUnprocessedEntities: BackendFeature; export default catalogModuleUnprocessedEntities; // @public diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index ad91bd573d..f6f1154917 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.4.10", + "version": "0.5.0-next.0", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index c6347c35d3..6128d697e3 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend +## 1.25.3-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 53cce86: Fixed an issue with the by-query call, where ordering by a field that does not exist on all entities led to not all results being returned +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + ## 1.25.0 ### Minor Changes diff --git a/plugins/catalog-backend/api-report-alpha.md b/plugins/catalog-backend/api-report-alpha.md index 260f59e70f..74f1e7d1b4 100644 --- a/plugins/catalog-backend/api-report-alpha.md +++ b/plugins/catalog-backend/api-report-alpha.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ConditionalPolicyDecision } from '@backstage/plugin-permission-common'; import { Conditions } from '@backstage/plugin-permission-node'; import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node'; @@ -75,7 +75,7 @@ export type CatalogPermissionRule< > = PermissionRule; // @alpha -const catalogPlugin: BackendFeatureCompat; +const catalogPlugin: BackendFeature; export default catalogPlugin; // @alpha diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 4003fd2b6a..6261cacf20 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "1.25.0", + "version": "1.25.3-next.0", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 3c98f2a8bc..92acd9e3ce 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -1648,6 +1648,43 @@ describe('DefaultEntitiesCatalog', () => { expect(response5.totalItems).toBe(6); }, ); + + it.each(databases.eachSupportedId())( + 'should sort properly for fields that do not exist on all entities, %p', + async databaseId => { + await createDatabase(databaseId); + + await Promise.all([ + addEntityToSearch(entityFrom('AA', { uid: 'id1' })), + addEntityToSearch(entityFrom('BB', { uid: 'id2', title: 'YY' })), + addEntityToSearch(entityFrom('CC', { uid: 'id3', title: 'XX' })), + ]); + + const catalog = new DefaultEntitiesCatalog({ + database: knex, + logger: mockServices.logger.mock(), + stitcher, + }); + + await expect( + catalog + .queryEntities({ + orderFields: [{ field: 'metadata.title', order: 'asc' }], + credentials: mockCredentials.none(), + }) + .then(r => r.items.map(e => e.metadata.name)), + ).resolves.toEqual(['CC', 'BB', 'AA']); // 'AA' has no title, ends up last + + await expect( + catalog + .queryEntities({ + orderFields: [{ field: 'metadata.title', order: 'desc' }], + credentials: mockCredentials.none(), + }) + .then(r => r.items.map(e => e.metadata.name)), + ).resolves.toEqual(['BB', 'CC', 'AA']); // 'AA' has no title, ends up last + }, + ); }); describe('removeEntityByUid', () => { diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 9049685d38..f9f731e3e4 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -249,7 +249,18 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { ]); } }); - entitiesQuery = entitiesQuery.orderBy('final_entities.entity_id', 'asc'); // stable sort + + if (!request?.order) { + entitiesQuery = entitiesQuery + .leftOuterJoin( + 'refresh_state', + 'refresh_state.entity_id', + 'final_entities.entity_id', + ) + .orderBy('refresh_state.entity_ref', 'asc'); // default sort + } else { + entitiesQuery.orderBy('final_entities.entity_id', 'asc'); // stable sort + } const { limit, offset } = parsePagination(request?.pagination); if (limit !== undefined) { @@ -379,12 +390,20 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { const [prevItemOrderFieldValue, prevItemUid] = cursor.orderFieldValues || []; - const dbQuery = db('search') - .join('final_entities', 'search.entity_id', 'final_entities.entity_id') - .where('search.key', sortField.field); + const dbQuery = db('final_entities').leftOuterJoin('search', qb => + qb + .on('search.entity_id', 'final_entities.entity_id') + .andOnVal('search.key', sortField.field), + ); if (cursor.filter) { - parseFilter(cursor.filter, dbQuery, db, false, 'search.entity_id'); + parseFilter( + cursor.filter, + dbQuery, + db, + false, + 'final_entities.entity_id', + ); } const normalizedFullTextFilterTerm = cursor.fullTextFilter?.term?.trim(); @@ -410,7 +429,7 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`, ); }); - dbQuery.andWhere('search.entity_id', 'in', matchQuery); + dbQuery.andWhere('final_entities.entity_id', 'in', matchQuery); } } @@ -427,32 +446,59 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { ) .orWhere('value', '=', prevItemOrderFieldValue) .andWhere( - 'search.entity_id', + 'final_entities.entity_id', isFetchingBackwards !== isOrderingDescending ? '<' : '>', prevItemUid, ); }); } - dbQuery - .orderBy([ + if (db.client.config.client === 'pg') { + // pg correctly orders by the column value and handling nulls in one go + dbQuery.orderBy([ { - column: 'value', + column: 'search.value', + order: isFetchingBackwards + ? invertOrder(sortField.order) + : sortField.order, + nulls: 'last', + }, + { + column: 'final_entities.entity_id', + order: isFetchingBackwards + ? invertOrder(sortField.order) + : sortField.order, + }, + ]); + } else { + // sqlite and mysql translate the above statement ONLY into "order by (value is null) asc" + // no matter what the order is, for some reason, so we have to manually add back the statement + // that translates to "order by value " while avoiding to give an order + dbQuery.orderBy([ + { + column: 'search.value', + order: undefined, + nulls: 'last', + }, + { + column: 'search.value', order: isFetchingBackwards ? invertOrder(sortField.order) : sortField.order, }, { - column: 'search.entity_id', + column: 'final_entities.entity_id', order: isFetchingBackwards ? invertOrder(sortField.order) : sortField.order, }, - ]) - // fetch an extra item to check if there are more items. - .limit(isFetchingBackwards ? limit : limit + 1); + ]); + } - countQuery.count('search.entity_id', { as: 'count' }); + // fetch an extra item to check if there are more items. + dbQuery.limit(isFetchingBackwards ? limit : limit + 1); + + countQuery.count('final_entities.entity_id', { as: 'count' }); const [rows, [{ count }]] = await Promise.all([ limit > 0 ? dbQuery : [], diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index a1715bbbca..8a7784877e 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-graph +## 0.4.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + ## 0.4.8 ### Patch Changes diff --git a/plugins/catalog-graph/api-report-alpha.md b/plugins/catalog-graph/api-report-alpha.md index 6ab438def3..65f8af3764 100644 --- a/plugins/catalog-graph/api-report-alpha.md +++ b/plugins/catalog-graph/api-report-alpha.md @@ -5,18 +5,18 @@ ```ts import { AnyExtensionDataRef } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Direction } from '@backstage/plugin-catalog-graph'; import { Entity } from '@backstage/catalog-model'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { catalogGraph: RouteRef; }, @@ -28,8 +28,8 @@ const _default: BackstagePlugin< }>; }, { - 'entity-card:catalog-graph/relations': ExtensionDefinition< - { + 'entity-card:catalog-graph/relations': ExtensionDefinition<{ + config: { kinds: string[] | undefined; relations: string[] | undefined; maxDepth: number | undefined; @@ -43,8 +43,8 @@ const _default: BackstagePlugin< height: number | undefined; } & { filter: string | undefined; - }, - { + }; + configInput: { height?: number | undefined; curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined; direction?: Direction | undefined; @@ -58,27 +58,28 @@ const _default: BackstagePlugin< relationPairs?: [string, string][] | undefined; } & { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -86,15 +87,13 @@ const _default: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'entity-card'; - namespace: undefined; - name: 'relations'; - } - >; - 'page:catalog-graph': ExtensionDefinition< - { + }; + kind: 'entity-card'; + namespace: undefined; + name: 'relations'; + }>; + 'page:catalog-graph': ExtensionDefinition<{ + config: { selectedKinds: string[] | undefined; selectedRelations: string[] | undefined; rootEntityRefs: string[] | undefined; @@ -110,8 +109,8 @@ const _default: BackstagePlugin< zoom: 'disabled' | 'enabled' | 'enable-on-click' | undefined; } & { path: string | undefined; - }, - { + }; + configInput: { curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined; direction?: Direction | undefined; zoom?: 'disabled' | 'enabled' | 'enable-on-click' | undefined; @@ -127,21 +126,22 @@ const _default: BackstagePlugin< showFilters?: boolean | undefined; } & { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -149,13 +149,11 @@ const _default: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; + }; + kind: 'page'; + namespace: undefined; + name: undefined; + }>; } >; export default _default; diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 326a80fdf2..9c59eb1b08 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.4.8", + "version": "0.4.9-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 1b8d14ff3f..c5dcfd4e11 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog-import +## 0.12.3-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.12.2 ### Patch Changes diff --git a/plugins/catalog-import/api-report-alpha.md b/plugins/catalog-import/api-report-alpha.md index 9f82f39266..3eed9ce3e4 100644 --- a/plugins/catalog-import/api-report-alpha.md +++ b/plugins/catalog-import/api-report-alpha.md @@ -5,57 +5,58 @@ ```ts import { AnyApiFactory } from '@backstage/core-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { importPage: RouteRef; }, {}, { - 'api:catalog-import': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: undefined; - } - >; - 'page:catalog-import': ExtensionDefinition< - { + 'api:catalog-import': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'page:catalog-import': ExtensionDefinition<{ + kind: 'page'; + namespace: undefined; + name: undefined; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 7909c33936..e0e7056e47 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.12.2", + "version": "0.12.3-next.0", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 1c77be04e9..89ed287403 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-node +## 1.12.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + ## 1.12.5 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index ff4c0d17a8..20124ceca5 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.12.5", + "version": "1.12.7-next.0", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 8cb8985542..7f01541057 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-react +## 1.12.4-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 5446061: The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130). +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-react@0.4.25 + ## 1.12.3 ### Patch Changes diff --git a/plugins/catalog-react/api-report-alpha.md b/plugins/catalog-react/api-report-alpha.md index b22d76cd15..5dec3ab99d 100644 --- a/plugins/catalog-react/api-report-alpha.md +++ b/plugins/catalog-react/api-report-alpha.md @@ -83,7 +83,7 @@ export function convertLegacyEntityCardExtension( | typeof EntityCardBlueprint.dataRefs.filterFunction.T | typeof EntityCardBlueprint.dataRefs.filterExpression.T; }, -): ExtensionDefinition; +): ExtensionDefinition; // @alpha (undocumented) export function convertLegacyEntityContentExtension( @@ -96,42 +96,41 @@ export function convertLegacyEntityContentExtension( defaultPath?: string; defaultTitle?: string; }, -): ExtensionDefinition; +): ExtensionDefinition; // @alpha -export const EntityCardBlueprint: ExtensionBlueprint< - { - kind: 'entity-card'; - namespace: undefined; - name: undefined; - }, - { +export const EntityCardBlueprint: ExtensionBlueprint<{ + kind: 'entity-card'; + namespace: undefined; + name: undefined; + params: { loader: () => Promise; filter?: string | ((entity: Entity) => boolean) | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - { + }; + dataRefs: { filterFunction: ConfigurableExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', @@ -142,59 +141,58 @@ export const EntityCardBlueprint: ExtensionBlueprint< 'catalog.entity-filter-expression', {} >; - } ->; + }; +}>; // @alpha -export const EntityContentBlueprint: ExtensionBlueprint< - { - kind: 'entity-content'; - namespace: undefined; - name: undefined; - }, - { +export const EntityContentBlueprint: ExtensionBlueprint<{ + kind: 'entity-content'; + namespace: undefined; + name: undefined; + params: { loader: () => Promise; defaultPath: string; defaultTitle: string; routeRef?: RouteRef | undefined; filter?: string | ((entity: Entity) => boolean) | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + config: { path: string | undefined; title: string | undefined; filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; title?: string | undefined; path?: string | undefined; - }, - { + }; + dataRefs: { title: ConfigurableExtensionDataRef< string, 'catalog.entity-content-title', @@ -210,8 +208,8 @@ export const EntityContentBlueprint: ExtensionBlueprint< 'catalog.entity-filter-expression', {} >; - } ->; + }; +}>; // @alpha export function isOwnerOf(owner: Entity, entity: Entity): boolean; diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 7cf7464a88..33f7772e81 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.12.3", + "version": "1.12.4-next.0", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx index 0cb7cdead8..2ab87b20ae 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityCardBlueprint.test.tsx @@ -20,7 +20,10 @@ import { createExtension, createExtensionInput, } from '@backstage/frontend-plugin-api'; -import { createExtensionTester } from '@backstage/frontend-test-utils'; +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; import { waitFor, screen } from '@testing-library/react'; import { Entity } from '@backstage/catalog-model'; @@ -37,6 +40,7 @@ describe('EntityCardBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "entity-content:catalog/overview", "input": "cards", @@ -163,9 +167,11 @@ describe('EntityCardBlueprint', () => { }, }); - createExtensionTester(extension, { config: { mock: 'mock test config' } }) - .add(mockExtension) - .render(); + renderInTestApp( + createExtensionTester(extension, { config: { mock: 'mock test config' } }) + .add(mockExtension) + .reactElement(), + ); await waitFor(() => { expect(screen.getByTestId('test')).toBeInTheDocument(); diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx index 5d91f402de..cc86f01501 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.test.tsx @@ -15,7 +15,10 @@ */ import React from 'react'; import { EntityContentBlueprint } from './EntityContentBlueprint'; -import { createExtensionTester } from '@backstage/frontend-test-utils'; +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; import { coreExtensionData, createExtension, @@ -39,6 +42,7 @@ describe('EntityContentBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "page:catalog/entity", "input": "contents", @@ -211,9 +215,11 @@ describe('EntityContentBlueprint', () => { }, }); - createExtensionTester(extension, { config: { mock: 'mock test config' } }) - .add(mockExtension) - .render(); + renderInTestApp( + createExtensionTester(extension, { config: { mock: 'mock test config' } }) + .add(mockExtension) + .reactElement(), + ); await waitFor(() => { expect(screen.getByTestId('test')).toBeInTheDocument(); diff --git a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.test.tsx b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.test.tsx index 7dacd9c3e8..dd13318333 100644 --- a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.test.tsx +++ b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.test.tsx @@ -48,12 +48,11 @@ describe('convertLegacyEntityCardExtension', () => { ); const converted = convertLegacyEntityCardExtension(LegacyExtension); - expect(converted.kind).toBe('entity-card'); - expect(converted.namespace).toBe(undefined); - expect(converted.name).toBe('example'); const tester = createExtensionTester(converted); + expect(tester.query(converted).node.spec.id).toBe('entity-card:example'); + await renderInTestApp(tester.reactElement(), { mountedRoutes: { '/': convertLegacyRouteRef(routeRef), @@ -83,12 +82,11 @@ describe('convertLegacyEntityCardExtension', () => { name: 'other', filter: 'my-filter', }); - expect(converted.kind).toBe('entity-card'); - expect(converted.namespace).toBe(undefined); - expect(converted.name).toBe('other'); const tester = createExtensionTester(converted); + expect(tester.query(converted).node.spec.id).toBe('entity-card:other'); + await renderInTestApp(tester.reactElement(), { mountedRoutes: { '/': convertLegacyRouteRef(routeRef), @@ -106,7 +104,7 @@ describe('convertLegacyEntityCardExtension', () => { }); it('should support various naming patterns for entity card extensions', async () => { - const withName = (name: string) => { + const getDiscoveredId = (name: string) => { const converted = convertLegacyEntityCardExtension( legacyPlugin.provide( createRoutableExtension({ @@ -116,14 +114,14 @@ describe('convertLegacyEntityCardExtension', () => { }), ), ); - return converted.name; + return createExtensionTester(converted).query(converted).node.spec.id; }; - expect(withName('EntityTestCard')).toBe(undefined); - expect(withName('EntityTestTrimCard')).toBe('trim'); - expect(withName('EntityTeStTrimCard')).toBe('trim'); - expect(withName('EntityExampleCard')).toBe('example'); - expect(withName('EntityExAmpleCard')).toBe('ex-ample'); - expect(withName('ExampleCard')).toBe('example-card'); + expect(getDiscoveredId('EntityTestCard')).toBe('entity-card:test'); // falls back to test namespace + expect(getDiscoveredId('EntityTestTrimCard')).toBe('entity-card:trim'); + expect(getDiscoveredId('EntityTeStTrimCard')).toBe('entity-card:trim'); + expect(getDiscoveredId('EntityExampleCard')).toBe('entity-card:example'); + expect(getDiscoveredId('EntityExAmpleCard')).toBe('entity-card:ex-ample'); + expect(getDiscoveredId('ExampleCard')).toBe('entity-card:example-card'); }); }); diff --git a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx index 76442f526d..ed92461736 100644 --- a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx +++ b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx @@ -30,7 +30,7 @@ export function convertLegacyEntityCardExtension( | typeof EntityCardBlueprint.dataRefs.filterFunction.T | typeof EntityCardBlueprint.dataRefs.filterExpression.T; }, -): ExtensionDefinition { +): ExtensionDefinition { const element = ; const extName = getComponentData(element, 'core.extensionName'); diff --git a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.test.tsx b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.test.tsx index 6d7c8f0420..d8ca126aa2 100644 --- a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.test.tsx +++ b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.test.tsx @@ -49,12 +49,11 @@ describe('convertLegacyEntityContentExtension', () => { ); const converted = convertLegacyEntityContentExtension(LegacyExtension); - expect(converted.kind).toBe('entity-content'); - expect(converted.namespace).toBe(undefined); - expect(converted.name).toBe('example'); const tester = createExtensionTester(converted); + expect(tester.query(converted).node.spec.id).toBe('entity-content:example'); + await renderInTestApp(tester.reactElement(), { mountedRoutes: { '/': convertLegacyRouteRef(routeRef), @@ -88,12 +87,11 @@ describe('convertLegacyEntityContentExtension', () => { defaultTitle: 'Other', filter: 'my-filter', }); - expect(converted.kind).toBe('entity-content'); - expect(converted.namespace).toBe(undefined); - expect(converted.name).toBe('other'); const tester = createExtensionTester(converted); + expect(tester.query(converted).node.spec.id).toBe('entity-content:other'); + await renderInTestApp(tester.reactElement(), { mountedRoutes: { '/': convertLegacyRouteRef(routeRef), @@ -123,14 +121,14 @@ describe('convertLegacyEntityContentExtension', () => { }), ), ); - return converted.name; + return createExtensionTester(converted).query(converted).node.spec.id; }; - expect(withName('EntityTestContent')).toBe(undefined); - expect(withName('EntityTestTrimContent')).toBe('trim'); - expect(withName('EntityTeStTrimContent')).toBe('trim'); - expect(withName('EntityExampleContent')).toBe('example'); - expect(withName('EntityExAmpleContent')).toBe('ex-ample'); - expect(withName('ExampleContent')).toBe('example-content'); + expect(withName('EntityTestContent')).toBe('entity-content:test'); // falls back to test namespace + expect(withName('EntityTestTrimContent')).toBe('entity-content:trim'); + expect(withName('EntityTeStTrimContent')).toBe('entity-content:trim'); + expect(withName('EntityExampleContent')).toBe('entity-content:example'); + expect(withName('EntityExAmpleContent')).toBe('entity-content:ex-ample'); + expect(withName('ExampleContent')).toBe('entity-content:example-content'); }); }); diff --git a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx index e44b5671fc..02f5d104bc 100644 --- a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx +++ b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx @@ -40,7 +40,7 @@ export function convertLegacyEntityContentExtension( defaultPath?: string; defaultTitle?: string; }, -): ExtensionDefinition { +): ExtensionDefinition { const element = ; const extName = getComponentData(element, 'core.extensionName'); diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts index 8d841f3c3c..b546f9aa93 100644 --- a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts @@ -71,6 +71,9 @@ export function defaultEntityPresentation( }; } +const isString = (value: unknown): value is string => + Boolean(value) && typeof value === 'string'; + // Try to extract display-worthy parts of an entity or ref as best we can, without throwing function getParts(entityOrRef: Entity | CompoundEntityRef | string): { kind?: string; @@ -99,35 +102,29 @@ function getParts(entityOrRef: Entity | CompoundEntityRef | string): { } if (typeof entityOrRef === 'object' && entityOrRef !== null) { - const kind = [get(entityOrRef, 'kind')].find( - candidate => candidate && typeof candidate === 'string', - ); + const kind = [get(entityOrRef, 'kind')].find(isString); const namespace = [ get(entityOrRef, 'metadata.namespace'), get(entityOrRef, 'namespace'), - ].find(candidate => candidate && typeof candidate === 'string'); + ].find(isString); const name = [ get(entityOrRef, 'metadata.name'), get(entityOrRef, 'name'), - ].find(candidate => candidate && typeof candidate === 'string'); + ].find(isString); - const title = [get(entityOrRef, 'metadata.title')].find( - candidate => candidate && typeof candidate === 'string', - ); + const title = [get(entityOrRef, 'metadata.title')].find(isString); const description = [get(entityOrRef, 'metadata.description')].find( - candidate => candidate && typeof candidate === 'string', + isString, ); const displayName = [get(entityOrRef, 'spec.profile.displayName')].find( - candidate => candidate && typeof candidate === 'string', + isString, ); - const type = [get(entityOrRef, 'spec.type')].find( - candidate => candidate && typeof candidate === 'string', - ); + const type = [get(entityOrRef, 'spec.type')].find(isString); return { kind, namespace, name, title, description, displayName, type }; } diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.test.ts b/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.test.ts index 742efaab9f..1d9eaa8af7 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.test.ts +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.test.ts @@ -70,6 +70,21 @@ describe('useFacetsEntities', () => { expect(result.current[0]).toEqual({ value: { items: [] }, loading: true }); }); + it(`should return empty response when facet is not present`, async () => { + mockedGetEntityFacets.mockResolvedValueOnce({ + facets: { 'metadata.tags': [{ value: 'tag', count: 1 }] }, + }); + mockedGetEntitiesByRefs.mockResolvedValueOnce({ items: [] }); + const { result } = renderHook(() => useFacetsEntities({ enabled: true })); + result.current[1]({ text: '' }); + await waitFor(() => { + expect(result.current[0]).toEqual({ + value: { items: [] }, + loading: false, + }); + }); + }); + it(`should return the owners`, async () => { const entityRefs = ['component:default/e1', 'component:default/e2']; mockedGetEntityFacets.mockResolvedValue(facetsFromEntityRefs(entityRefs)); diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.ts b/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.ts index 10a5c5a8c2..f43bba97c7 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.ts +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/useFacetsEntities.ts @@ -34,6 +34,9 @@ type FacetsInitialRequest = { text: string; }; +const maybeString = (value: unknown): string | undefined => + typeof value === 'string' ? value : undefined; + /** * This hook asynchronously loads the entity owners using the facets endpoint. * EntityOwnerPicker uses this hook when mode="owners-only" is passed as prop. @@ -52,7 +55,7 @@ export function useFacetsEntities({ enabled }: { enabled: boolean }) { const facetsResponse = await catalogApi.getEntityFacets({ facets: [facet], }); - const entityRefs = facetsResponse.facets[facet].map(e => e.value); + const entityRefs = facetsResponse.facets[facet]?.map(e => e.value) ?? []; return catalogApi .getEntitiesByRefs({ entityRefs }) @@ -67,11 +70,11 @@ export function useFacetsEntities({ enabled }: { enabled: boolean }) { 'en-US', ) || ( - get(a, 'spec.profile.displayName') || + maybeString(get(a, 'spec.profile.displayName')) || a.metadata.title || a.metadata.name ).localeCompare( - get(b, 'spec.profile.displayName') || + maybeString(get(b, 'spec.profile.displayName')) || b.metadata.title || b.metadata.name, 'en-US', diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 2185791a40..3d5de77a6b 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-catalog +## 1.22.1-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 5446061: The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130). +- 180a45f: Entity presentation api now only fetches fields that are required to display entity title +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + - @backstage/plugin-search-common@1.2.14 + ## 1.22.0 ### Minor Changes diff --git a/plugins/catalog/api-report-alpha.md b/plugins/catalog/api-report-alpha.md index 42f4355921..62e631dd4a 100644 --- a/plugins/catalog/api-report-alpha.md +++ b/plugins/catalog/api-report-alpha.md @@ -8,13 +8,13 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyExtensionDataRef } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; @@ -23,21 +23,19 @@ import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-rea import { TranslationRef } from '@backstage/core-plugin-api/alpha'; // @alpha -export const CatalogFilterBlueprint: ExtensionBlueprint< - { - kind: 'catalog-filter'; - namespace: undefined; - name: undefined; - }, - { +export const CatalogFilterBlueprint: ExtensionBlueprint<{ + kind: 'catalog-filter'; + namespace: undefined; + name: undefined; + params: { loader: () => Promise; - }, - ConfigurableExtensionDataRef, - {}, - {}, - {}, - never ->; + }; + output: ConfigurableExtensionDataRef; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: never; +}>; // @alpha (undocumented) export const catalogTranslationRef: TranslationRef< @@ -125,7 +123,7 @@ export const catalogTranslationRef: TranslationRef< >; // @alpha (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { catalogIndex: RouteRef; catalogEntity: RouteRef<{ @@ -148,10 +146,26 @@ const _default: BackstagePlugin< unregisterRedirect: ExternalRouteRef; }, { - 'nav-item:catalog': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef< + 'api:catalog': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'nav-item:catalog': ExtensionDefinition<{ + kind: 'nav-item'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { title: string; icon: IconComponent; @@ -159,366 +173,356 @@ const _default: BackstagePlugin< }, 'core.nav-item.target', {} - >, - {}, - { - kind: 'nav-item'; - namespace: undefined; - name: undefined; - } - >; - 'api:catalog/starred-entities': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'starred-entities'; - } - >; - 'api:catalog/entity-presentation': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'entity-presentation'; - } - >; - 'entity-card:catalog/about': ExtensionDefinition< - { + >; + inputs: {}; + }>; + 'api:catalog/starred-entities': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'starred-entities'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:catalog/entity-presentation': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'entity-presentation'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'entity-card:catalog/about': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'about'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'about'; - } - >; - 'entity-card:catalog/links': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/links': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'links'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'links'; - } - >; - 'entity-card:catalog/labels': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/labels': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'labels'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'labels'; - } - >; - 'entity-card:catalog/depends-on-components': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/depends-on-components': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'depends-on-components'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'depends-on-components'; - } - >; - 'entity-card:catalog/depends-on-resources': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/depends-on-resources': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'depends-on-resources'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'depends-on-resources'; - } - >; - 'entity-card:catalog/has-components': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/has-components': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'has-components'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'has-components'; - } - >; - 'entity-card:catalog/has-resources': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/has-resources': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'has-resources'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'has-resources'; - } - >; - 'entity-card:catalog/has-subcomponents': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/has-subcomponents': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'has-subcomponents'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'has-subcomponents'; - } - >; - 'entity-card:catalog/has-subdomains': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/has-subdomains': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'has-subdomains'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'has-subdomains'; - } - >; - 'entity-card:catalog/has-systems': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:catalog/has-systems': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'has-systems'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'has-systems'; - } - >; - 'entity-content:catalog/overview': ExtensionDefinition< - { - [x: string]: any; - } & { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-content:catalog/overview': ExtensionDefinition<{ + config: { path: string | undefined; title: string | undefined; filter: string | undefined; - }, - { - [x: string]: any; - } & { + }; + configInput: { filter?: string | undefined; title?: string | undefined; path?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-content-title', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: { cards: ExtensionInput< | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef< @@ -540,33 +544,37 @@ const _default: BackstagePlugin< optional: false; } >; - }, - { - kind: 'entity-content'; - namespace: undefined; - name: 'overview'; - } - >; - 'catalog-filter:catalog/tag': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'tag'; - } - >; - 'catalog-filter:catalog/kind': ExtensionDefinition< - { + }; + kind: 'entity-content'; + namespace: undefined; + name: 'overview'; + }>; + 'catalog-filter:catalog/tag': ExtensionDefinition<{ + kind: 'catalog-filter'; + namespace: undefined; + name: 'tag'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'catalog-filter:catalog/kind': ExtensionDefinition<{ + config: { initialFilter: string; - }, - { + }; + configInput: { initialFilter?: string | undefined; - }, - ConfigurableExtensionDataRef, - { + }; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -574,33 +582,37 @@ const _default: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'kind'; - } - >; - 'catalog-filter:catalog/type': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'type'; - } - >; - 'catalog-filter:catalog/mode': ExtensionDefinition< - { + }; + kind: 'catalog-filter'; + namespace: undefined; + name: 'kind'; + }>; + 'catalog-filter:catalog/type': ExtensionDefinition<{ + kind: 'catalog-filter'; + namespace: undefined; + name: 'type'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'catalog-filter:catalog/mode': ExtensionDefinition<{ + config: { mode: 'all' | 'owners-only' | undefined; - }, - { + }; + configInput: { mode?: 'all' | 'owners-only' | undefined; - }, - ConfigurableExtensionDataRef, - { + }; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -608,55 +620,63 @@ const _default: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'mode'; - } - >; - 'catalog-filter:catalog/namespace': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'namespace'; - } - >; - 'catalog-filter:catalog/lifecycle': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'lifecycle'; - } - >; - 'catalog-filter:catalog/processing-status': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'processing-status'; - } - >; - 'catalog-filter:catalog/list': ExtensionDefinition< - { + }; + kind: 'catalog-filter'; + namespace: undefined; + name: 'mode'; + }>; + 'catalog-filter:catalog/namespace': ExtensionDefinition<{ + kind: 'catalog-filter'; + namespace: undefined; + name: 'namespace'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'catalog-filter:catalog/lifecycle': ExtensionDefinition<{ + kind: 'catalog-filter'; + namespace: undefined; + name: 'lifecycle'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'catalog-filter:catalog/processing-status': ExtensionDefinition<{ + kind: 'catalog-filter'; + namespace: undefined; + name: 'processing-status'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + }>; + 'catalog-filter:catalog/list': ExtensionDefinition<{ + config: { initialFilter: 'all' | 'owned' | 'starred'; - }, - { + }; + configInput: { initialFilter?: 'all' | 'owned' | 'starred' | undefined; - }, - ConfigurableExtensionDataRef, - { + }; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: { [x: string]: ExtensionInput< AnyExtensionDataRef, { @@ -664,34 +684,29 @@ const _default: BackstagePlugin< singleton: boolean; } >; - }, - { - kind: 'catalog-filter'; - namespace: undefined; - name: 'list'; - } - >; - 'page:catalog': ExtensionDefinition< - { - [x: string]: any; - } & { + }; + kind: 'catalog-filter'; + namespace: undefined; + name: 'list'; + }>; + 'page:catalog': ExtensionDefinition<{ + config: { path: string | undefined; - }, - { - [x: string]: any; - } & { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: { filters: ExtensionInput< ConfigurableExtensionDataRef, { @@ -699,34 +714,29 @@ const _default: BackstagePlugin< optional: false; } >; - }, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; - 'page:catalog/entity': ExtensionDefinition< - { - [x: string]: any; - } & { + }; + kind: 'page'; + namespace: undefined; + name: undefined; + }>; + 'page:catalog/entity': ExtensionDefinition<{ + config: { path: string | undefined; - }, - { - [x: string]: any; - } & { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: { contents: ExtensionInput< | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef @@ -761,35 +771,31 @@ const _default: BackstagePlugin< optional: false; } >; - }, - { - kind: 'page'; - namespace: undefined; - name: 'entity'; - } - >; - 'search-result-list-item:catalog': ExtensionDefinition< - { + }; + kind: 'page'; + namespace: undefined; + name: 'entity'; + }>; + 'search-result-list-item:catalog': ExtensionDefinition<{ + kind: 'search-result-list-item'; + namespace: undefined; + name: undefined; + config: { noTrack: boolean; - }, - { + }; + configInput: { noTrack?: boolean | undefined; - }, - ConfigurableExtensionDataRef< + }; + output: ConfigurableExtensionDataRef< { predicate?: SearchResultItemExtensionPredicate | undefined; component: SearchResultItemExtensionComponent; }, 'search.search-result-list-item.item', {} - >, - {}, - { - kind: 'search-result-list-item'; - namespace: undefined; - name: undefined; - } - >; + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index a592ae3fd5..2212eb037d 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.22.0", + "version": "1.22.1-next.0", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx b/plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx index 9cf8b65957..9e3616ff8c 100644 --- a/plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx +++ b/plugins/catalog/src/alpha/blueprints/CatalogFilterBlueprint.test.tsx @@ -20,7 +20,10 @@ import { createExtension, createExtensionInput, } from '@backstage/frontend-plugin-api'; -import { createExtensionTester } from '@backstage/frontend-test-utils'; +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; import { waitFor, screen } from '@testing-library/react'; describe('CatalogFilterBlueprint', () => { @@ -33,6 +36,7 @@ describe('CatalogFilterBlueprint', () => { expect(extension).toMatchInlineSnapshot(` { "$$type": "@backstage/ExtensionDefinition", + "T": undefined, "attachTo": { "id": "page:catalog", "input": "filters", @@ -89,9 +93,11 @@ describe('CatalogFilterBlueprint', () => { }, }); - createExtensionTester(extension, { config: { test: 'mock test config' } }) - .add(mockExtension) - .render(); + renderInTestApp( + createExtensionTester(extension, { config: { test: 'mock test config' } }) + .add(mockExtension) + .reactElement(), + ); await waitFor(() => { expect(screen.getByTestId('test')).toBeInTheDocument(); diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts index 4cc4d79f52..2c3fb9352b 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts @@ -161,6 +161,15 @@ describe('DefaultEntityPresentationApi', () => { expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledWith( expect.objectContaining({ entityRefs: ['component:default/test'], + fields: [ + 'kind', + 'metadata.name', + 'metadata.namespace', + 'metadata.title', + 'metadata.description', + 'spec.profile.displayName', + 'spec.type', + ], }), ); }); diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts index 1955763d3d..2832792951 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts @@ -26,15 +26,15 @@ import { EntityRefPresentation, EntityRefPresentationSnapshot, } from '@backstage/plugin-catalog-react'; -import { HumanDuration, durationToMilliseconds } from '@backstage/types'; +import { durationToMilliseconds, HumanDuration } from '@backstage/types'; import DataLoader from 'dataloader'; import ExpiryMap from 'expiry-map'; import ObservableImpl from 'zen-observable'; import { + createDefaultRenderer, DEFAULT_BATCH_DELAY, DEFAULT_CACHE_TTL, DEFAULT_ICONS, - createDefaultRenderer, } from './defaults'; /** @@ -371,6 +371,15 @@ export class DefaultEntityPresentationApi implements EntityPresentationApi { async (entityRefs: readonly string[]) => { const { items } = await options.catalogApi!.getEntitiesByRefs({ entityRefs: entityRefs as string[], + fields: [ + 'kind', + 'metadata.name', + 'metadata.namespace', + 'metadata.title', + 'metadata.description', + 'spec.profile.displayName', + 'spec.type', + ], }); const now = Date.now(); diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index ae25e3b15c..2774ae1b34 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-devtools-backend +## 0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-common@0.8.1 + ## 0.3.9 ### Patch Changes diff --git a/plugins/devtools-backend/api-report.md b/plugins/devtools-backend/api-report.md index 835eb23e26..5fceda3d46 100644 --- a/plugins/devtools-backend/api-report.md +++ b/plugins/devtools-backend/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { ConfigInfo } from '@backstage/plugin-devtools-common'; import { DevToolsInfo } from '@backstage/plugin-devtools-common'; @@ -30,7 +30,7 @@ export class DevToolsBackendApi { } // @public -const devtoolsPlugin: BackendFeatureCompat; +const devtoolsPlugin: BackendFeature; export default devtoolsPlugin; // @public @deprecated (undocumented) diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 03b3df0c00..cfe0e1d1f5 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.3.9", + "version": "0.4.0-next.0", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index e1e3b11cf6..0d9dfcc1b2 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-devtools +## 0.1.18-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- 019d9ad: Minor dockerfile syntax update +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-react@0.4.25 + ## 0.1.17 ### Patch Changes diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index ca9b436acc..fef51626ad 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -20,7 +20,7 @@ You may need to modify your Dockerfile to ensure `backstage.json` is copied into ```sh WORKDIR /app # This switches many Node.js dependencies to production mode. -ENV NODE_ENV production +ENV NODE_ENV=production # Then copy the rest of the backend bundle, along with any other files we might want (including backstage.json). COPY --chown=node:node ... backstage.json ./ diff --git a/plugins/devtools/api-report-alpha.md b/plugins/devtools/api-report-alpha.md index 888faa29c7..7fb16734f1 100644 --- a/plugins/devtools/api-report-alpha.md +++ b/plugins/devtools/api-report-alpha.md @@ -5,62 +5,66 @@ ```ts import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { root: RouteRef; }, {}, { - 'api:devtools': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: undefined; - } - >; - 'page:devtools': ExtensionDefinition< - { + 'api:devtools': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'page:devtools': ExtensionDefinition<{ + kind: 'page'; + namespace: undefined; + name: undefined; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; - 'nav-item:devtools': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef< + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + }>; + 'nav-item:devtools': ExtensionDefinition<{ + kind: 'nav-item'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { title: string; icon: IconComponent; @@ -68,14 +72,9 @@ const _default: BackstagePlugin< }, 'core.nav-item.target', {} - >, - {}, - { - kind: 'nav-item'; - namespace: undefined; - name: undefined; - } - >; + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index db6236f327..22c0b3eddb 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.17", + "version": "0.1.18-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "devtools", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index 64db006eea..52cf45ae2c 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.4.2-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + ## 0.4.0 ### Minor Changes diff --git a/plugins/events-backend-module-aws-sqs/api-report-alpha.md b/plugins/events-backend-module-aws-sqs/api-report-alpha.md index ee14cc5b5b..1e8ae5c690 100644 --- a/plugins/events-backend-module-aws-sqs/api-report-alpha.md +++ b/plugins/events-backend-module-aws-sqs/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const eventsModuleAwsSqsConsumingEventPublisher: BackendFeatureCompat; +const eventsModuleAwsSqsConsumingEventPublisher: BackendFeature; export default eventsModuleAwsSqsConsumingEventPublisher; // (No @packageDocumentation comment for this package) diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 89d8c17c85..2354c1fb79 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-aws-sqs", - "version": "0.4.0", + "version": "0.4.2-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.test.ts b/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.test.ts index a25d2ef947..13bc3d2272 100644 --- a/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.test.ts +++ b/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.test.ts @@ -35,7 +35,7 @@ describe('eventsModuleAwsSqsConsumingEventPublisher', () => { await startTestBackend({ features: [ - eventsServiceFactory(), + eventsServiceFactory, eventsModuleAwsSqsConsumingEventPublisher, mockServices.rootConfig.factory({ data: { diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index efe13729b2..ad86906d1c 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-azure +## 0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/events-backend-module-azure/api-report-alpha.md b/plugins/events-backend-module-azure/api-report-alpha.md index 03e807242f..861ff95bcd 100644 --- a/plugins/events-backend-module-azure/api-report-alpha.md +++ b/plugins/events-backend-module-azure/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const eventsModuleAzureDevOpsEventRouter: BackendFeatureCompat; +const eventsModuleAzureDevOpsEventRouter: BackendFeature; export default eventsModuleAzureDevOpsEventRouter; export { eventsModuleAzureDevOpsEventRouter }; diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 7a49aff69c..11202a486e 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-azure", - "version": "0.2.9", + "version": "0.2.11-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.test.ts b/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.test.ts index b9271ece10..9ca2ed819d 100644 --- a/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.test.ts +++ b/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.test.ts @@ -32,7 +32,7 @@ describe('eventsModuleAzureDevOpsEventRouter', () => { }); await startTestBackend({ - features: [eventsServiceFactory(), eventsModuleAzureDevOpsEventRouter], + features: [eventsServiceFactory, eventsModuleAzureDevOpsEventRouter], }); expect(events.subscribed).toHaveLength(1); diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index f84238c5b9..bd3a5dd34c 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/api-report-alpha.md b/plugins/events-backend-module-bitbucket-cloud/api-report-alpha.md index 71c106ef01..6ee4fab225 100644 --- a/plugins/events-backend-module-bitbucket-cloud/api-report-alpha.md +++ b/plugins/events-backend-module-bitbucket-cloud/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const eventsModuleBitbucketCloudEventRouter: BackendFeatureCompat; +const eventsModuleBitbucketCloudEventRouter: BackendFeature; export default eventsModuleBitbucketCloudEventRouter; export { eventsModuleBitbucketCloudEventRouter }; diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 959639fdd9..28a917c763 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-cloud", - "version": "0.2.9", + "version": "0.2.11-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.test.ts b/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.test.ts index 799a71de8f..aa15120418 100644 --- a/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.test.ts +++ b/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.test.ts @@ -32,7 +32,7 @@ describe('eventsModuleBitbucketCloudEventRouter', () => { }); await startTestBackend({ - features: [eventsServiceFactory(), eventsModuleBitbucketCloudEventRouter], + features: [eventsServiceFactory, eventsModuleBitbucketCloudEventRouter], }); expect(events.subscribed).toHaveLength(1); diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index 88c2ac91d8..b2b9aa139f 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/api-report-alpha.md b/plugins/events-backend-module-gerrit/api-report-alpha.md index 8922f317e9..83b2fdff98 100644 --- a/plugins/events-backend-module-gerrit/api-report-alpha.md +++ b/plugins/events-backend-module-gerrit/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const eventsModuleGerritEventRouter: BackendFeatureCompat; +const eventsModuleGerritEventRouter: BackendFeature; export default eventsModuleGerritEventRouter; export { eventsModuleGerritEventRouter }; diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 10abf94085..7bd829e02e 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gerrit", - "version": "0.2.9", + "version": "0.2.11-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.test.ts b/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.test.ts index ca661215de..cf46a9a7ee 100644 --- a/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.test.ts +++ b/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.test.ts @@ -32,7 +32,7 @@ describe('eventsModuleGerritEventRouter', () => { }); await startTestBackend({ - features: [eventsServiceFactory(), eventsModuleGerritEventRouter], + features: [eventsServiceFactory, eventsModuleGerritEventRouter], }); expect(events.subscribed).toHaveLength(1); diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index f08743d3b3..9ad3204050 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend-module-github +## 0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/events-backend-module-github/api-report-alpha.md b/plugins/events-backend-module-github/api-report-alpha.md index 50d899f71d..63e51f1dae 100644 --- a/plugins/events-backend-module-github/api-report-alpha.md +++ b/plugins/events-backend-module-github/api-report-alpha.md @@ -3,13 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -export const eventsModuleGithubEventRouter: BackendFeatureCompat; +export const eventsModuleGithubEventRouter: BackendFeature; // @alpha -export const eventsModuleGithubWebhook: BackendFeatureCompat; +export const eventsModuleGithubWebhook: BackendFeature; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index 16238b7b93..3c08b10c89 100644 --- a/plugins/events-backend-module-github/package.json +++ b/plugins/events-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-github", - "version": "0.2.9", + "version": "0.2.11-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.test.ts b/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.test.ts index ba0c71f158..d69a128c4c 100644 --- a/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.test.ts +++ b/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.test.ts @@ -32,7 +32,7 @@ describe('eventsModuleGithubEventRouter', () => { }); await startTestBackend({ - features: [eventsServiceFactory(), eventsModuleGithubEventRouter], + features: [eventsServiceFactory, eventsModuleGithubEventRouter], }); expect(events.subscribed).toHaveLength(1); diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index 85b62d9adf..c4faca4da4 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.2.11-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/api-report-alpha.md b/plugins/events-backend-module-gitlab/api-report-alpha.md index 4e81eb4269..1406b3ef64 100644 --- a/plugins/events-backend-module-gitlab/api-report-alpha.md +++ b/plugins/events-backend-module-gitlab/api-report-alpha.md @@ -3,13 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -export const eventsModuleGitlabEventRouter: BackendFeatureCompat; +export const eventsModuleGitlabEventRouter: BackendFeature; // @alpha -export const eventsModuleGitlabWebhook: BackendFeatureCompat; +export const eventsModuleGitlabWebhook: BackendFeature; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index f0183783ec..5d287fb864 100644 --- a/plugins/events-backend-module-gitlab/package.json +++ b/plugins/events-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gitlab", - "version": "0.2.9", + "version": "0.2.11-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.test.ts b/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.test.ts index 44e90d1b7e..48ed7ef4d3 100644 --- a/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.test.ts +++ b/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.test.ts @@ -32,7 +32,7 @@ describe('eventsModuleGitlabEventRouter', () => { }); await startTestBackend({ - features: [eventsServiceFactory(), eventsModuleGitlabEventRouter], + features: [eventsServiceFactory, eventsModuleGitlabEventRouter], }); expect(events.subscribed).toHaveLength(1); diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 3047079949..b9c612b329 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.0-next.0 + ## 0.1.33 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 0fb0092bf0..aae188ef7a 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-test-utils", - "version": "0.1.33", + "version": "0.1.35-next.0", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", "backstage": { "role": "node-library", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index 763699805b..f8fbf82dc7 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-events-backend +## 0.3.12-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/config@1.2.0 + ## 0.3.10 ### Patch Changes diff --git a/plugins/events-backend/api-report-alpha.md b/plugins/events-backend/api-report-alpha.md index a3cc7148ce..ef881006e4 100644 --- a/plugins/events-backend/api-report-alpha.md +++ b/plugins/events-backend/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const eventsPlugin: BackendFeatureCompat; +const eventsPlugin: BackendFeature; export default eventsPlugin; // (No @packageDocumentation comment for this package) diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index a0b2b17ba1..b1b586ecfa 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.3.10", + "version": "0.3.12-next.0", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-backend/src/service/EventsPlugin.test.ts b/plugins/events-backend/src/service/EventsPlugin.test.ts index bb923bbed6..48ce1090fb 100644 --- a/plugins/events-backend/src/service/EventsPlugin.test.ts +++ b/plugins/events-backend/src/service/EventsPlugin.test.ts @@ -56,7 +56,7 @@ describe('eventsPlugin', () => { const { server } = await startTestBackend({ extensionPoints: [], features: [ - eventsServiceFactory(), + eventsServiceFactory, eventsPlugin, testModule, mockServices.logger.factory(), diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 5762c94e9e..3dfcf3c9e9 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-events-node +## 0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + ## 0.3.9 ### Patch Changes diff --git a/plugins/events-node/api-report.md b/plugins/events-node/api-report.md index 729f526dee..d8a2281faa 100644 --- a/plugins/events-node/api-report.md +++ b/plugins/events-node/api-report.md @@ -4,7 +4,7 @@ ```ts import { LoggerService } from '@backstage/backend-plugin-api'; -import { ServiceFactoryCompat } from '@backstage/backend-plugin-api'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; // @public @@ -63,11 +63,10 @@ export interface EventsService { export type EventsServiceEventHandler = (params: EventParams) => Promise; // @public (undocumented) -export const eventsServiceFactory: ServiceFactoryCompat< +export const eventsServiceFactory: ServiceFactory< EventsService, 'plugin', - 'singleton', - undefined + 'singleton' >; // @public diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 2b51efd6af..435f2f6667 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-node", - "version": "0.3.9", + "version": "0.4.0-next.0", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index 7c555c4754..ea9a0bca3a 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.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/errors@1.2.4 + ## 1.0.30 ### Patch Changes diff --git a/plugins/example-todo-list-backend/api-report.md b/plugins/example-todo-list-backend/api-report.md index 69c6bdf239..8fa39f0cec 100644 --- a/plugins/example-todo-list-backend/api-report.md +++ b/plugins/example-todo-list-backend/api-report.md @@ -3,22 +3,22 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import express from 'express'; -import { IdentityApi } from '@backstage/plugin-auth-node'; +import { HttpAuthService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; // @public export function createRouter(options: RouterOptions): Promise; // @public -const exampleTodoListPlugin: BackendFeatureCompat; +const exampleTodoListPlugin: BackendFeature; export default exampleTodoListPlugin; // @public export interface RouterOptions { // (undocumented) - identity: IdentityApi; + httpAuth: HttpAuthService; // (undocumented) logger: LoggerService; } diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 8c378a814e..dd6f2f74ff 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.30", + "version": "1.0.31-next.0", "backstage": { "role": "backend-plugin", "pluginId": "todo-list", diff --git a/plugins/example-todo-list-backend/src/plugin.ts b/plugins/example-todo-list-backend/src/plugin.ts index ccff192d4c..1c7bba929a 100644 --- a/plugins/example-todo-list-backend/src/plugin.ts +++ b/plugins/example-todo-list-backend/src/plugin.ts @@ -30,14 +30,14 @@ export const exampleTodoListPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - identity: coreServices.identity, + httpAuth: coreServices.httpAuth, logger: coreServices.logger, httpRouter: coreServices.httpRouter, }, - async init({ identity, logger, httpRouter }) { + async init({ httpAuth, logger, httpRouter }) { httpRouter.use( await createRouter({ - identity, + httpAuth, logger, }), ); diff --git a/plugins/example-todo-list-backend/src/service/router.test.ts b/plugins/example-todo-list-backend/src/service/router.test.ts index e9abb5a76c..50bacac203 100644 --- a/plugins/example-todo-list-backend/src/service/router.test.ts +++ b/plugins/example-todo-list-backend/src/service/router.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; import express from 'express'; import request from 'supertest'; @@ -27,7 +26,7 @@ describe('createRouter', () => { beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), - identity: {} as DefaultIdentityClient, + httpAuth: mockServices.httpAuth.mock(), }); app = express().use(router); }); diff --git a/plugins/example-todo-list-backend/src/service/router.ts b/plugins/example-todo-list-backend/src/service/router.ts index 09b8669507..5f2d9fd37c 100644 --- a/plugins/example-todo-list-backend/src/service/router.ts +++ b/plugins/example-todo-list-backend/src/service/router.ts @@ -19,8 +19,7 @@ import express from 'express'; import Router from 'express-promise-router'; import { add, getAll, update } from './todos'; import { InputError } from '@backstage/errors'; -import { IdentityApi } from '@backstage/plugin-auth-node'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api'; /** * Dependencies of the todo-list router @@ -29,7 +28,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; */ export interface RouterOptions { logger: LoggerService; - identity: IdentityApi; + httpAuth: HttpAuthService; } /** @@ -44,7 +43,7 @@ export interface RouterOptions { export async function createRouter( options: RouterOptions, ): Promise { - const { logger, identity } = options; + const { logger, httpAuth } = options; const router = Router(); router.use(express.json()); @@ -59,16 +58,16 @@ export async function createRouter( }); router.post('/todos', async (req, res) => { - let author: string | undefined = undefined; - - const user = await identity.getIdentity({ request: req }); - author = user?.identity.userEntityRef; + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); if (!isTodoCreateRequest(req.body)) { throw new InputError('Invalid payload'); } - const todo = add({ title: req.body.title, author }); + const todo = add({ + title: req.body.title, + author: credentials.principal.userEntityRef, + }); res.json(todo); }); diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index b2435eb5db..4d412eae76 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-home +## 0.7.10-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-home-react@0.1.16 + ## 0.7.9 ### Patch Changes diff --git a/plugins/home/api-report-alpha.md b/plugins/home/api-report-alpha.md index e32412168b..7b4b157eb2 100644 --- a/plugins/home/api-report-alpha.md +++ b/plugins/home/api-report-alpha.md @@ -4,43 +4,40 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< {}, {}, { - 'page:home': ExtensionDefinition< - { - [x: string]: any; - } & { + 'page:home': ExtensionDefinition<{ + config: { path: string | undefined; - }, - { - [x: string]: any; - } & { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: { props: ExtensionInput< | ConfigurableExtensionDataRef< React_2.JSX.Element, @@ -61,13 +58,11 @@ const _default: BackstagePlugin< optional: true; } >; - }, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; + }; + kind: 'page'; + namespace: undefined; + name: undefined; + }>; } >; export default _default; diff --git a/plugins/home/package.json b/plugins/home/package.json index 97d7597b99..c7ef7b3862 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.7.9", + "version": "0.7.10-next.0", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index e944ce6255..3e81a64b08 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-kubernetes-backend +## 0.18.6-next.0 + +### Patch Changes + +- f55f8bf: The `KubernetesBuilder` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-kubernetes-node@0.1.19-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-permission-common@0.8.1 + ## 0.18.4 ### Patch Changes diff --git a/plugins/kubernetes-backend/api-report-alpha.md b/plugins/kubernetes-backend/api-report-alpha.md index c3a86779e9..f6da905f30 100644 --- a/plugins/kubernetes-backend/api-report-alpha.md +++ b/plugins/kubernetes-backend/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const kubernetesPlugin: BackendFeatureCompat; +const kubernetesPlugin: BackendFeature; export default kubernetesPlugin; // (No @packageDocumentation comment for this package) diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index c31af334d7..2711085b81 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -28,8 +28,8 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { ObjectToFetch as ObjectToFetch_2 } from '@backstage/plugin-kubernetes-node'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionsService } from '@backstage/backend-plugin-api'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { RequestHandler } from 'http-proxy-middleware'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { TokenCredential } from '@azure/identity'; // @public (undocumented) @@ -153,7 +153,7 @@ export const HEADER_KUBERNETES_AUTH: string; // @public export const HEADER_KUBERNETES_CLUSTER: string; -// @public (undocumented) +// @public @deprecated (undocumented) export class KubernetesBuilder { constructor(env: KubernetesEnvironment); // (undocumented) @@ -268,7 +268,7 @@ export class KubernetesBuilder { setServiceLocator(serviceLocator?: KubernetesServiceLocator_2): this; } -// @public +// @public @deprecated export type KubernetesBuilderReturn = Promise<{ router: express.Router; clusterSupplier: KubernetesClustersSupplier_2; @@ -289,7 +289,7 @@ export type KubernetesClustersSupplier = // @public @deprecated (undocumented) export type KubernetesCredential = k8sAuthTypes.KubernetesCredential; -// @public (undocumented) +// @public @deprecated (undocumented) export interface KubernetesEnvironment { // (undocumented) auth?: AuthService; @@ -380,16 +380,16 @@ export class OidcStrategy implements AuthenticationStrategy_2 { validateCluster(authMetadata: AuthMetadata_2): Error[]; } -// @public (undocumented) +// @public @deprecated (undocumented) export interface RouterOptions { // (undocumented) catalogApi: CatalogApi; // (undocumented) clusterSupplier?: KubernetesClustersSupplier; // (undocumented) - config: Config; + config: RootConfigService; // (undocumented) - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; // (undocumented) logger: Logger; // (undocumented) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 978171e2ea..1401870767 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.18.4", + "version": "0.18.6-next.0", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-backend/src/plugin.ts b/plugins/kubernetes-backend/src/plugin.ts index 57221e377c..284fa2e5e2 100644 --- a/plugins/kubernetes-backend/src/plugin.ts +++ b/plugins/kubernetes-backend/src/plugin.ts @@ -193,27 +193,33 @@ export const kubernetesPlugin = createBackendPlugin({ auth, httpAuth, }) { - // TODO: expose all of the customization & extension points of the builder here - const builder: KubernetesBuilder = KubernetesBuilder.createBuilder({ - logger, - config, - catalogApi, - permissions, - discovery, - auth, - httpAuth, - }) - .setObjectsProvider(extPointObjectsProvider.getObjectsProvider()) - .setClusterSupplier(extPointClusterSuplier.getClusterSupplier()) - .setFetcher(extPointFetcher.getFetcher()) - .setServiceLocator(extPointServiceLocator.getServiceLocator()); + if (config.has('kubernetes')) { + // TODO: expose all of the customization & extension points of the builder here + const builder: KubernetesBuilder = KubernetesBuilder.createBuilder({ + logger, + config, + catalogApi, + permissions, + discovery, + auth, + httpAuth, + }) + .setObjectsProvider(extPointObjectsProvider.getObjectsProvider()) + .setClusterSupplier(extPointClusterSuplier.getClusterSupplier()) + .setFetcher(extPointFetcher.getFetcher()) + .setServiceLocator(extPointServiceLocator.getServiceLocator()); - AuthStrategy.addAuthStrategiesFromArray( - extPointAuthStrategy.getAuthenticationStrategies(), - builder, - ); - const { router } = await builder.build(); - http.use(router); + AuthStrategy.addAuthStrategiesFromArray( + extPointAuthStrategy.getAuthenticationStrategies(), + builder, + ); + const { router } = await builder.build(); + http.use(router); + } else { + logger.warn( + 'Failed to initialize kubernetes backend: valid kubernetes config is missing', + ); + } }, }); }, diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index 60683a74f3..3299d11e5d 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -728,6 +728,19 @@ metadata: const throwError = () => startTestBackend({ features: [ + mockServices.rootConfig.factory({ + data: { + kubernetes: { + serviceLocatorMethod: { type: 'multiTenant' }, + clusterLocatorMethods: [ + { + type: 'config', + clusters: [], + }, + ], + }, + }, + }), import('@backstage/plugin-kubernetes-backend/alpha'), createBackendModule({ pluginId: 'kubernetes', diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 8f006cdc86..62577c4fb0 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -74,7 +74,7 @@ import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; import { KubernetesProxy } from './KubernetesProxy'; /** - * + * @deprecated Please migrate to the new backend system as this will be removed in the future. * @public */ export interface KubernetesEnvironment { @@ -89,7 +89,7 @@ export interface KubernetesEnvironment { /** * The return type of the `KubernetesBuilder.build` method - * + * @deprecated Please migrate to the new backend system as this will be removed in the future. * @public */ export type KubernetesBuilderReturn = Promise<{ @@ -104,9 +104,9 @@ export type KubernetesBuilderReturn = Promise<{ }>; /** - * + * @deprecated Please migrate to the new backend system as this will be removed in the future. * @public - */ + * */ export class KubernetesBuilder { private clusterSupplier?: KubernetesClustersSupplier; private defaultClusterRefreshInterval: Duration = Duration.fromObject({ diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index f7e58f535c..d1b54c5927 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -14,25 +14,27 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; import { Logger } from 'winston'; import { KubernetesClustersSupplier } from '../types/types'; import express from 'express'; import { KubernetesBuilder } from './KubernetesBuilder'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; +import { + DiscoveryService, + RootConfigService, +} from '@backstage/backend-plugin-api'; /** - * + * @deprecated Please migrate to the new backend system as this will be removed in the future. * @public */ export interface RouterOptions { logger: Logger; - config: Config; + config: RootConfigService; catalogApi: CatalogApi; clusterSupplier?: KubernetesClustersSupplier; - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; permissions: PermissionEvaluator; } @@ -40,7 +42,7 @@ export interface RouterOptions { * creates and configure a new router for handling the kubernetes backend APIs * @param options - specifies the options required by this plugin * @returns a new router - * @deprecated Please use the new KubernetesBuilder instead like this + * @deprecated Please migrate to the new backend system as this will be removed in the future. * ``` * import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend'; * const { router } = await KubernetesBuilder.createBuilder({ diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index a35b71719a..8fedd3d637 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.2 + ## 0.0.14 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index c7eb6658b0..65e475675b 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.14", + "version": "0.0.15-next.0", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 6d81f07df2..7b687e0fa7 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-node +## 0.1.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.1.17 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 5f9c9efa55..2c998e9802 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.1.17", + "version": "0.1.19-next.0", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index a2e47a6b1b..4ff750f743 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-kubernetes +## 0.11.14-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- bfc0f42: Make k8s entity content appear on components & resources only by default in new FE system +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.2 + ## 0.11.13 ### Patch Changes diff --git a/plugins/kubernetes/README.md b/plugins/kubernetes/README.md index 4ffde719f2..380c1dffbd 100644 --- a/plugins/kubernetes/README.md +++ b/plugins/kubernetes/README.md @@ -52,7 +52,7 @@ export const app = createApp({ }); ``` -2. Next, enable your desired extensions in `app-config.yaml` +2. Next, enable your desired extensions in `app-config.yaml`. ```yaml app: @@ -60,4 +60,14 @@ app: - entity-content:kubernetes/kubernetes ``` -Now, the extension appears on your entity page as one of the tabs, which is called `KUBERNETES` +Now, the extension appears on your entity page as one of the tabs, which is called `KUBERNETES`. +By default, the tab will only appear on entities that are Components or Resources. You can override +that behavior by providing a config block to the extension, like so: + +```yaml +app: + extensions: + - entity-content:kubernetes/kubernetes: + config: + filter: kind:component,api,resource,system +``` diff --git a/plugins/kubernetes/api-report-alpha.md b/plugins/kubernetes/api-report-alpha.md index 15f7efaf1b..852a5c6c1a 100644 --- a/plugins/kubernetes/api-report-alpha.md +++ b/plugins/kubernetes/api-report-alpha.md @@ -7,118 +7,139 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { kubernetes: RouteRef; }, {}, { - 'page:kubernetes': ExtensionDefinition< - { + 'api:kubernetes': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'page:kubernetes': ExtensionDefinition<{ + kind: 'page'; + namespace: undefined; + name: undefined; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; - 'entity-content:kubernetes/kubernetes': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-content:kubernetes/kubernetes': ExtensionDefinition<{ + kind: 'entity-content'; + namespace: undefined; + name: 'kubernetes'; + config: { path: string | undefined; title: string | undefined; filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; title?: string | undefined; path?: string | undefined; - }, - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-content'; - namespace: undefined; - name: 'kubernetes'; - } - >; - 'api:kubernetes/proxy': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'proxy'; - } - >; - 'api:kubernetes/auth-providers': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'auth-providers'; - } - >; - 'api:kubernetes/cluster-link-formatter': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: 'cluster-link-formatter'; - } - >; + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-content-title', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'api:kubernetes/proxy': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'proxy'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:kubernetes/auth-providers': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'auth-providers'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'api:kubernetes/cluster-link-formatter': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: 'cluster-link-formatter'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index c982e67d04..238155d940 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.11.13", + "version": "0.11.14-next.0", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes/src/alpha/entityContents.tsx b/plugins/kubernetes/src/alpha/entityContents.tsx index 26730e76d4..2703fcdfc2 100644 --- a/plugins/kubernetes/src/alpha/entityContents.tsx +++ b/plugins/kubernetes/src/alpha/entityContents.tsx @@ -23,6 +23,7 @@ export const entityKubernetesContent = EntityContentBlueprint.make({ params: { defaultPath: '/kubernetes', defaultTitle: 'Kubernetes', + filter: 'kind:component,resource', loader: () => import('./KubernetesContentPage').then(m => compatWrapper(), diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index b040e3fe5a..33d1df94c1 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + ## 0.2.0 ### Minor Changes diff --git a/plugins/notifications-backend-module-email/api-report.md b/plugins/notifications-backend-module-email/api-report.md index b105f15cba..be3b58d1fd 100644 --- a/plugins/notifications-backend-module-email/api-report.md +++ b/plugins/notifications-backend-module-email/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { Notification as Notification_2 } from '@backstage/plugin-notifications-common'; @@ -17,7 +17,7 @@ export interface NotificationsEmailTemplateExtensionPoint { export const notificationsEmailTemplateExtensionPoint: ExtensionPoint; // @public (undocumented) -const notificationsModuleEmail: BackendFeatureCompat; +const notificationsModuleEmail: BackendFeature; export default notificationsModuleEmail; // @public (undocumented) diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index 9c799503e8..20e076cdd2 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-email", - "version": "0.2.0", + "version": "0.3.0-next.0", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index bd0323d5c2..081048900f 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-notifications-backend +## 0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-events-node@0.4.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-notifications-common@0.0.5 + ## 0.3.4 ### Patch Changes diff --git a/plugins/notifications-backend/api-report.md b/plugins/notifications-backend/api-report.md index de5441f85e..c0769be06d 100644 --- a/plugins/notifications-backend/api-report.md +++ b/plugins/notifications-backend/api-report.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public -const notificationsPlugin: BackendFeatureCompat; +const notificationsPlugin: BackendFeature; export default notificationsPlugin; // (No @packageDocumentation comment for this package) diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index faf3118ef8..a0273d628b 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.3.4", + "version": "0.4.0-next.0", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-backend/src/plugin.ts b/plugins/notifications-backend/src/plugin.ts index c0344fb06f..288a0f8507 100644 --- a/plugins/notifications-backend/src/plugin.ts +++ b/plugins/notifications-backend/src/plugin.ts @@ -67,6 +67,7 @@ export const notificationsPlugin = createBackendPlugin({ database: coreServices.database, discovery: coreServices.discovery, signals: signalsServiceRef, + config: coreServices.rootConfig, }, async init({ auth, @@ -77,6 +78,7 @@ export const notificationsPlugin = createBackendPlugin({ database, discovery, signals, + config, }) { httpRouter.use( await createRouter({ @@ -84,6 +86,7 @@ export const notificationsPlugin = createBackendPlugin({ httpAuth, userInfo, logger, + config, database, discovery, signals, diff --git a/plugins/notifications-backend/src/service/router.test.ts b/plugins/notifications-backend/src/service/router.test.ts index 804b0ff088..a3a66636ca 100644 --- a/plugins/notifications-backend/src/service/router.test.ts +++ b/plugins/notifications-backend/src/service/router.test.ts @@ -23,7 +23,8 @@ import request from 'supertest'; import { createRouter } from './router'; import { ConfigReader } from '@backstage/config'; import { SignalsService } from '@backstage/plugin-signals-node'; -import { mockServices } from '@backstage/backend-test-utils'; +import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; +import { NotificationSendOptions } from '@backstage/plugin-notifications-node'; function createDatabase(): PluginDatabaseManager { return DatabaseManager.fromConfig( @@ -47,8 +48,13 @@ describe('createRouter', () => { const discovery = mockServices.discovery(); const userInfo = mockServices.userInfo(); - const httpAuth = mockServices.httpAuth(); + const httpAuth = mockServices.httpAuth({ + defaultCredentials: mockCredentials.service(), + }); const auth = mockServices.auth(); + const config = mockServices.rootConfig({ + data: { app: { baseUrl: 'http://localhost' } }, + }); beforeAll(async () => { const router = await createRouter({ @@ -57,6 +63,7 @@ describe('createRouter', () => { discovery, signals: signalService, userInfo, + config, httpAuth, auth, }); @@ -75,4 +82,80 @@ describe('createRouter', () => { expect(response.body).toEqual({ status: 'ok' }); }); }); + + describe('POST /', () => { + const sendNotification = async (data: NotificationSendOptions) => + request(app) + .post('/') + .send(data) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + it('returns error on invalid link', async () => { + const javascriptXSS = await sendNotification({ + recipients: { + type: 'broadcast', + }, + payload: { + title: 'test notification', + // eslint-disable-next-line no-script-url + link: 'javascript:alert(document.domain)', + }, + }); + + expect(javascriptXSS.status).toEqual(400); + + const ftpLink = await sendNotification({ + recipients: { + type: 'broadcast', + }, + payload: { + title: 'test notification', + link: 'ftp://example.com', + }, + }); + + expect(ftpLink.status).toEqual(400); + }); + + it('should accept absolute http links', async () => { + const httpLink = await sendNotification({ + recipients: { + type: 'broadcast', + }, + payload: { + title: 'test notification', + link: 'http://localhost/test', + }, + }); + + expect(httpLink.status).toEqual(200); + + const httpsLink = await sendNotification({ + recipients: { + type: 'broadcast', + }, + payload: { + title: 'test notification', + link: 'https://example.com', + }, + }); + + expect(httpsLink.status).toEqual(200); + }); + + it('should accept relative links', async () => { + const catalogLink = await sendNotification({ + recipients: { + type: 'broadcast', + }, + payload: { + title: 'test notification', + link: '/catalog', + }, + }); + + expect(catalogLink.status).toEqual(200); + }); + }); }); diff --git a/plugins/notifications-backend/src/service/router.ts b/plugins/notifications-backend/src/service/router.ts index abd7e2dc13..95b9467f4a 100644 --- a/plugins/notifications-backend/src/service/router.ts +++ b/plugins/notifications-backend/src/service/router.ts @@ -46,10 +46,12 @@ import { } from '@backstage/plugin-notifications-common'; import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams'; import { getUsersForEntityRef } from './getUsersForEntityRef'; +import { Config } from '@backstage/config'; /** @internal */ export interface RouterOptions { logger: LoggerService; + config: Config; database: PluginDatabaseManager; discovery: DiscoveryService; auth: AuthService; @@ -65,6 +67,7 @@ export async function createRouter( options: RouterOptions, ): Promise { const { + config, logger, database, auth, @@ -79,6 +82,7 @@ export async function createRouter( const catalogClient = catalog ?? new CatalogClient({ discoveryApi: discovery }); const store = await DatabaseNotificationsStore.create({ database }); + const frontendBaseUrl = config.getString('app.baseUrl'); const getUser = async (req: Request) => { const credentials = await httpAuth.credentials(req, { allow: ['user'] }); @@ -177,6 +181,18 @@ export async function createRouter( } }; + const validateLink = (link: string) => { + const stripLeadingSlash = (s: string) => s.replace(/^\//, ''); + const ensureTrailingSlash = (s: string) => s.replace(/\/?$/, '/'); + const url = new URL( + stripLeadingSlash(link), + ensureTrailingSlash(frontendBaseUrl), + ); + if (url.protocol !== 'https:' && url.protocol !== 'http:') { + throw new Error('Only HTTP/HTTPS links are allowed'); + } + }; + // TODO: Move to use OpenAPI router instead const router = Router(); router.use(express.json()); @@ -419,13 +435,21 @@ export async function createRouter( allow: ['service'], }); - const { title } = payload; + const { title, link } = payload; if (!recipients || !title) { logger.error(`Invalid notification request received`); throw new InputError(`Invalid notification request received`); } + if (link) { + try { + validateLink(link); + } catch (e) { + throw new InputError('Invalid link provided', e); + } + } + const origin = credentials.principal.subject; const baseNotification = { payload: { diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index 1c98305038..e1d48a7d88 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-notifications-node +## 0.2.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-signals-node@0.1.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-notifications-common@0.0.5 + ## 0.2.4 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index 37d2663a3a..af3fdcde9b 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.2.4", + "version": "0.2.6-next.0", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index 0f257047ba..19cd62732a 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-notifications +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-signals-react@0.0.5-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + ## 0.3.0 ### Minor Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index c847fc605d..ba211d21ea 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.3.0", + "version": "0.3.1-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 4570bcf112..f2b8ca2f81 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + ## 0.1.27 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 607600fbaa..3120392573 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org-react", - "version": "0.1.27", + "version": "0.1.28-next.0", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 55b9b3431a..8b98dd83e2 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-org +## 0.6.29-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.6.28 ### Patch Changes diff --git a/plugins/org/api-report-alpha.md b/plugins/org/api-report-alpha.md index 1187c8f0e3..a8c6bd70ec 100644 --- a/plugins/org/api-report-alpha.md +++ b/plugins/org/api-report-alpha.md @@ -3,152 +3,148 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; // @alpha (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< {}, { catalogIndex: ExternalRouteRef; }, { - 'entity-card:org/group-profile': ExtensionDefinition< - { + 'entity-card:org/group-profile': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'group-profile'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'group-profile'; - } - >; - 'entity-card:org/members-list': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:org/members-list': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'members-list'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'members-list'; - } - >; - 'entity-card:org/ownership': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:org/ownership': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'ownership'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'ownership'; - } - >; - 'entity-card:org/user-profile': ExtensionDefinition< - { + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; + 'entity-card:org/user-profile': ExtensionDefinition<{ + kind: 'entity-card'; + namespace: undefined; + name: 'user-profile'; + config: { filter: string | undefined; - }, - { + }; + configInput: { filter?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef< - (entity: Entity) => boolean, - 'catalog.entity-filter-function', - { - optional: true; - } - > - | ConfigurableExtensionDataRef< - string, - 'catalog.entity-filter-expression', - { - optional: true; - } - >, - {}, - { - kind: 'entity-card'; - namespace: undefined; - name: 'user-profile'; - } - >; + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'catalog.entity-filter-expression', + { + optional: true; + } + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/org/package.json b/plugins/org/package.json index 3940932cbe..94c3a7fdc3 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.28", + "version": "0.6.29-next.0", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 6d2a77fb3c..32c768972b 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-permission-common@0.8.1 + ## 0.1.20 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/api-report.md b/plugins/permission-backend-module-policy-allow-all/api-report.md index 02a4255424..26966f4938 100644 --- a/plugins/permission-backend-module-policy-allow-all/api-report.md +++ b/plugins/permission-backend-module-policy-allow-all/api-report.md @@ -3,9 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public -const permissionModuleAllowAllPolicy: BackendFeatureCompat; +const permissionModuleAllowAllPolicy: BackendFeature; export default permissionModuleAllowAllPolicy; ``` diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index 637843cdeb..7ee62b6e91 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", - "version": "0.1.20", + "version": "0.2.0-next.0", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 2f43217d5d..2733e1a634 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-permission-backend +## 0.5.49-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- fcb9356: Deprecated `createRouter` and its router options in favour of the new backend system. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + ## 0.5.47 ### Patch Changes diff --git a/plugins/permission-backend/api-report-alpha.md b/plugins/permission-backend/api-report-alpha.md index 7448c4eed0..d0a10b6b3a 100644 --- a/plugins/permission-backend/api-report-alpha.md +++ b/plugins/permission-backend/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const permissionPlugin: BackendFeatureCompat; +const permissionPlugin: BackendFeature; export default permissionPlugin; // (No @packageDocumentation comment for this package) diff --git a/plugins/permission-backend/api-report.md b/plugins/permission-backend/api-report.md index d8d40be440..bd976a8d7b 100644 --- a/plugins/permission-backend/api-report.md +++ b/plugins/permission-backend/api-report.md @@ -4,24 +4,24 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import express from 'express'; import { HttpAuthService } from '@backstage/backend-plugin-api'; import { IdentityApi } from '@backstage/plugin-auth-node'; import { LoggerService } from '@backstage/backend-plugin-api'; import { PermissionPolicy } from '@backstage/plugin-permission-node'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { UserInfoService } from '@backstage/backend-plugin-api'; -// @public +// @public @deprecated export function createRouter(options: RouterOptions): Promise; -// @public +// @public @deprecated export interface RouterOptions { // (undocumented) auth?: AuthService; // (undocumented) - config: Config; + config: RootConfigService; // (undocumented) discovery: DiscoveryService; // (undocumented) diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 4b6287d7d2..42d1ce3dae 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.47", + "version": "0.5.49-next.0", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-backend/src/service/router.ts b/plugins/permission-backend/src/service/router.ts index fd33f0b629..db2b72f95c 100644 --- a/plugins/permission-backend/src/service/router.ts +++ b/plugins/permission-backend/src/service/router.ts @@ -42,7 +42,6 @@ import { import { PermissionIntegrationClient } from './PermissionIntegrationClient'; import { memoize } from 'lodash'; import DataLoader from 'dataloader'; -import { Config } from '@backstage/config'; import { AuthService, BackstageCredentials, @@ -51,6 +50,7 @@ import { DiscoveryService, HttpAuthService, LoggerService, + RootConfigService, UserInfoService, } from '@backstage/backend-plugin-api'; @@ -97,13 +97,14 @@ const evaluatePermissionRequestBatchSchema: z.ZodSchema { it('should bypass the permission backend if permissions are disabled', async () => { const client = ServerPermissionClient.fromConfig(new ConfigReader({}), { discovery, - tokenManager: mockServices.tokenManager.mock(), auth: mockServices.auth(), }); @@ -101,7 +100,6 @@ describe('ServerPermissionClient', () => { it('should bypass the permission backend if permissions are enabled and request has valid server credentials', async () => { const client = ServerPermissionClient.fromConfig(config, { discovery, - tokenManager: mockServices.tokenManager.mock(), auth: mockServices.auth(), }); @@ -115,7 +113,6 @@ describe('ServerPermissionClient', () => { it('should call the permission backend if permissions are enabled and request does not have valid server credentials', async () => { const client = ServerPermissionClient.fromConfig(config, { discovery, - tokenManager: mockServices.tokenManager.mock(), auth: mockServices.auth(), }); @@ -156,7 +153,6 @@ describe('ServerPermissionClient', () => { it('should bypass the permission backend if permissions are disabled', async () => { const client = ServerPermissionClient.fromConfig(new ConfigReader({}), { discovery, - tokenManager: mockServices.tokenManager.mock(), auth: mockServices.auth(), }); @@ -173,7 +169,6 @@ describe('ServerPermissionClient', () => { it('should bypass the permission backend if permissions are enabled and request has valid server credentials', async () => { const client = ServerPermissionClient.fromConfig(config, { discovery, - tokenManager: mockServices.tokenManager.mock(), auth: mockServices.auth(), }); @@ -190,7 +185,6 @@ describe('ServerPermissionClient', () => { it('should call the permission backend if permissions are enabled and request does not have valid server credentials', async () => { const client = ServerPermissionClient.fromConfig(config, { discovery, - tokenManager: mockServices.tokenManager.mock(), auth: mockServices.auth(), }); @@ -223,7 +217,6 @@ describe('ServerPermissionClient', () => { }), { discovery, - tokenManager: mockServices.tokenManager(), auth: mockServices.auth(), }, ); diff --git a/plugins/permission-node/src/ServerPermissionClient.ts b/plugins/permission-node/src/ServerPermissionClient.ts index e21067bfa1..c34a8fe930 100644 --- a/plugins/permission-node/src/ServerPermissionClient.ts +++ b/plugins/permission-node/src/ServerPermissionClient.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { createLegacyAuthAdapters } from '@backstage/backend-common'; +import { + TokenManager, + createLegacyAuthAdapters, +} from '@backstage/backend-common'; import { AuthService, BackstageCredentials, @@ -22,7 +25,6 @@ import { DiscoveryService, PermissionsService, PermissionsServiceRequestOptions, - TokenManagerService, } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { @@ -51,7 +53,8 @@ export class ServerPermissionClient implements PermissionsService { config: Config, options: { discovery: DiscoveryService; - tokenManager: TokenManagerService; + /** @deprecated This option will be removed in the future, provide a the auth option instead */ + tokenManager?: TokenManager; auth?: AuthService; }, ) { @@ -62,6 +65,7 @@ export class ServerPermissionClient implements PermissionsService { if ( permissionEnabled && + tokenManager && (tokenManager as any).isInsecureServerTokenManager ) { throw new Error( diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 3db9b519fa..2038b4a7ea 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-proxy-backend +## 0.5.6-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- d298e6e: Deprecated `createRouter` and its router options in favour of the new backend system. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + ## 0.5.4 ### Patch Changes diff --git a/plugins/proxy-backend/api-report-alpha.md b/plugins/proxy-backend/api-report-alpha.md index bbd891b014..e6d161655d 100644 --- a/plugins/proxy-backend/api-report-alpha.md +++ b/plugins/proxy-backend/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // (No @packageDocumentation comment for this package) diff --git a/plugins/proxy-backend/api-report.md b/plugins/proxy-backend/api-report.md index 12e6bfa1c9..9e1b5087f1 100644 --- a/plugins/proxy-backend/api-report.md +++ b/plugins/proxy-backend/api-report.md @@ -3,20 +3,20 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Config } from '@backstage/config'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; import express from 'express'; import { Logger } from 'winston'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { RootConfigService } from '@backstage/backend-plugin-api'; -// @public +// @public @deprecated export function createRouter(options: RouterOptions): Promise; -// @public (undocumented) +// @public @deprecated (undocumented) export interface RouterOptions { // (undocumented) - config: Config; + config: RootConfigService; // (undocumented) - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; // (undocumented) logger: Logger; // (undocumented) diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index fdddaa6283..4638be55b1 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.5.4", + "version": "0.5.6-next.0", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin", diff --git a/plugins/proxy-backend/src/service/router.credentials.test.ts b/plugins/proxy-backend/src/service/router.credentials.test.ts index a363e7c632..a48b18e8e2 100644 --- a/plugins/proxy-backend/src/service/router.credentials.test.ts +++ b/plugins/proxy-backend/src/service/router.credentials.test.ts @@ -80,8 +80,8 @@ describe('credentials', () => { features: [ import('../alpha'), mockServices.rootConfig.factory({ data: config }), - authServiceFactory(), - httpAuthServiceFactory(), + authServiceFactory, + httpAuthServiceFactory, ], }); @@ -295,5 +295,5 @@ describe('credentials', () => { } finally { await backend.stop(); } - }); + }, 20_000); }); diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 6a513678f0..9ea064ea55 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -25,9 +25,12 @@ import { } from 'http-proxy-middleware'; import { Logger } from 'winston'; import http from 'http'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { JsonObject } from '@backstage/types'; -import { HttpRouterService } from '@backstage/backend-plugin-api'; +import { + DiscoveryService, + HttpRouterService, + RootConfigService, +} from '@backstage/backend-plugin-api'; // A list of headers that are always forwarded to the proxy targets. const safeForwardHeaders = [ @@ -50,11 +53,14 @@ const safeForwardHeaders = [ 'user-agent', ]; -/** @public */ +/** + * @public + * @deprecated Please migrate to the new backend system as this will be removed in the future. + */ export interface RouterOptions { logger: Logger; - config: Config; - discovery: PluginEndpointDiscovery; + config: RootConfigService; + discovery: DiscoveryService; skipInvalidProxies?: boolean; reviveConsumedRequestBodies?: boolean; } @@ -278,6 +284,7 @@ function readProxyConfig(config: Config, logger: Logger): JsonObject { * * @see https://backstage.io/docs/plugins/proxying * @public + * @deprecated Please migrate to the new backend system as this will be removed in the future. */ export async function createRouter( options: RouterOptions, diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index b6610a9638..1c82a5bba1 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.1.15 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/api-report.md b/plugins/scaffolder-backend-module-azure/api-report.md index be5ab117e4..f61fff9b0f 100644 --- a/plugins/scaffolder-backend-module-azure/api-report.md +++ b/plugins/scaffolder-backend-module-azure/api-report.md @@ -3,14 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; // @public -const azureModule: BackendFeatureCompat; +const azureModule: BackendFeature; export default azureModule; // @public diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 43cdd10a96..eb4fb7e935 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", - "version": "0.1.15", + "version": "0.2.0-next.0", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index dfec3e29aa..a305262a6d 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + ## 0.1.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md b/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md index 1f1a7ca1d0..90a8cbc29f 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md @@ -3,14 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; // @public -const bitbucketCloudModule: BackendFeatureCompat; +const bitbucketCloudModule: BackendFeature; export default bitbucketCloudModule; // @public diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index cfedc6048a..dbbb2082e6 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud", - "version": "0.1.13", + "version": "0.2.0-next.0", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index 8c12baad68..e2067f2b96 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.1.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/api-report.md b/plugins/scaffolder-backend-module-bitbucket-server/api-report.md index 50377f8db2..d103743b91 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/api-report.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/api-report.md @@ -3,14 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; // @public -const bitbucketServerModule: BackendFeatureCompat; +const bitbucketServerModule: BackendFeature; export default bitbucketServerModule; // @public diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 76d00d0ea3..6305efa7c1 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-server", - "version": "0.1.13", + "version": "0.2.0-next.0", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 5f36050fff..b628eb3671 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/api-report.md b/plugins/scaffolder-backend-module-bitbucket/api-report.md index db73dcbf2c..952b1665fb 100644 --- a/plugins/scaffolder-backend-module-bitbucket/api-report.md +++ b/plugins/scaffolder-backend-module-bitbucket/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import * as bitbucketCloud from '@backstage/plugin-scaffolder-backend-module-bitbucket-cloud'; import * as bitbucketServer from '@backstage/plugin-scaffolder-backend-module-bitbucket-server'; import { Config } from '@backstage/config'; @@ -12,7 +12,7 @@ import { ScmIntegrationRegistry } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; // @public @deprecated -const bitbucketModule: BackendFeatureCompat; +const bitbucketModule: BackendFeature; export default bitbucketModule; // @public @deprecated (undocumented) diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index d222e24fc3..81d34dcf50 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.2.13", + "version": "0.3.0-next.0", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 3fbba8f6da..d0861cf549 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.2.24 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md b/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md index df2e74337f..2dd5e244dd 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrations } from '@backstage/integration'; @@ -11,7 +11,7 @@ import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { UrlReaderService } from '@backstage/backend-plugin-api'; // @public -const confluenceToMarkdownModule: BackendFeatureCompat; +const confluenceToMarkdownModule: BackendFeature; export default confluenceToMarkdownModule; // @public (undocumented) diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index a85e032cea..8766ae63e3 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", - "version": "0.2.24", + "version": "0.3.0-next.0", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 38b4185f16..2097f650ab 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + ## 0.2.47 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/api-report.md b/plugins/scaffolder-backend-module-cookiecutter/api-report.md index a2332da624..19f43389dc 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/api-report.md +++ b/plugins/scaffolder-backend-module-cookiecutter/api-report.md @@ -5,7 +5,7 @@ ```ts /// -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ContainerRunner } from '@backstage/backend-common'; import { JsonObject } from '@backstage/types'; import { ScmIntegrations } from '@backstage/integration'; @@ -13,7 +13,7 @@ import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { UrlReaderService } from '@backstage/backend-plugin-api'; // @public -const cookiecutterModule: BackendFeatureCompat; +const cookiecutterModule: BackendFeature; export default cookiecutterModule; // @public diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 4e30c0cf98..2c41ca4e8d 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", - "version": "0.2.47", + "version": "0.3.0-next.0", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index 46c3fe7957..b255527bdd 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.1.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/api-report.md b/plugins/scaffolder-backend-module-gcp/api-report.md index d75a6ce155..8928792ba9 100644 --- a/plugins/scaffolder-backend-module-gcp/api-report.md +++ b/plugins/scaffolder-backend-module-gcp/api-report.md @@ -3,9 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @public -const gcpBucketModule: BackendFeatureCompat; +const gcpBucketModule: BackendFeature; export default gcpBucketModule; ``` diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 71f16f4b96..8151f984c6 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gcp", - "version": "0.1.1", + "version": "0.2.0-next.0", "description": "The GCP Bucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index 7db14044e9..33b0cbdfe6 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 9e5923d: Added test cases for publish:gerrit action examples +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.1.15 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/api-report.md b/plugins/scaffolder-backend-module-gerrit/api-report.md index 60cd91355f..128f80954e 100644 --- a/plugins/scaffolder-backend-module-gerrit/api-report.md +++ b/plugins/scaffolder-backend-module-gerrit/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -43,6 +43,6 @@ export function createPublishGerritReviewAction(options: { >; // @public -const gerritModule: BackendFeatureCompat; +const gerritModule: BackendFeature; export default gerritModule; ``` diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 3557ac6719..c00e285e83 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", - "version": "0.1.15", + "version": "0.2.0-next.0", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.test.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.test.ts new file mode 100644 index 0000000000..16ffd198fb --- /dev/null +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.test.ts @@ -0,0 +1,590 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +jest.mock('@backstage/plugin-scaffolder-node', () => { + return { + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + initRepoAndPush: jest.fn().mockResolvedValue({ + commitHash: '460f19cc36b551763d157f1b5e4a4b446165dbb8', + }), + commitAndPushRepo: jest.fn().mockResolvedValue({ + commitHash: '460f19cc36b551763d157f1b5e4a4b446165dbb8', + }), + }; +}); + +import path from 'path'; +import { createPublishGerritAction } from './gerrit'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { registerMswTestHooks } from '@backstage/backend-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { initRepoAndPush } from '@backstage/plugin-scaffolder-node'; +import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; +import { examples } from './gerrit.examples'; +import yaml from 'yaml'; + +describe('publish:gerrit', () => { + const config = new ConfigReader({ + integrations: { + gerrit: [ + { + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.com/gitiles', + username: 'demouser', + password: 'accesstoken', + }, + ], + }, + }); + + const description = 'sample description'; + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishGerritAction({ integrations, config }); + const mockContext = createMockActionContext({ + input: { + repoUrl: + 'gerrit.com?owner=owner&workspace=parent&project=project&repo=repo', + description, + }, + }); + const server = setupServer(); + registerMswTestHooks(server); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it(`should ${examples[0].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['master'], + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[0].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'master', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: {}, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/master', + ); + }); + + it(`should ${examples[1].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['master'], + create_empty_commit: false, + owners: ['owner'], + description: yaml.parse(examples[1].example).steps[0].input + .description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[1].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'master', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: {}, + }); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/master', + ); + }); + + it(`should ${examples[2].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['staging'], + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[2].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'staging', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: {}, + }); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/staging', + ); + }); + + it(`should ${examples[3].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['master'], + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[3].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'master', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining( + 'Initial Commit Message\n\nChange-Id:', + ), + gitAuthorInfo: {}, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/master', + ); + }); + + it(`should ${examples[4].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['master'], + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[4].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'master', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: { name: 'John Doe' }, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/master', + ); + }); + + it(`should ${examples[5].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['master'], + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[5].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'master', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: { email: 'johndoe@email.com' }, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/master', + ); + }); + + it(`should ${examples[6].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['master'], + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[6].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: `${mockContext.workspacePath}${path.sep}repository${path.sep}`, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'master', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: {}, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/master', + ); + }); + + it(`should ${examples[7].description}`, async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrit.com/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic ZGVtb3VzZXI6YWNjZXNzdG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['staging'], + create_empty_commit: false, + owners: ['owner'], + description: 'Initialize a gerrit repository', + parent: 'parent', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + const suffixPath = '&workspace=parent&project=test-project'; + let input; + try { + input = yaml.parse(examples[7].example).steps[0].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...input, + repoUrl: input.repoUrl + suffixPath, + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: `${mockContext.workspacePath}${path.sep}repository${path.sep}`, + remoteUrl: 'https://gerrit.com/a/repo', + defaultBranch: 'staging', + auth: { username: 'demouser', password: 'accesstoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining( + 'Initial Commit Message\n\nChange-Id:', + ), + gitAuthorInfo: { name: 'John Doe', email: 'johndoe@email.com' }, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrit.com/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrit.com/gitiles/repo/+/refs/heads/staging', + ); + }); + + it('should not create new projects on dryRun', async () => { + await action.handler({ + ...mockContext, + isDryRun: true, + input: { + ...mockContext.input, + repoUrl: 'gerrit.com?workspace=workspace&repo=repo', + sourcePath: 'repository/', + }, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'commitHash', + 'abcd-dry-run-1234', + ); + }); + + it('should fail if repoUrl is incorrect', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'check.com?workspace=w&owner=o', description }, + }), + ).rejects.toThrow( + /No matching integration configuration for host check.com, please check your integrations config/, + ); + }); + + it('should fail if no integration config is given', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + repoUrl: 'missing.com?workspace=w&owner=o&repo=r', + description, + }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); +}); diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.ts index 6729b3602f..261465b3a0 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.examples.ts @@ -156,4 +156,22 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: + 'Initialize a Gerrit Repository with Custom Default Branch and Commit Message', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gerrit', + name: 'Publish to Gerrit', + input: { + repoUrl: 'gerrit.com?repo=repo&owner=owner', + defaultBranch: 'feature-branch', + gitCommitMessage: 'Feature branch initialized', + }, + }, + ], + }), + }, ]; diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index dd0f55ce6c..117b3ea78e 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.1.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/api-report.md b/plugins/scaffolder-backend-module-gitea/api-report.md index 1dca414fa9..6d021bdf12 100644 --- a/plugins/scaffolder-backend-module-gitea/api-report.md +++ b/plugins/scaffolder-backend-module-gitea/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -28,6 +28,6 @@ export function createPublishGiteaAction(options: { >; // @public -const giteaModule: BackendFeatureCompat; +const giteaModule: BackendFeature; export default giteaModule; ``` diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 51055aae54..279a596d79 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", - "version": "0.1.13", + "version": "0.2.0-next.0", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 3c96c7b73d..cb52e9e482 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.4.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/api-report.md b/plugins/scaffolder-backend-module-github/api-report.md index 4ec5795dd9..1fec7f0422 100644 --- a/plugins/scaffolder-backend-module-github/api-report.md +++ b/plugins/scaffolder-backend-module-github/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { createPullRequest } from 'octokit-plugin-create-pull-request'; import { GithubCredentialsProvider } from '@backstage/integration'; @@ -424,6 +424,6 @@ export function getOctokitOptions(options: { }): Promise; // @public -const githubModule: BackendFeatureCompat; +const githubModule: BackendFeature; export default githubModule; ``` diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index e8dd77bd5f..ab4f129594 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.4.1", + "version": "0.5.0-next.0", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 4c9aa96b19..cbe0e36df6 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + ## 0.4.5 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/api-report.md b/plugins/scaffolder-backend-module-gitlab/api-report.md index ab89669ede..49bda648f6 100644 --- a/plugins/scaffolder-backend-module-gitlab/api-report.md +++ b/plugins/scaffolder-backend-module-gitlab/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -255,7 +255,7 @@ export const editGitlabIssueAction: (options: { >; // @public -const gitlabModule: BackendFeatureCompat; +const gitlabModule: BackendFeature; export default gitlabModule; // @public diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 8db4f1ade5..51d7903cb1 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitlab", - "version": "0.4.5", + "version": "0.5.0-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabMergeRequest.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabMergeRequest.test.ts index 3eda50a924..46e60584c9 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabMergeRequest.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabMergeRequest.test.ts @@ -408,7 +408,7 @@ describe('createGitLabMergeRequest', () => { 'owner/repo', 'new-mr', 'Create my new MR', - [ + expect.arrayContaining([ { action: 'create', filePath: 'irrelevant/bar.txt', @@ -423,7 +423,7 @@ describe('createGitLabMergeRequest', () => { content: 'SGVsbG8gdGhlcmUh', execute_filemode: false, }, - ], + ]), ); }); }); @@ -450,7 +450,7 @@ describe('createGitLabMergeRequest', () => { 'owner/repo', 'new-mr', 'Create my new MR', - [ + expect.arrayContaining([ { action: 'update', filePath: 'source/auto.txt', @@ -465,7 +465,7 @@ describe('createGitLabMergeRequest', () => { encoding: 'base64', execute_filemode: false, }, - ], + ]), ); }); }); @@ -562,7 +562,7 @@ describe('createGitLabMergeRequest', () => { 'owner/repo', 'new-mr', 'Create my new MR', - [ + expect.arrayContaining([ { action: 'update', filePath: 'source/auto.txt', @@ -577,7 +577,7 @@ describe('createGitLabMergeRequest', () => { encoding: 'base64', execute_filemode: false, }, - ], + ]), ); }); @@ -604,7 +604,7 @@ describe('createGitLabMergeRequest', () => { 'owner/repo', 'new-mr', 'Create my new MR', - [ + expect.arrayContaining([ { action: 'update', filePath: 'source/auto.txt', @@ -619,7 +619,7 @@ describe('createGitLabMergeRequest', () => { encoding: 'base64', execute_filemode: false, }, - ], + ]), ); }); diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index c6feaf8741..c6361b7e69 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/plugin-notifications-common@0.0.5 + ## 0.0.6 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/api-report.md b/plugins/scaffolder-backend-module-notifications/api-report.md index 4befadf403..091782449f 100644 --- a/plugins/scaffolder-backend-module-notifications/api-report.md +++ b/plugins/scaffolder-backend-module-notifications/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import { NotificationService } from '@backstage/plugin-notifications-node'; import { NotificationSeverity } from '@backstage/plugin-notifications-common'; @@ -27,6 +27,6 @@ export function createSendNotificationAction(options: { >; // @public -const scaffolderModuleNotifications: BackendFeatureCompat; +const scaffolderModuleNotifications: BackendFeature; export default scaffolderModuleNotifications; ``` diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index 58c70478f2..8ec318432f 100644 --- a/plugins/scaffolder-backend-module-notifications/package.json +++ b/plugins/scaffolder-backend-module-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-notifications", - "version": "0.0.6", + "version": "0.1.0-next.0", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 1cb678999e..ddb4825d82 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + ## 0.4.40 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/api-report.md b/plugins/scaffolder-backend-module-rails/api-report.md index 31e8f6834c..07b3713b15 100644 --- a/plugins/scaffolder-backend-module-rails/api-report.md +++ b/plugins/scaffolder-backend-module-rails/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ContainerRunner } from '@backstage/backend-common'; import { JsonObject } from '@backstage/types'; import { ScmIntegrations } from '@backstage/integration'; @@ -27,6 +27,6 @@ export function createFetchRailsAction(options: { >; // @public -const railsModule: BackendFeatureCompat; +const railsModule: BackendFeature; export default railsModule; ``` diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index f92cdb2c4f..64241de844 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.4.40", + "version": "0.5.0-next.0", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 3a1c970c87..17979b55b1 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.2.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.1.31 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/api-report.md b/plugins/scaffolder-backend-module-sentry/api-report.md index 69d164a2f3..a857d10fdd 100644 --- a/plugins/scaffolder-backend-module-sentry/api-report.md +++ b/plugins/scaffolder-backend-module-sentry/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; @@ -23,7 +23,7 @@ export function createSentryCreateProjectAction(options: { >; // @public -const sentryModule: BackendFeatureCompat; +const sentryModule: BackendFeature; export default sentryModule; // (No @packageDocumentation comment for this package) diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 9c2f40628e..4f9da15018 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-sentry", - "version": "0.1.31", + "version": "0.2.0-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 97fdd27e4a..0924d440b5 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.4.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.12-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/api-report.md b/plugins/scaffolder-backend-module-yeoman/api-report.md index 278c9b41ae..9980dc2152 100644 --- a/plugins/scaffolder-backend-module-yeoman/api-report.md +++ b/plugins/scaffolder-backend-module-yeoman/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; @@ -18,6 +18,6 @@ export function createRunYeomanAction(): TemplateAction< >; // @public -const yeomanModule: BackendFeatureCompat; +const yeomanModule: BackendFeature; export default yeomanModule; ``` diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 2599de5921..910af87192 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.3.7", + "version": "0.4.0-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index afa0291723..a780a9421e 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-scaffolder-backend +## 1.25.0-next.0 + +### Minor Changes + +- 62898bd: `createRouter` and its related types has been marked as deprecared. This backend should instead be initialized using the new backend system. + +### Patch Changes + +- c160951: Found the issue during testing the clean up of the workspace for the database implementation. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.0-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.0 + - @backstage/plugin-auth-node@0.5.2-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 1.24.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/api-report-alpha.md b/plugins/scaffolder-backend/api-report-alpha.md index 5bb6cb1701..db62c6caf7 100644 --- a/plugins/scaffolder-backend/api-report-alpha.md +++ b/plugins/scaffolder-backend/api-report-alpha.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ConditionalPolicyDecision } from '@backstage/plugin-permission-common'; import { Conditions } from '@backstage/plugin-permission-node'; import { JsonObject } from '@backstage/types'; @@ -78,7 +78,7 @@ export const scaffolderActionConditions: Conditions<{ }>; // @alpha -const scaffolderPlugin: BackendFeatureCompat; +const scaffolderPlugin: BackendFeature; export default scaffolderPlugin; // @alpha diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 6f6063a252..00f690c646 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -15,6 +15,7 @@ import * as bitbucketCloud from '@backstage/plugin-scaffolder-backend-module-bit import * as bitbucketServer from '@backstage/plugin-scaffolder-backend-module-bitbucket-server'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; +import { DatabaseService } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import { Duration } from 'luxon'; import { executeShellCommand as executeShellCommand_2 } from '@backstage/plugin-scaffolder-node'; @@ -320,7 +321,7 @@ export const createPublishGitlabMergeRequestAction: (options: { JsonObject >; -// @public +// @public @deprecated export function createRouter(options: RouterOptions): Promise; // @public @deprecated (undocumented) @@ -469,7 +470,7 @@ export const executeShellCommand: typeof executeShellCommand_2; // @public @deprecated export const fetchContents: typeof fetchContents_2; -// @public +// @public @deprecated export interface RouterOptions { // (undocumented) actions?: TemplateAction_2[]; @@ -489,7 +490,7 @@ export interface RouterOptions { // (undocumented) config: Config; // (undocumented) - database: PluginDatabaseManager; + database: DatabaseService; // (undocumented) discovery?: DiscoveryService; // (undocumented) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 25b717b6ca..783f1b7496 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "1.24.0", + "version": "1.25.0-next.0", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", @@ -124,7 +124,7 @@ "@types/nunjucks": "^3.1.4", "@types/supertest": "^2.0.8", "@types/zen-observable": "^0.8.0", - "esbuild": "^0.21.0", + "esbuild": "^0.23.0", "strip-ansi": "^7.1.0", "supertest": "^6.1.3", "wait-for-expect": "^3.0.2" diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index ba675b2cc7..23fe793432 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -532,8 +532,8 @@ export class DatabaseTaskStore implements TaskStore { } async cleanWorkspace({ taskId }: { taskId: string }): Promise { - await this.db('tasks').where({ id: taskId }).update({ - workspace: undefined, + await this.db('tasks').where({ id: taskId }).update({ + workspace: null, }); } @@ -542,10 +542,11 @@ export class DatabaseTaskStore implements TaskStore { taskId: string; }): Promise { if (options.path) { + const workspace = (await serializeWorkspace(options)).contents; await this.db('tasks') .where({ id: options.taskId }) .update({ - workspace: (await serializeWorkspace(options)).contents, + workspace, }); } } diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseWorkspaceProvider.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseWorkspaceProvider.ts index 69f90b3d7e..b3c3c24975 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseWorkspaceProvider.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseWorkspaceProvider.ts @@ -29,7 +29,7 @@ export class DatabaseWorkspaceProvider implements WorkspaceProvider { path: string; taskId: string; }): Promise { - this.storage.serializeWorkspace?.(options); + await this.storage.serializeWorkspace?.(options); } public async rehydrateWorkspace(options: { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 3d7d8b8b1d..09fc162ea6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -509,11 +509,11 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const output = this.render(task.spec.output, context, renderTemplate); await taskTrack.markSuccessful(); + await task.cleanWorkspace?.(); return { output }; } finally { if (workspacePath) { - await task.cleanWorkspace?.(); await fs.remove(workspacePath); } } diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 51e11d630f..9673d19255 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -44,9 +44,12 @@ import { AuthorizeResult, PermissionEvaluator, } from '@backstage/plugin-permission-common'; -import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; +import { + mockCredentials, + mockErrorHandler, + mockServices, +} from '@backstage/backend-test-utils'; import { AutocompleteHandler } from '@backstage/plugin-scaffolder-node/alpha'; -import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter'; import { UrlReaders } from '@backstage/backend-defaults/urlReader'; const mockAccess = jest.fn(); @@ -1501,8 +1504,6 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ results: [{ title: 'blob' }], }); - const logger = mockServices.logger.mock(); - const middleware = MiddlewareFactory.create({ config, logger }); const router = await createRouter({ logger: loggerToWinstonLogger(mockServices.logger.mock()), config: new ConfigReader({}), @@ -1519,7 +1520,7 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ }, }); - app = express().use(router).use(middleware.error()); + app = express().use(router).use(mockErrorHandler()); }); it('should throw an error when the provider is not registered', async () => { diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 4f310e1846..3eec4e4450 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -17,7 +17,6 @@ import { createLegacyAuthAdapters, HostDiscovery, - PluginDatabaseManager, } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; import { @@ -88,6 +87,7 @@ import { PermissionsService, UrlReaderService, SchedulerService, + DatabaseService, } from '@backstage/backend-plugin-api'; import { IdentityApi, @@ -140,13 +140,14 @@ function isActionPermissionRuleInput( * RouterOptions * * @public + * @deprecated Please migrate to the new backend system as this will be removed in the future. */ export interface RouterOptions { logger: Logger; config: Config; reader: UrlReaderService; lifecycle?: LifecycleService; - database: PluginDatabaseManager; + database: DatabaseService; catalogClient: CatalogApi; scheduler?: SchedulerService; actions?: TemplateAction[]; @@ -258,6 +259,7 @@ const readDuration = ( /** * A method to create a router for the scaffolder backend plugin. * @public + * @deprecated Please migrate to the new backend system as this will be removed in the future. */ export async function createRouter( options: RouterOptions, diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index dd4ed1e2a1..a376071bdb 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.0 + - @backstage/backend-test-utils@0.6.0-next.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.0 + - @backstage/types@1.1.1 + ## 0.1.10 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 9675a689ab..b9102e03fc 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.1.10", + "version": "0.1.12-next.0", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index d04d7f16e0..aa235c19ec 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-node +## 0.4.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 0.4.9 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 52964f814a..32c3f042a9 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.4.9", + "version": "0.4.11-next.0", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 57799ab836..501c8417ff 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-scaffolder-react +## 1.12.0-next.0 + +### Minor Changes + +- 4512f71: Add `ui:backstage.review.name` option for custom item names on scaffolder review page, and also add support for rendering the `title` property instead of the key name. + +### Patch Changes + +- 3ebb64f: - Fix secret widget field not displaying as required. + - Fix secret widget not able to be required inside nested objects. + - Fix secret widget not able to be disabled. + - Support `minLength` and `maxLength` properties for secret widget. +- 8dd6ef6: Fix an issue where keys with duplicate final key parts are not all displayed in the `ReviewState`. Change the way the keys are formatted to include the full schema path, separated by `>`. +- 9a0672a: Scaffolder review page shows static amount of asterisks for secret fields. +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 1.11.0 ### Minor Changes diff --git a/plugins/scaffolder-react/api-report-alpha.md b/plugins/scaffolder-react/api-report-alpha.md index 4655cbfd62..545cc02e9e 100644 --- a/plugins/scaffolder-react/api-report-alpha.md +++ b/plugins/scaffolder-react/api-report-alpha.md @@ -152,7 +152,10 @@ export type ScaffolderReactTemplateCategoryPickerClassKey = 'root' | 'label'; // @alpha export const SecretWidget: ( - props: Pick, + props: Pick< + WidgetProps, + 'name' | 'onChange' | 'schema' | 'required' | 'disabled' + >, ) => React_2.JSX.Element; // @alpha diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 97e04c8d42..42e4a23488 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.11.0", + "version": "1.12.0-next.0", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx index 194c5f3e77..a3fcecbe2b 100644 --- a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx +++ b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx @@ -170,7 +170,8 @@ describe('ReviewState', () => { ); expect(getByRole('row', { name: 'Name lols' })).toBeInTheDocument(); - expect(getByRole('row', { name: 'Foo lols' })).toBeInTheDocument(); + expect(getByRole('row', { name: 'Test bob' })).toBeInTheDocument(); + expect(getByRole('row', { name: 'Nest > Foo lols' })).toBeInTheDocument(); }); it('should display enum label from enumNames', async () => { @@ -219,7 +220,9 @@ describe('ReviewState', () => { expect( queryByRole('row', { name: 'Name Label-type2' }), ).toBeInTheDocument(); - expect(queryByRole('row', { name: 'Foo Label-type2' })).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Nest > Foo Label-type2' }), + ).toBeInTheDocument(); }); it('should display enum value if no corresponding enumNames', async () => { @@ -292,8 +295,12 @@ describe('ReviewState', () => { , ); - expect(queryByRole('row', { name: 'Foo type3' })).toBeInTheDocument(); - expect(queryByRole('row', { name: 'Bar type4' })).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Foo type3' }), + ).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Bar type4' }), + ).toBeInTheDocument(); }); it('should display nested objects in separate rows', async () => { @@ -303,6 +310,7 @@ describe('ReviewState', () => { bar: 'type4', example: { test: 'type6', + foo: 'type7', }, }, }; @@ -329,6 +337,9 @@ describe('ReviewState', () => { test: { type: 'string', }, + foo: { + type: 'string', + }, }, }, }, @@ -345,9 +356,18 @@ describe('ReviewState', () => { , ); - expect(queryByRole('row', { name: 'Foo type3' })).toBeInTheDocument(); - expect(queryByRole('row', { name: 'Bar type4' })).toBeInTheDocument(); - expect(queryByRole('row', { name: 'Test type6' })).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Foo type3' }), + ).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Bar type4' }), + ).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Example > Test type6' }), + ).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Example > Foo type7' }), + ).toBeInTheDocument(); }); it('should display partially nested objects', async () => { @@ -404,8 +424,77 @@ describe('ReviewState', () => { , ); - expect(queryByRole('row', { name: 'Foo type3' })).toBeInTheDocument(); - expect(queryByRole('row', { name: 'Bar type4' })).toBeInTheDocument(); - expect(queryByRole('row', { name: 'Test type6' })).not.toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Foo type3' }), + ).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Bar type4' }), + ).toBeInTheDocument(); + expect( + queryByRole('row', { name: 'Name > Example > Test type6' }), + ).not.toBeInTheDocument(); + }); + + it('should allow using the title property', async () => { + const formState = { + foo: 'test', + }; + + const schemas: ParsedTemplateSchema[] = [ + { + mergedSchema: { + type: 'object', + properties: { + foo: { + type: 'string', + title: 'Test Thing', + }, + }, + }, + schema: {}, + title: 'test', + uiSchema: {}, + }, + ]; + + const { queryByRole } = render( + , + ); + + expect(queryByRole('row', { name: 'Test Thing test' })).toBeInTheDocument(); + }); + + it('should allow custom review name', async () => { + const formState = { + foo: 'test', + }; + + const schemas: ParsedTemplateSchema[] = [ + { + mergedSchema: { + type: 'object', + properties: { + foo: { + type: 'string', + 'ui:backstage': { + review: { + name: 'bar', + }, + }, + }, + }, + }, + schema: {}, + title: 'test', + uiSchema: {}, + }, + ]; + + const { queryByRole } = render( + , + ); + + expect(queryByRole('row', { name: 'Bar test' })).toBeInTheDocument(); + expect(queryByRole('row', { name: 'Foo test' })).not.toBeInTheDocument(); }); }); diff --git a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx index d87584afff..7d2c6042dc 100644 --- a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx +++ b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx @@ -18,7 +18,7 @@ import { StructuredMetadataTable } from '@backstage/core-components'; import { JsonObject, JsonValue } from '@backstage/types'; import { Draft07 as JSONSchema } from 'json-schema-library'; import { ParsedTemplateSchema } from '../../hooks/useTemplateSchema'; -import { isJsonObject, getLastKey } from './util'; +import { isJsonObject, formatKey } from './util'; /** * The props for the {@link ReviewState} component. @@ -41,25 +41,33 @@ function processSchema( data: formState, }); + const name = + definitionInSchema?.['ui:backstage']?.review?.name ?? + definitionInSchema?.title ?? + key; + if (definitionInSchema) { const backstageReviewOptions = definitionInSchema['ui:backstage']?.review; if (backstageReviewOptions) { if (backstageReviewOptions.mask) { - return [[getLastKey(key), backstageReviewOptions.mask]]; + return [[name, backstageReviewOptions.mask]]; } if (backstageReviewOptions.show === false) { return []; } } - if (definitionInSchema['ui:widget'] === 'password') { - return [[getLastKey(key), '******']]; + if ( + definitionInSchema['ui:widget'] === 'password' || + definitionInSchema['ui:field']?.toLocaleLowerCase('en-us') === 'secret' + ) { + return [[name, '******']]; } if (definitionInSchema.enum && definitionInSchema.enumNames) { return [ [ - getLastKey(key), + name, definitionInSchema.enumNames[ definitionInSchema.enum.indexOf(value) ] || value, @@ -75,7 +83,7 @@ function processSchema( } } - return [[getLastKey(key), value]]; + return [[name, value]]; } /** @@ -93,5 +101,8 @@ export const ReviewState = (props: ReviewStateProps) => { }) .filter(prop => prop.length > 0), ); - return ; + const options = { + titleFormat: formatKey, + }; + return ; }; diff --git a/plugins/scaffolder-react/src/next/components/ReviewState/util.test.ts b/plugins/scaffolder-react/src/next/components/ReviewState/util.test.ts index 649d0c3160..bc8001baea 100644 --- a/plugins/scaffolder-react/src/next/components/ReviewState/util.test.ts +++ b/plugins/scaffolder-react/src/next/components/ReviewState/util.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { isJsonObject, getLastKey } from './util'; +import { isJsonObject, formatKey } from './util'; describe('isJsonObject', () => { it('should return true for non-null objects', () => { @@ -39,40 +39,42 @@ describe('isJsonObject', () => { }); }); -describe('getLastKey', () => { - it('should return the last part of a simple key', () => { - expect(getLastKey('simple')).toBe('simple'); +describe('formatKey', () => { + it('should replace / with > globally in the key', () => { + expect(formatKey('simple/key')).toBe('Simple > Key'); }); - it('should return the last part of a nested key', () => { - expect(getLastKey('parent/child')).toBe('child'); + it('should leave a top-level key untouched', () => { + expect(formatKey('topLevel')).toBe('Top Level'); }); - it('should return the last part of a deeply nested key', () => { - expect(getLastKey('grandparent/parent/child')).toBe('child'); + it('should handle keys with a leading slash', () => { + expect(formatKey('/simple/key')).toBe('Simple > Key'); }); it('should handle keys with trailing slash', () => { - expect(getLastKey('parent/child/')).toBe(''); + expect(formatKey('parent/child/')).toBe('Parent > Child'); }); it('should handle empty string', () => { - expect(getLastKey('')).toBe(''); + expect(formatKey('')).toBe(''); }); it('should handle keys with multiple consecutive slashes', () => { - expect(getLastKey('parent//child')).toBe('child'); + expect(formatKey('parent//child')).toBe('Parent > Child'); }); it('should handle keys with only slashes', () => { - expect(getLastKey('////')).toBe(''); + expect(formatKey('////')).toBe(''); }); it('should handle keys with spaces', () => { - expect(getLastKey('parent/child with spaces')).toBe('child with spaces'); + expect(formatKey('parent/child with spaces')).toBe( + 'Parent > Child With Spaces', + ); }); - it('should handle keys with special characters', () => { - expect(getLastKey('parent/child@!#$%^&*()')).toBe('child@!#$%^&*()'); + it('should remove special characters', () => { + expect(formatKey('parent/child@!#$%^&*()')).toBe('Parent > Child'); }); }); diff --git a/plugins/scaffolder-react/src/next/components/ReviewState/util.ts b/plugins/scaffolder-react/src/next/components/ReviewState/util.ts index d2caf71964..44311ee206 100644 --- a/plugins/scaffolder-react/src/next/components/ReviewState/util.ts +++ b/plugins/scaffolder-react/src/next/components/ReviewState/util.ts @@ -15,13 +15,17 @@ */ import { JsonObject, JsonValue } from '@backstage/types'; +import { startCase } from 'lodash'; export function isJsonObject(value?: JsonValue): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); } -// Helper function to get the last part of the key -export function getLastKey(key: string): string { +// Helper function to format a key into a human-readable string +export function formatKey(key: string): string { const parts = key.split('/'); - return parts[parts.length - 1]; + return parts + .filter(Boolean) + .map(part => startCase(part)) + .join(' > '); } diff --git a/plugins/scaffolder-react/src/next/components/SecretWidget/SecretWidget.tsx b/plugins/scaffolder-react/src/next/components/SecretWidget/SecretWidget.tsx index 0b8ca8da3b..6b9094c199 100644 --- a/plugins/scaffolder-react/src/next/components/SecretWidget/SecretWidget.tsx +++ b/plugins/scaffolder-react/src/next/components/SecretWidget/SecretWidget.tsx @@ -24,13 +24,18 @@ import React from 'react'; * @alpha */ export const SecretWidget = ( - props: Pick, + props: Pick< + WidgetProps, + 'name' | 'onChange' | 'schema' | 'required' | 'disabled' + >, ) => { const { setSecrets, secrets } = useTemplateSecrets(); const { name, onChange, - schema: { title }, + schema: { title, minLength, maxLength }, + required, + disabled, } = props; return ( @@ -45,6 +50,12 @@ export const SecretWidget = ( value={secrets[name] ?? ''} type="password" autoComplete="off" + required={required} + disabled={disabled} + inputProps={{ + minLength, + maxLength, + }} /> ); }; diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 409ae4734f..8f2aaf80bc 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-scaffolder +## 1.25.0-next.0 + +### Minor Changes + +- 5143616: Added EntityOwnerPicker component to the TemplateListPage to allow filtering on owner + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 1.24.0 ### Minor Changes diff --git a/plugins/scaffolder/api-report-alpha.md b/plugins/scaffolder/api-report-alpha.md index 007b106665..555ab2ac13 100644 --- a/plugins/scaffolder/api-report-alpha.md +++ b/plugins/scaffolder/api-report-alpha.md @@ -5,13 +5,13 @@ ```ts import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react'; import type { FormProps as FormProps_2 } from '@rjsf/core'; import { FormProps as FormProps_3 } from '@backstage/plugin-scaffolder-react'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { LayoutOptions } from '@backstage/plugin-scaffolder-react'; import { PathParams } from '@backstage/core-plugin-api'; @@ -24,7 +24,7 @@ import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react'; import { TranslationRef } from '@backstage/core-plugin-api/alpha'; // @alpha (undocumented) -const _default: BackstagePlugin< +const _default: FrontendPlugin< { root: RouteRef; selectedTemplate: SubRouteRef< @@ -44,48 +44,52 @@ const _default: BackstagePlugin< }>; }, { - 'api:scaffolder': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef, - {}, - { - kind: 'api'; - namespace: undefined; - name: undefined; - } - >; - 'page:scaffolder': ExtensionDefinition< - { + 'api:scaffolder': ExtensionDefinition<{ + kind: 'api'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + }>; + 'page:scaffolder': ExtensionDefinition<{ + kind: 'page'; + namespace: undefined; + name: undefined; + config: { path: string | undefined; - }, - { + }; + configInput: { path?: string | undefined; - }, - | ConfigurableExtensionDataRef< - React_2.JSX.Element, - 'core.reactElement', - {} - > - | ConfigurableExtensionDataRef - | ConfigurableExtensionDataRef< - RouteRef, - 'core.routing.ref', - { - optional: true; - } - >, - {}, - { - kind: 'page'; - namespace: undefined; - name: undefined; - } - >; - 'nav-item:scaffolder': ExtensionDefinition< - {}, - {}, - ConfigurableExtensionDataRef< + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + }>; + 'nav-item:scaffolder': ExtensionDefinition<{ + kind: 'nav-item'; + namespace: undefined; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< { title: string; icon: IconComponent; @@ -93,14 +97,9 @@ const _default: BackstagePlugin< }, 'core.nav-item.target', {} - >, - {}, - { - kind: 'nav-item'; - namespace: undefined; - name: undefined; - } - >; + >; + inputs: {}; + }>; } >; export default _default; diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 48fe404d31..5b558ee232 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.24.0", + "version": "1.25.0-next.0", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx index ee36d876cf..2ad0752cc8 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.test.tsx @@ -45,6 +45,9 @@ describe('TemplateListPage', () => { getEntityFacets: async () => ({ facets: { 'spec.type': [{ value: 'service', count: 1 }] }, }), + getEntitiesByRefs: async () => ({ + items: [], + }), }; it('should render the search bar for templates', async () => { @@ -114,6 +117,28 @@ describe('TemplateListPage', () => { expect(getByText('Categories')).toBeInTheDocument(); }); + it('should render the EntityOwnerPicker', async () => { + const { getByText } = await renderInTestApp( + + + , + { mountedRoutes: { '/': rootRouteRef } }, + ); + + expect(getByText('Owner')).toBeInTheDocument(); + }); + // eslint-disable-next-line jest/no-disabled-tests it.skip('should render the EntityTag picker', async () => { const { getByText } = await renderInTestApp( diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx index 400bc9d192..4042506d1e 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx @@ -34,6 +34,7 @@ import { EntityTagPicker, CatalogFilterLayout, UserListPicker, + EntityOwnerPicker, } from '@backstage/plugin-catalog-react'; import { ScaffolderPageContextMenu, @@ -198,6 +199,7 @@ export const TemplateListPage = (props: TemplateListPageProps) => { /> + Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogCollatorEntityTransformer } from '@backstage/plugin-search-backend-module-catalog'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; @@ -16,7 +16,7 @@ export type CatalogCollatorExtensionPoint = { export const catalogCollatorExtensionPoint: ExtensionPoint; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // (No @packageDocumentation comment for this package) diff --git a/plugins/search-backend-module-catalog/api-report.md b/plugins/search-backend-module-catalog/api-report.md index 8019ab0aac..aed73254cc 100644 --- a/plugins/search-backend-module-catalog/api-report.md +++ b/plugins/search-backend-module-catalog/api-report.md @@ -9,11 +9,11 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; import { Config } from '@backstage/config'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { Entity } from '@backstage/catalog-model'; import { GetEntitiesRequest } from '@backstage/catalog-client'; import { Permission } from '@backstage/plugin-permission-common'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { Readable } from 'stream'; import { TokenManager } from '@backstage/backend-common'; @@ -25,7 +25,7 @@ export type CatalogCollatorEntityTransformer = ( // @public (undocumented) export const defaultCatalogCollatorEntityTransformer: CatalogCollatorEntityTransformer; -// @public +// @public @deprecated export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { // (undocumented) static fromConfig( @@ -40,10 +40,10 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { readonly visibilityPermission: Permission; } -// @public (undocumented) +// @public @deprecated (undocumented) export type DefaultCatalogCollatorFactoryOptions = { auth?: AuthService; - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; tokenManager?: TokenManager; locationTemplate?: string; filter?: GetEntitiesRequest['filter']; diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index fadfc99535..ea216c5033 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.2.0", + "version": "0.2.2-next.0", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-catalog/src/alpha.ts b/plugins/search-backend-module-catalog/src/alpha.ts index 2ae9cd8af9..03e5bcb226 100644 --- a/plugins/search-backend-module-catalog/src/alpha.ts +++ b/plugins/search-backend-module-catalog/src/alpha.ts @@ -80,7 +80,6 @@ export default createBackendModule({ auth: coreServices.auth, config: coreServices.rootConfig, discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, scheduler: coreServices.scheduler, indexRegistry: searchIndexRegistryExtensionPoint, catalog: catalogServiceRef, @@ -89,7 +88,6 @@ export default createBackendModule({ auth, config, discovery, - tokenManager, scheduler, indexRegistry, catalog, @@ -102,7 +100,6 @@ export default createBackendModule({ auth, entityTransformer, discovery, - tokenManager, catalogClient: catalog, }), }); diff --git a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts index 4bdb8a5ae9..32a6f8b701 100644 --- a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts +++ b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -import { - PluginEndpointDiscovery, - TokenManager, -} from '@backstage/backend-common'; import { registerMswTestHooks } from '@backstage/backend-test-utils'; import { Entity } from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; @@ -26,6 +22,7 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { Readable } from 'stream'; import { DefaultCatalogCollatorFactory } from './DefaultCatalogCollatorFactory'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; const server = setupServer(); @@ -61,17 +58,13 @@ const expectedEntities: Entity[] = [ describe('DefaultCatalogCollatorFactory', () => { const config = new ConfigReader({}); - const mockDiscoveryApi: jest.Mocked = { + const mockDiscoveryApi: jest.Mocked = { getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007'), getExternalBaseUrl: jest.fn(), }; - const mockTokenManager: jest.Mocked = { - getToken: jest.fn().mockResolvedValue({ token: '' }), - authenticate: jest.fn(), - }; + const options = { discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, }; registerMswTestHooks(server); @@ -209,7 +202,6 @@ describe('DefaultCatalogCollatorFactory', () => { // Provide an alternate location template. factory = DefaultCatalogCollatorFactory.fromConfig(new ConfigReader({}), { discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, locationTemplate: '/software/:name', }); collator = await factory.getCollator(); @@ -225,7 +217,6 @@ describe('DefaultCatalogCollatorFactory', () => { // Provide a custom filter. factory = DefaultCatalogCollatorFactory.fromConfig(new ConfigReader({}), { discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, filter: { kind: ['Foo', 'Bar'], }, diff --git a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts index 22dcc67233..11d72c156b 100644 --- a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts +++ b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts @@ -15,7 +15,6 @@ */ import { - PluginEndpointDiscovery, TokenManager, createLegacyAuthAdapters, } from '@backstage/backend-common'; @@ -34,12 +33,15 @@ import { Readable } from 'stream'; import { CatalogCollatorEntityTransformer } from './CatalogCollatorEntityTransformer'; import { readCollatorConfigOptions } from './config'; import { defaultCatalogCollatorEntityTransformer } from './defaultCatalogCollatorEntityTransformer'; -import { AuthService } from '@backstage/backend-plugin-api'; +import { AuthService, DiscoveryService } from '@backstage/backend-plugin-api'; -/** @public */ +/** + * @public + * @deprecated This type is deprecated along with the {@link DefaultCatalogCollatorFactory}. + */ export type DefaultCatalogCollatorFactoryOptions = { auth?: AuthService; - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; tokenManager?: TokenManager; /** * @deprecated Use the config key `search.collators.catalog.locationTemplate` instead. @@ -64,6 +66,7 @@ export type DefaultCatalogCollatorFactoryOptions = { * Collates entities from the Catalog into documents for the search backend. * * @public + * @deprecated Migrate to the {@link https://backstage.io/docs/backend-system/building-backends/migrating | new backend system} and install this collator via module instead (see {@link https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-catalog/README.md#installation | here} for more installation details). */ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { public readonly type = 'software-catalog'; @@ -105,7 +108,7 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { batchSize: number; entityTransformer?: CatalogCollatorEntityTransformer; auth: AuthService; - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; catalogClient?: CatalogApi; }) { const { diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 70cdbc0e93..65cf4bb263 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.5.6-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 5726390: Internal refactor to use `LoggerService` and `DatabaseService` instead of the legacy `Logger` and `PluginDatabaseManager` types. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.14 + ## 1.5.4 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/api-report-alpha.md b/plugins/search-backend-module-elasticsearch/api-report-alpha.md index ca1858f9c8..23a4664f9c 100644 --- a/plugins/search-backend-module-elasticsearch/api-report-alpha.md +++ b/plugins/search-backend-module-elasticsearch/api-report-alpha.md @@ -3,12 +3,12 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ElasticSearchQueryTranslator } from '@backstage/plugin-search-backend-module-elasticsearch'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // @alpha (undocumented) diff --git a/plugins/search-backend-module-elasticsearch/api-report.md b/plugins/search-backend-module-elasticsearch/api-report.md index 4c55dad91e..c565a554ed 100644 --- a/plugins/search-backend-module-elasticsearch/api-report.md +++ b/plugins/search-backend-module-elasticsearch/api-report.md @@ -14,7 +14,6 @@ import { Config } from '@backstage/config'; import type { ConnectionOptions } from 'tls'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { IndexableResultSet } from '@backstage/plugin-search-common'; -import { Logger } from 'winston'; import { LoggerService } from '@backstage/backend-plugin-api'; import { Readable } from 'stream'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; @@ -306,7 +305,7 @@ export interface ElasticSearchNodeOptions { // @public export type ElasticSearchOptions = { - logger: Logger | LoggerService; + logger: LoggerService; config: Config; aliasPostfix?: string; indexPrefix?: string; @@ -330,7 +329,7 @@ export class ElasticSearchSearchEngine implements SearchEngine { elasticSearchClientOptions: ElasticSearchClientOptions, aliasPostfix: string, indexPrefix: string, - logger: Logger | LoggerService, + logger: LoggerService, batchSize: number, highlightOptions?: ElasticSearchHighlightOptions, ); @@ -373,7 +372,7 @@ export type ElasticSearchSearchEngineIndexerOptions = { indexPrefix: string; indexSeparator: string; alias: string; - logger: Logger | LoggerService; + logger: LoggerService; elasticSearchClientWrapper: ElasticSearchClientWrapper; batchSize: number; skipRefresh?: boolean; diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index e166cacb83..969152c536 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", - "version": "1.5.4", + "version": "1.5.6-next.0", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module", @@ -59,8 +59,7 @@ "aws4": "^1.12.0", "elastic-builder": "^2.16.0", "lodash": "^4.17.21", - "uuid": "^9.0.0", - "winston": "^3.2.1" + "uuid": "^9.0.0" }, "devDependencies": { "@backstage/backend-common": "workspace:^", diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index 0832aaea85..e2934da404 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -33,7 +33,6 @@ import { import { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper'; import { ElasticSearchCustomIndexTemplate } from './types'; import { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineIndexer'; -import { Logger } from 'winston'; import { MissingIndexError } from '@backstage/plugin-search-backend-node'; import esb from 'elastic-builder'; import { v4 as uuid } from 'uuid'; @@ -77,7 +76,7 @@ export type ElasticSearchQueryTranslator = ( * @public */ export type ElasticSearchOptions = { - logger: Logger | LoggerService; + logger: LoggerService; config: Config; aliasPostfix?: string; indexPrefix?: string; @@ -131,7 +130,7 @@ export class ElasticSearchSearchEngine implements SearchEngine { private readonly elasticSearchClientOptions: ElasticSearchClientOptions, private readonly aliasPostfix: string, private readonly indexPrefix: string, - private readonly logger: Logger | LoggerService, + private readonly logger: LoggerService, private readonly batchSize: number, highlightOptions?: ElasticSearchHighlightOptions, ) { diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts index 616c1ca349..3af195bd55 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts @@ -17,7 +17,6 @@ import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node'; import { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper'; import { IndexableDocument } from '@backstage/plugin-search-common'; -import { Logger } from 'winston'; import { Readable } from 'stream'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -30,7 +29,7 @@ export type ElasticSearchSearchEngineIndexerOptions = { indexPrefix: string; indexSeparator: string; alias: string; - logger: Logger | LoggerService; + logger: LoggerService; elasticSearchClientWrapper: ElasticSearchClientWrapper; batchSize: number; skipRefresh?: boolean; @@ -56,7 +55,7 @@ export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer { private readonly indexPrefix: string; private readonly indexSeparator: string; private readonly alias: string; - private readonly logger: Logger | LoggerService; + private readonly logger: LoggerService; private readonly sourceStream: Readable; private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper; private configuredBatchSize: number; diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 41062f760d..05c68e9d00 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-search-backend-module-explore +## 0.2.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 5726390: The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module: + + - `DefaultCatalogCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-catalog/README.md#installation; + - `ToolDocumentCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-explore/README.md#installation; + - `DefaultTechDocsCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-techdocs/README.md#installation. + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.14 + ## 0.2.0 ### Minor Changes diff --git a/plugins/search-backend-module-explore/api-report-alpha.md b/plugins/search-backend-module-explore/api-report-alpha.md index 052860b957..3a1eef9393 100644 --- a/plugins/search-backend-module-explore/api-report-alpha.md +++ b/plugins/search-backend-module-explore/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // (No @packageDocumentation comment for this package) diff --git a/plugins/search-backend-module-explore/api-report.md b/plugins/search-backend-module-explore/api-report.md index c881de9fad..79163df05c 100644 --- a/plugins/search-backend-module-explore/api-report.md +++ b/plugins/search-backend-module-explore/api-report.md @@ -7,18 +7,18 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { ExploreTool } from '@backstage-community/plugin-explore-common'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { LoggerService } from '@backstage/backend-plugin-api'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { Readable } from 'stream'; import { TokenManager } from '@backstage/backend-common'; // @public export interface ToolDocument extends IndexableDocument, ExploreTool {} -// @public +// @public @deprecated export class ToolDocumentCollatorFactory implements DocumentCollatorFactory { // (undocumented) execute(): AsyncGenerator; @@ -33,9 +33,9 @@ export class ToolDocumentCollatorFactory implements DocumentCollatorFactory { readonly type: string; } -// @public +// @public @deprecated (undocumented) export type ToolDocumentCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; logger: LoggerService; tokenManager?: TokenManager; auth?: AuthService; diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 1f7611c46d..7aa0b449ae 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-explore", - "version": "0.2.0", + "version": "0.2.2-next.0", "description": "A module for the search backend that exports explore modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-explore/src/alpha.ts b/plugins/search-backend-module-explore/src/alpha.ts index ee4bedde21..63a13c273b 100644 --- a/plugins/search-backend-module-explore/src/alpha.ts +++ b/plugins/search-backend-module-explore/src/alpha.ts @@ -43,7 +43,6 @@ export default createBackendModule({ logger: coreServices.logger, discovery: coreServices.discovery, scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, auth: coreServices.auth, indexRegistry: searchIndexRegistryExtensionPoint, }, @@ -52,7 +51,6 @@ export default createBackendModule({ logger, discovery, scheduler, - tokenManager, auth, indexRegistry, }) { @@ -74,7 +72,6 @@ export default createBackendModule({ discovery, logger, auth, - tokenManager, }), }); }, diff --git a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts index 82f5cffbc0..92f75df91f 100644 --- a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts +++ b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - PluginEndpointDiscovery, - TokenManager, -} from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { TestPipeline } from '@backstage/plugin-search-backend-node'; import { @@ -27,6 +23,7 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { Readable } from 'stream'; import { ToolDocumentCollatorFactory } from './ToolDocumentCollatorFactory'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; const logger = mockServices.logger.mock(); @@ -58,18 +55,14 @@ const mockTools = { describe('ToolDocumentCollatorFactory', () => { const config = new ConfigReader({}); - const mockDiscoveryApi: jest.Mocked = { + const mockDiscoveryApi: jest.Mocked = { getBaseUrl: jest.fn().mockResolvedValue('http://test-backend/api/explore'), getExternalBaseUrl: jest.fn(), }; - const mockTokenManager: jest.Mocked = { - getToken: jest.fn().mockResolvedValue({ token: '' }), - authenticate: jest.fn(), - }; + const options = { discovery: mockDiscoveryApi, logger, - tokenManager: mockTokenManager, }; it('has expected type', () => { diff --git a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.ts b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.ts index 3a2f73c628..da047fe8ab 100644 --- a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.ts +++ b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.ts @@ -16,10 +16,13 @@ import { createLegacyAuthAdapters, - PluginEndpointDiscovery, TokenManager, } from '@backstage/backend-common'; -import { AuthService, LoggerService } from '@backstage/backend-plugin-api'; +import { + AuthService, + DiscoveryService, + LoggerService, +} from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { ExploreTool } from '@backstage-community/plugin-explore-common'; import { @@ -37,12 +40,11 @@ import { Readable } from 'stream'; export interface ToolDocument extends IndexableDocument, ExploreTool {} /** - * The options for the {@link ToolDocumentCollatorFactory}. - * * @public + * @deprecated This type is deprecated along with the {@link ToolDocumentCollatorFactory}. */ export type ToolDocumentCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; logger: LoggerService; tokenManager?: TokenManager; auth?: AuthService; @@ -52,18 +54,18 @@ export type ToolDocumentCollatorFactoryOptions = { * Search collator responsible for collecting explore tools to index. * * @public + * @deprecated Migrate to the {@link https://backstage.io/docs/backend-system/building-backends/migrating | new backend system} and install this collator via module instead (see {@link https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-explore/README.md#installation | here} for more installation details). */ export class ToolDocumentCollatorFactory implements DocumentCollatorFactory { public readonly type: string = 'tools'; - private readonly discovery: PluginEndpointDiscovery; + private readonly discovery: DiscoveryService; private readonly logger: LoggerService; private readonly auth: AuthService; private constructor(options: ToolDocumentCollatorFactoryOptions) { this.discovery = options.discovery; this.logger = options.logger; - this.auth = createLegacyAuthAdapters(options).auth; } diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 6c96b4e32c..cac5550c8a 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.35-next.0 + +### Patch Changes + +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 5726390: Internal refactor to use `LoggerService` and `DatabaseService` instead of the legacy `Logger` and `PluginDatabaseManager` types. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.14 + ## 0.5.33 ### Patch Changes diff --git a/plugins/search-backend-module-pg/api-report-alpha.md b/plugins/search-backend-module-pg/api-report-alpha.md index ea2f6afece..b36b782f6c 100644 --- a/plugins/search-backend-module-pg/api-report-alpha.md +++ b/plugins/search-backend-module-pg/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // (No @packageDocumentation comment for this package) diff --git a/plugins/search-backend-module-pg/api-report.md b/plugins/search-backend-module-pg/api-report.md index 21f2d7737d..f0dea48b9c 100644 --- a/plugins/search-backend-module-pg/api-report.md +++ b/plugins/search-backend-module-pg/api-report.md @@ -5,11 +5,11 @@ ```ts import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node'; import { Config } from '@backstage/config'; +import { DatabaseService } from '@backstage/backend-plugin-api'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { IndexableResultSet } from '@backstage/plugin-search-common'; import { Knex } from 'knex'; -import { Logger } from 'winston'; -import { PluginDatabaseManager } from '@backstage/backend-common'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; import { SearchQuery } from '@backstage/plugin-search-common'; @@ -25,9 +25,7 @@ export class DatabaseDocumentStore implements DatabaseStore { // (undocumented) completeInsert(tx: Knex.Transaction, type: string): Promise; // (undocumented) - static create( - database: PluginDatabaseManager, - ): Promise; + static create(database: DatabaseService): Promise; // (undocumented) getTransaction(): Promise; // (undocumented) @@ -85,12 +83,16 @@ export interface DocumentResultRow { // @public (undocumented) export class PgSearchEngine implements SearchEngine { // @deprecated - constructor(databaseStore: DatabaseStore, config: Config, logger?: Logger); + constructor( + databaseStore: DatabaseStore, + config: Config, + logger?: LoggerService, + ); // @deprecated (undocumented) static from(options: { - database: PluginDatabaseManager; + database: DatabaseService; config: Config; - logger?: Logger; + logger?: LoggerService; }): Promise; // (undocumented) static fromConfig( @@ -104,7 +106,7 @@ export class PgSearchEngine implements SearchEngine { // (undocumented) setTranslator(translator: PgSearchQueryTranslator): void; // (undocumented) - static supported(database: PluginDatabaseManager): Promise; + static supported(database: DatabaseService): Promise; // (undocumented) translator( query: SearchQuery, @@ -128,7 +130,7 @@ export type PgSearchEngineIndexerOptions = { batchSize: number; type: string; databaseStore: DatabaseStore; - logger?: Logger; + logger?: LoggerService; }; // @public @@ -146,8 +148,8 @@ export type PgSearchHighlightOptions = { // @public export type PgSearchOptions = { - database: PluginDatabaseManager; - logger?: Logger; + database: DatabaseService; + logger?: LoggerService; }; // @public (undocumented) diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 6005cf3409..01b20fb454 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.33", + "version": "0.5.35-next.0", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", @@ -56,8 +56,7 @@ "@backstage/plugin-search-common": "workspace:^", "knex": "^3.0.0", "lodash": "^4.17.21", - "uuid": "^9.0.0", - "winston": "^3.2.1" + "uuid": "^9.0.0" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts index b6513a0a97..4ff787071a 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { PluginDatabaseManager } from '@backstage/backend-common'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; import { SearchQuery, @@ -28,8 +27,8 @@ import { PgSearchQuery, } from '../database'; import { v4 as uuid } from 'uuid'; -import { Logger } from 'winston'; import { Config } from '@backstage/config'; +import { DatabaseService, LoggerService } from '@backstage/backend-plugin-api'; /** * Search query that the Postgres search engine understands. @@ -62,8 +61,8 @@ export type PgSearchQueryTranslator = ( * @public */ export type PgSearchOptions = { - database: PluginDatabaseManager; - logger?: Logger; + database: DatabaseService; + logger?: LoggerService; }; /** @@ -84,7 +83,7 @@ export type PgSearchHighlightOptions = { /** @public */ export class PgSearchEngine implements SearchEngine { - private readonly logger?: Logger; + private readonly logger?: LoggerService; private readonly highlightOptions: PgSearchHighlightOptions; private readonly indexerBatchSize: number; @@ -94,7 +93,7 @@ export class PgSearchEngine implements SearchEngine { constructor( private readonly databaseStore: DatabaseStore, config: Config, - logger?: Logger, + logger?: LoggerService, ) { const uuidTag = uuid(); const highlightConfig = config.getOptionalConfig( @@ -121,12 +120,12 @@ export class PgSearchEngine implements SearchEngine { } /** - * @deprecated This will be removed in a future release, please us fromConfig instead + * @deprecated This will be removed in a future release, please use fromConfig instead */ static async from(options: { - database: PluginDatabaseManager; + database: DatabaseService; config: Config; - logger?: Logger; + logger?: LoggerService; }): Promise { return new PgSearchEngine( await DatabaseDocumentStore.create(options.database), @@ -143,7 +142,7 @@ export class PgSearchEngine implements SearchEngine { ); } - static async supported(database: PluginDatabaseManager): Promise { + static async supported(database: DatabaseService): Promise { return await DatabaseDocumentStore.supported(await database.getClient()); } diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts index c460889bd4..2e1997c42c 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts @@ -17,20 +17,20 @@ import { BatchSearchEngineIndexer } from '@backstage/plugin-search-backend-node'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { Knex } from 'knex'; -import { Logger } from 'winston'; import { DatabaseStore } from '../database'; +import { LoggerService } from '@backstage/backend-plugin-api'; /** @public */ export type PgSearchEngineIndexerOptions = { batchSize: number; type: string; databaseStore: DatabaseStore; - logger?: Logger; + logger?: LoggerService; }; /** @public */ export class PgSearchEngineIndexer extends BatchSearchEngineIndexer { - private logger?: Logger; + private logger?: LoggerService; private store: DatabaseStore; private type: string; private tx: Knex.Transaction | undefined; diff --git a/plugins/search-backend-module-pg/src/alpha.ts b/plugins/search-backend-module-pg/src/alpha.ts index 902e8c3ad2..3ee4d685e7 100644 --- a/plugins/search-backend-module-pg/src/alpha.ts +++ b/plugins/search-backend-module-pg/src/alpha.ts @@ -19,7 +19,6 @@ import { } from '@backstage/backend-plugin-api'; import { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; import { PgSearchEngine } from './PgSearchEngine'; -import { loggerToWinstonLogger } from '@backstage/backend-common'; /** * @alpha @@ -41,7 +40,7 @@ export default createBackendModule({ searchEngineRegistry.setSearchEngine( await PgSearchEngine.fromConfig(config, { database, - logger: loggerToWinstonLogger(logger), + logger, }), ); } else { diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts index 88ba7072dd..0487097d9a 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { PluginDatabaseManager } from '@backstage/backend-common'; -import { resolvePackagePath } from '@backstage/backend-plugin-api'; +import { + DatabaseService, + resolvePackagePath, +} from '@backstage/backend-plugin-api'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { Knex } from 'knex'; import { @@ -33,7 +35,7 @@ const migrationsDir = resolvePackagePath( /** @public */ export class DatabaseDocumentStore implements DatabaseStore { static async create( - database: PluginDatabaseManager, + database: DatabaseService, ): Promise { const knex = await database.getClient(); try { diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index 35ff0312ab..92c8c81331 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-search-common@1.2.14 + ## 0.2.0 ### Minor Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/api-report.md b/plugins/search-backend-module-stack-overflow-collator/api-report.md index bf5ebb5408..3b9b79c597 100644 --- a/plugins/search-backend-module-stack-overflow-collator/api-report.md +++ b/plugins/search-backend-module-stack-overflow-collator/api-report.md @@ -5,7 +5,7 @@ ```ts /// -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { IndexableDocument } from '@backstage/plugin-search-common'; @@ -13,7 +13,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { Readable } from 'stream'; // @public -const searchStackOverflowCollatorModule: BackendFeatureCompat; +const searchStackOverflowCollatorModule: BackendFeature; export default searchStackOverflowCollatorModule; // @public diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 36d948c34e..84363f010b 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", - "version": "0.2.0", + "version": "0.3.0-next.0", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index de280174fa..47e94e2937 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.2.2-next.0 + +### Patch Changes + +- 19ff127: Internal refactor to remove dependencies on the identity and token manager services, which have been removed. Public APIs no longer require the identity service or token manager to be provided. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- 5726390: The following collator factories are deprecated, please [migrate](https://backstage.io/docs/backend-system/building-backends/migrating) to the new backend system and follow the instructions below to install collators via module: + + - `DefaultCatalogCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-catalog/README.md#installation; + - `ToolDocumentCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-explore/README.md#installation; + - `DefaultTechDocsCollatorFactory`: https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-techdocs/README.md#installation. + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.12.11-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-catalog-node@1.12.7-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.2.0 ### Minor Changes diff --git a/plugins/search-backend-module-techdocs/api-report-alpha.md b/plugins/search-backend-module-techdocs/api-report-alpha.md index ed1e72b3bf..4176bd56c6 100644 --- a/plugins/search-backend-module-techdocs/api-report-alpha.md +++ b/plugins/search-backend-module-techdocs/api-report-alpha.md @@ -3,12 +3,12 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { TechDocsCollatorEntityTransformer } from '@backstage/plugin-search-backend-module-techdocs'; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // @alpha (undocumented) diff --git a/plugins/search-backend-module-techdocs/api-report.md b/plugins/search-backend-module-techdocs/api-report.md index 35cf8533cf..9c9fae1a38 100644 --- a/plugins/search-backend-module-techdocs/api-report.md +++ b/plugins/search-backend-module-techdocs/api-report.md @@ -8,12 +8,12 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { Entity } from '@backstage/catalog-model'; import { HttpAuthService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { Permission } from '@backstage/plugin-permission-common'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { Readable } from 'stream'; import { TechDocsDocument } from '@backstage/plugin-techdocs-node'; import { TokenManager } from '@backstage/backend-common'; @@ -21,7 +21,7 @@ import { TokenManager } from '@backstage/backend-common'; // @public (undocumented) export const defaultTechDocsCollatorEntityTransformer: TechDocsCollatorEntityTransformer; -// @public +// @public @deprecated export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { // (undocumented) static fromConfig( @@ -41,9 +41,9 @@ export type TechDocsCollatorEntityTransformer = ( entity: Entity, ) => Omit; -// @public +// @public @deprecated export type TechDocsCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; logger: LoggerService; tokenManager?: TokenManager; auth?: AuthService; diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index cb95652355..9d5f3b238a 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.2.0", + "version": "0.2.2-next.0", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/src/alpha.ts b/plugins/search-backend-module-techdocs/src/alpha.ts index 77cf6af47b..344a50c59d 100644 --- a/plugins/search-backend-module-techdocs/src/alpha.ts +++ b/plugins/search-backend-module-techdocs/src/alpha.ts @@ -78,7 +78,6 @@ export default createBackendModule({ auth: coreServices.auth, httpAuth: coreServices.httpAuth, discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, scheduler: coreServices.scheduler, catalog: catalogServiceRef, indexRegistry: searchIndexRegistryExtensionPoint, @@ -89,7 +88,6 @@ export default createBackendModule({ auth, httpAuth, discovery, - tokenManager, scheduler, catalog, indexRegistry, @@ -110,7 +108,6 @@ export default createBackendModule({ schedule: scheduler.createScheduledTaskRunner(schedule), factory: DefaultTechDocsCollatorFactory.fromConfig(config, { discovery, - tokenManager, auth, httpAuth, logger, diff --git a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts index 476c07ff48..1fc23b915e 100644 --- a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts +++ b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts @@ -13,10 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - PluginEndpointDiscovery, - TokenManager, -} from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { ConfigReader } from '@backstage/config'; import { TestPipeline } from '@backstage/plugin-search-backend-node'; @@ -30,6 +26,7 @@ import { Readable } from 'stream'; import { DefaultTechDocsCollatorFactory } from './DefaultTechDocsCollatorFactory'; import { defaultTechDocsCollatorEntityTransformer } from './defaultTechDocsCollatorEntityTransformer'; import { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTransformer'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; const logger = mockServices.logger.mock(); @@ -82,18 +79,13 @@ const expectedEntities: Entity[] = [ describe('DefaultTechDocsCollatorFactory', () => { const config = new ConfigReader({}); - const mockDiscoveryApi: jest.Mocked = { + const mockDiscoveryApi: jest.Mocked = { getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), getExternalBaseUrl: jest.fn(), }; - const mockTokenManager: jest.Mocked = { - getToken: jest.fn().mockResolvedValue({ token: '' }), - authenticate: jest.fn(), - }; const options = { logger, discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, }; it('has expected type', () => { @@ -187,7 +179,6 @@ describe('DefaultTechDocsCollatorFactory', () => { }); factory = DefaultTechDocsCollatorFactory.fromConfig(_config, { discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, logger, }); collator = await factory.getCollator(); diff --git a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts index 10c09d02b2..748cb515d9 100644 --- a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts +++ b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts @@ -16,7 +16,6 @@ import { createLegacyAuthAdapters, - PluginEndpointDiscovery, TokenManager, } from '@backstage/backend-common'; import { @@ -43,6 +42,7 @@ import { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTrans import { defaultTechDocsCollatorEntityTransformer } from './defaultTechDocsCollatorEntityTransformer'; import { AuthService, + DiscoveryService, HttpAuthService, LoggerService, } from '@backstage/backend-plugin-api'; @@ -57,9 +57,10 @@ interface MkSearchIndexDoc { * Options to configure the TechDocs collator factory * * @public + * @deprecated This type is deprecated along with the {@link DefaultTechDocsCollatorFactory}. */ export type TechDocsCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; + discovery: DiscoveryService; logger: LoggerService; tokenManager?: TokenManager; auth?: AuthService; @@ -82,13 +83,14 @@ type EntityInfo = { * TechDocs documents. * * @public + * @deprecated Migrate to the {@link https://backstage.io/docs/backend-system/building-backends/migrating | new backend system} and install this collator via module instead (see {@link https://github.com/backstage/backstage/blob/nbs10/search-deprecate-create-router/plugins/search-backend-module-techdocs/README.md#installation | here} for more installation details). */ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { public readonly type: string = 'techdocs'; public readonly visibilityPermission: Permission = catalogEntityReadPermission; - private discovery: PluginEndpointDiscovery; + private discovery: DiscoveryService; private locationTemplate: string; private readonly logger: LoggerService; private readonly auth: AuthService; diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 3c566e48d3..bcbee26057 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-node +## 1.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + ## 1.3.0 ### Minor Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index e951243136..a900da8917 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "1.3.0", + "version": "1.3.2-next.0", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 679c2777ef..4581b5ac91 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-search-backend +## 1.5.17-next.0 + +### Patch Changes + +- 5726390: Deprecate create router as the legacy backend system will no longer be supported. +- d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/plugin-permission-node@0.8.3-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.0 + - @backstage/plugin-search-backend-node@1.3.2-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + ## 1.5.15 ### Patch Changes diff --git a/plugins/search-backend/api-report-alpha.md b/plugins/search-backend/api-report-alpha.md index 2e5de660cb..8221359a36 100644 --- a/plugins/search-backend/api-report-alpha.md +++ b/plugins/search-backend/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const _default: BackendFeatureCompat; +const _default: BackendFeature; export default _default; // (No @packageDocumentation comment for this package) diff --git a/plugins/search-backend/api-report.md b/plugins/search-backend/api-report.md index 717a9dbe8b..b3f617e5e1 100644 --- a/plugins/search-backend/api-report.md +++ b/plugins/search-backend/api-report.md @@ -14,10 +14,10 @@ import { PermissionAuthorizer } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; -// @public (undocumented) +// @public @deprecated (undocumented) export function createRouter(options: RouterOptions): Promise; -// @public (undocumented) +// @public @deprecated (undocumented) export type RouterOptions = { engine: SearchEngine; types: Record; diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 60545dbf95..2c635fa4fe 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "1.5.15", + "version": "1.5.17-next.0", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", @@ -72,7 +72,6 @@ "express": "^4.17.1", "lodash": "^4.17.21", "qs": "^6.10.1", - "winston": "^3.2.1", "yn": "^4.0.0", "zod": "^3.22.4" }, diff --git a/plugins/search-backend/src/service/router.test.ts b/plugins/search-backend/src/service/router.test.ts index 81b45bc7e6..d22fc17e54 100644 --- a/plugins/search-backend/src/service/router.test.ts +++ b/plugins/search-backend/src/service/router.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { @@ -26,7 +25,12 @@ import request from 'supertest'; import { createRouter } from './router'; import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils'; import { Server } from 'http'; -import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; +import { + mockCredentials, + mockErrorHandler, + mockServices, +} from '@backstage/backend-test-utils'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; const mockPermissionEvaluator: PermissionEvaluator = { authorize: () => { @@ -42,7 +46,7 @@ describe('createRouter', () => { let mockSearchEngine: jest.Mocked; const mockBaseUrl = 'http://backstage:9191/api/proxy'; - const discovery: PluginEndpointDiscovery = { + const discovery: DiscoveryService = { async getBaseUrl() { return mockBaseUrl; }, @@ -83,7 +87,9 @@ describe('createRouter', () => { auth: mockServices.auth(), httpAuth: mockServices.httpAuth(), }); - app = wrapInOpenApiTestServer(express().use(router)); + app = wrapInOpenApiTestServer( + express().use(router).use(mockErrorHandler()), + ); }); beforeEach(() => { diff --git a/plugins/search-backend/src/service/router.ts b/plugins/search-backend/src/service/router.ts index d226805baf..9c22290a63 100644 --- a/plugins/search-backend/src/service/router.ts +++ b/plugins/search-backend/src/service/router.ts @@ -16,11 +16,7 @@ import express from 'express'; import { z } from 'zod'; -import { - createLegacyAuthAdapters, - errorHandler, - HostDiscovery, -} from '@backstage/backend-common'; +import { createLegacyAuthAdapters } from '@backstage/backend-common'; import { InputError } from '@backstage/errors'; import { Config } from '@backstage/config'; import { JsonObject, JsonValue } from '@backstage/types'; @@ -43,6 +39,7 @@ import { HttpAuthService, LoggerService, } from '@backstage/backend-plugin-api'; +import { HostDiscovery } from '@backstage/backend-defaults/discovery'; const jsonObjectSchema: z.ZodSchema = z.lazy(() => { const jsonValueSchema: z.ZodSchema = z.lazy(() => @@ -61,6 +58,7 @@ const jsonObjectSchema: z.ZodSchema = z.lazy(() => { /** * @public + * @deprecated Please migrate to the new backend system as this will be removed in the future. */ export type RouterOptions = { engine: SearchEngine; @@ -69,6 +67,7 @@ export type RouterOptions = { permissions: PermissionEvaluator | PermissionAuthorizer; config: Config; logger: LoggerService; + // TODO: Make "auth" and "httpAuth" required once we remove the usage of "tokenManager" auth?: AuthService; httpAuth?: HttpAuthService; }; @@ -79,6 +78,7 @@ const allowedLocationProtocols = ['http:', 'https:']; /** * @public + * @deprecated Please migrate to the new backend system as this will be removed in the future. */ export async function createRouter( options: RouterOptions, @@ -93,6 +93,7 @@ export async function createRouter( discovery = HostDiscovery.fromConfig(config), } = options; + // TODO: stop using this adapter when the "tokenManager" is removed const { auth, httpAuth } = createLegacyAuthAdapters({ ...options, discovery, @@ -217,7 +218,5 @@ export async function createRouter( } }); - router.use(errorHandler()); - return router; } diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index fa409b56b0..b86490a172 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-search-react +## 1.8.0-next.0 + +### Minor Changes + +- 9d66d8c: Make use of the `useApp` hook to retrieve the specified search icon in the SearchBar + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- f26ff99: Slight type tweak to match newer React versions better +- 5446061: The `/alpha` export no longer export extension creators for the new frontend system, existing usage should be switched to use the equivalent extension blueprint instead. For more information see the [new frontend system 1.30 migration documentation](https://backstage.io/docs/frontend-system/architecture/migrations#130). +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + ## 1.7.14 ### Patch Changes diff --git a/plugins/search-react/api-report-alpha.md b/plugins/search-react/api-report-alpha.md index 57bf03114f..51c98a8d93 100644 --- a/plugins/search-react/api-report-alpha.md +++ b/plugins/search-react/api-report-alpha.md @@ -30,29 +30,27 @@ export type SearchResultItemExtensionPredicate = ( ) => boolean; // @alpha -export const SearchResultListItemBlueprint: ExtensionBlueprint< - { - kind: 'search-result-list-item'; - namespace: undefined; - name: undefined; - }, - SearchResultListItemBlueprintParams, - ConfigurableExtensionDataRef< +export const SearchResultListItemBlueprint: ExtensionBlueprint<{ + kind: 'search-result-list-item'; + namespace: undefined; + name: undefined; + params: SearchResultListItemBlueprintParams; + output: ConfigurableExtensionDataRef< { predicate?: SearchResultItemExtensionPredicate | undefined; component: SearchResultItemExtensionComponent; }, 'search.search-result-list-item.item', {} - >, - {}, - { + >; + inputs: {}; + config: { noTrack: boolean; - }, - { + }; + configInput: { noTrack?: boolean | undefined; - }, - { + }; + dataRefs: { item: ConfigurableExtensionDataRef< { predicate?: SearchResultItemExtensionPredicate | undefined; @@ -61,8 +59,8 @@ export const SearchResultListItemBlueprint: ExtensionBlueprint< 'search.search-result-list-item.item', {} >; - } ->; + }; +}>; // @alpha (undocumented) export interface SearchResultListItemBlueprintParams { diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index b5b06a2904..d151ef1187 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -9,7 +9,6 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { AsyncState } from 'react-use/esm/useAsync'; import { AutocompleteProps } from '@material-ui/lab/Autocomplete'; import { Extension } from '@backstage/core-plugin-api'; -import { ForwardRefExoticComponent } from 'react'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { LinkProps } from '@backstage/core-components'; @@ -132,10 +131,23 @@ export type SearchAutocompleteProps