diff --git a/.changeset/analytics-millenial-whoop.md b/.changeset/analytics-millenial-whoop.md deleted file mode 100644 index 265e0072ce..0000000000 --- a/.changeset/analytics-millenial-whoop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-app-api': patch ---- - -Fixed a bug that could cause `navigate` analytics events to be misattributed to the plugin mounted on the root route (e.g. the `home` plugin at `/`) when the route that was navigated to wasn't associated with a routable extension. diff --git a/.changeset/chatty-foxes-buy.md b/.changeset/chatty-foxes-buy.md deleted file mode 100644 index 609ddc5269..0000000000 --- a/.changeset/chatty-foxes-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-graph': patch ---- - -Propagate entity spec to EntityNode so that spec info such as type can be used for graph node customization diff --git a/.changeset/chatty-seahorses-juggle.md b/.changeset/chatty-seahorses-juggle.md deleted file mode 100644 index 8d699699a7..0000000000 --- a/.changeset/chatty-seahorses-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-backend': patch ---- - -Fixed a bug where the proxy was not rewriting WebSocket request paths properly. diff --git a/.changeset/chilly-keys-count.md b/.changeset/chilly-keys-count.md deleted file mode 100644 index 3bbc4e40dc..0000000000 --- a/.changeset/chilly-keys-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-playlist': patch ---- - -Displaying an alert popup each time the Playlist is created or deleted diff --git a/.changeset/create-app-1690284535.md b/.changeset/create-app-1690284535.md deleted file mode 100644 index b50d431d4b..0000000000 --- a/.changeset/create-app-1690284535.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Bumped create-app version. diff --git a/.changeset/dirty-chefs-listen.md b/.changeset/dirty-chefs-listen.md deleted file mode 100644 index 84b684b977..0000000000 --- a/.changeset/dirty-chefs-listen.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/config-loader': minor ---- - -Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. - -In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. diff --git a/.changeset/dry-wasps-occur.md b/.changeset/dry-wasps-occur.md new file mode 100644 index 0000000000..0c7d231708 --- /dev/null +++ b/.changeset/dry-wasps-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-gcp-iap-provider': minor +--- + +New module for `@backstage/plugin-auth-backend` that adds a GCP IAP auth provider. diff --git a/.changeset/empty-rings-type.md b/.changeset/empty-rings-type.md new file mode 100644 index 0000000000..2aa41054f9 --- /dev/null +++ b/.changeset/empty-rings-type.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add examples for `publish:github` and `publish:gitlab` scaffolder actions. diff --git a/.changeset/gorgeous-months-doubt.md b/.changeset/gorgeous-months-doubt.md deleted file mode 100644 index d440e28b72..0000000000 --- a/.changeset/gorgeous-months-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-openapi-utils': patch ---- - -Add a new `createRouter` method for generating an `express` router that validates against your spec. Also fixes a bug with the query parameters type resolution. diff --git a/.changeset/healthy-tools-count.md b/.changeset/healthy-tools-count.md new file mode 100644 index 0000000000..5d3f04159f --- /dev/null +++ b/.changeset/healthy-tools-count.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Deprecated several exports that are now available from `@backstage/plugin-auth-node` instead. diff --git a/.changeset/hungry-shrimps-care.md b/.changeset/hungry-shrimps-care.md deleted file mode 100644 index 665a7bfc82..0000000000 --- a/.changeset/hungry-shrimps-care.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-events-backend-module-bitbucket-cloud': patch -'@backstage/plugin-search-backend-module-elasticsearch': patch -'@backstage/plugin-catalog-backend-module-unprocessed': patch -'@backstage/plugin-scaffolder-backend-module-sentry': patch -'@backstage/plugin-scaffolder-backend-module-yeoman': patch -'@backstage/plugin-scaffolder-backend-module-rails': patch -'@backstage/plugin-search-backend-module-techdocs': patch -'@backstage/plugin-cicd-statistics-module-gitlab': patch -'@backstage/plugin-events-backend-module-aws-sqs': patch -'@backstage/plugin-search-backend-module-catalog': patch -'@backstage/plugin-search-backend-module-explore': 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-module-azure': patch -'@backstage/plugin-events-backend-test-utils': patch -'@backstage/plugin-search-backend-module-pg': patch -'@backstage/plugin-entity-feedback-backend': patch -'@backstage/backend-openapi-utils': patch -'@backstage/plugin-bitbucket-cloud-common': patch -'@backstage/plugin-entity-feedback-common': patch -'@backstage/plugin-code-coverage-backend': patch -'@backstage/plugin-user-settings-backend': patch -'@backstage/plugin-analytics-module-ga4': patch -'@backstage/plugin-azure-devops-backend': patch -'@backstage/plugin-analytics-module-ga': patch -'@backstage/plugin-git-release-manager': patch -'@backstage/plugin-search-backend-node': patch -'@backstage/integration-react': patch -'@backstage/plugin-github-deployments': patch -'@backstage/plugin-microsoft-calendar': patch -'@backstage/plugin-newrelic-dashboard': patch -'@backstage/plugin-permission-backend': patch -'@backstage/plugin-entity-validation': patch -'@backstage/plugin-sonarqube-backend': patch -'@backstage/plugin-airbrake-backend': patch -'@backstage/plugin-devtools-backend': patch -'@backstage/plugin-linguist-backend': patch -'@backstage/plugin-periskop-backend': patch -'@backstage/plugin-playlist-backend': patch -'@backstage/plugin-apollo-explorer': patch -'@backstage/plugin-devtools-common': patch -'@backstage/plugin-entity-feedback': patch -'@backstage/plugin-explore-backend': patch -'@backstage/plugin-graphql-voyager': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-linguist-common': patch -'@backstage/plugin-playlist-common': patch -'@backstage/plugin-scaffolder-node': patch -'@backstage/plugin-apache-airflow': patch -'@backstage/plugin-bazaar-backend': patch -'@backstage/plugin-events-backend': patch -'@backstage/plugin-jenkins-common': patch -'@backstage/plugin-octopus-deploy': patch -'@backstage/plugin-search-backend': patch -'@backstage/plugin-stack-overflow': patch -'@backstage/plugin-catalog-graph': patch -'@backstage/plugin-code-coverage': patch -'@backstage/plugin-config-schema': patch -'@backstage/plugin-github-issues': patch -'@backstage/plugin-nomad-backend': patch -'@backstage/plugin-tech-insights': patch -'@backstage/plugin-catalog-node': patch -'@backstage/plugin-code-climate': patch -'@backstage/plugin-events-node': patch -'@backstage/plugin-firehydrant': patch -'@backstage/cli-node': patch -'@backstage/plugin-auth-node': patch -'@backstage/plugin-codescene': patch -'@backstage/plugin-dynatrace': patch -'@backstage/plugin-gcalendar': patch -'@backstage/plugin-org-react': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-xcmetrics': patch -'@backstage/plugin-airbrake': patch -'@backstage/plugin-periskop': patch -'@backstage/plugin-playlist': patch -'@backstage/plugin-bitrise': patch -'@backstage/plugin-allure': patch -'@backstage/plugin-bazaar': patch -'@backstage/plugin-ilert': patch -'@backstage/plugin-kafka': patch -'@backstage/plugin-nomad': patch -'@backstage/plugin-adr': patch -'@backstage/plugin-org': patch ---- - -Add package repository and homepage metadata diff --git a/.changeset/khaki-flies-draw.md b/.changeset/khaki-flies-draw.md deleted file mode 100644 index ada45dea06..0000000000 --- a/.changeset/khaki-flies-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-gitlab': patch ---- - -Added option to skip forked repos in GitlabDiscoveryEntityProvider diff --git a/.changeset/kind-cougars-allow.md b/.changeset/kind-cougars-allow.md deleted file mode 100644 index d37c4c5cf2..0000000000 --- a/.changeset/kind-cougars-allow.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/plugin-devtools-backend': patch ---- - -Add DevTools configuration to enable dependency listing to be filtered with custom prefixes. For instance, in your `app-config.yaml`: - -```yaml -devTools: - info: - packagePrefixes: - - @backstage/ - - @roadiehq/backstage- - - @spotify/backstage- -``` diff --git a/.changeset/large-experts-poke.md b/.changeset/large-experts-poke.md deleted file mode 100644 index 9f6ca640b6..0000000000 --- a/.changeset/large-experts-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-linguist-backend': patch ---- - -Fixed bug in LinguistBackendClient.ts file where if the linguistJsOptions is specified and sent over to the linguist-js package it would get changed (another attribute would be added) causing future entities of the batch to fail with an error diff --git a/.changeset/large-vans-cross.md b/.changeset/large-vans-cross.md deleted file mode 100644 index f50b432742..0000000000 --- a/.changeset/large-vans-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-sonarqube': patch ---- - -Remove sonarQube card disable class diff --git a/.changeset/loud-garlics-press.md b/.changeset/loud-garlics-press.md deleted file mode 100644 index 28aee20619..0000000000 --- a/.changeset/loud-garlics-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Reload the frontend when app config changes diff --git a/.changeset/mean-squids-relax.md b/.changeset/mean-squids-relax.md deleted file mode 100644 index 558b42fcfc..0000000000 --- a/.changeset/mean-squids-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-home-react': patch ---- - -Make `title` optional when defining the `createCardExtension` diff --git a/.changeset/neat-coins-raise.md b/.changeset/neat-coins-raise.md deleted file mode 100644 index c3045634bf..0000000000 --- a/.changeset/neat-coins-raise.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-bitbucket-server': patch -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch -'@backstage/plugin-catalog-backend-module-puppetdb': patch -'@backstage/plugin-catalog-backend-module-msgraph': patch -'@backstage/plugin-catalog-backend-module-gerrit': patch -'@backstage/plugin-catalog-backend-module-github': patch -'@backstage/plugin-catalog-backend-module-gitlab': patch -'@backstage/plugin-events-backend-module-aws-sqs': patch -'@backstage/plugin-catalog-backend-module-azure': patch -'@backstage/plugin-catalog-backend-module-aws': patch -'@backstage/backend-common': patch -'@backstage/plugin-jenkins-backend': patch -'@backstage/plugin-proxy-backend': patch ---- - -Fixed invalid configuration schema. The configuration schema may be more strict as a result. diff --git a/.changeset/neat-lamps-press.md b/.changeset/neat-lamps-press.md deleted file mode 100644 index 23c0b15fb7..0000000000 --- a/.changeset/neat-lamps-press.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-splunk-on-call': patch -'@backstage/plugin-xcmetrics': patch -'@backstage/plugin-catalog': patch ---- - -Prefer simple `theme.spacing` without string interpolation diff --git a/.changeset/odd-avocados-buy.md b/.changeset/odd-avocados-buy.md deleted file mode 100644 index 9e71c19fc0..0000000000 --- a/.changeset/odd-avocados-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch ---- - -Use `fetchContents` directly instead of a `fetchPlainAction` diff --git a/.changeset/pink-cups-rescue.md b/.changeset/pink-cups-rescue.md new file mode 100644 index 0000000000..abb69e88b7 --- /dev/null +++ b/.changeset/pink-cups-rescue.md @@ -0,0 +1,28 @@ +--- +'@backstage/plugin-auth-node': minor +--- + +Introduced a new system for building auth providers for `@backstage/plugin-auth-backend`, which both increases the amount of code re-use across providers, and also works better with the new backend system. + +Many existing types have been moved from `@backstage/plugin-auth-backend` in order to avoid a direct dependency on the plugin from modules. + +Auth provider integrations are now primarily implemented through a pattern of creating "authenticators", which are in turn specific to each kind of integrations. Initially there are two types: `createOAuthAuthenticator` and `createProxyAuthenticator`. These come paired with functions that let you create the corresponding route handlers, `createOAuthRouteHandlers` and `createProxyAuthRouteHandlers`, as well as provider factories, `createOAuthProviderFactory` and `createProxyAuthProviderFactory`. This new authenticator pattern allows the sign-in logic to be separated from the auth integration logic, allowing it to be completely re-used across all providers of the same kind. + +The new provider factories also implement a new declarative way to configure sign-in resolvers, rather than configuration through code. Sign-in resolvers can now be configured through the `resolvers` configuration key, where the first resolver that provides an identity will be used, for example: + +```yaml +auth: + providers: + google: + development: + clientId: ... + clientSecret: ... + signIn: + resolvers: + - resolver: emailMatchingUserEntityAnnotation + - resolver: emailLocalPartMatchingUserEntityName +``` + +These configurable resolvers are created with a new `createSignInResolverFactory` function, which creates a sign-in resolver factory, optionally with an options schema that will be used both when configuring the sign-in resolver through configuration and code. + +The internal helpers from `@backstage/plugin-auth-backend` that were used to implement auth providers using passport strategies have now also been made available as public API, through `PassportHelpers` and `PassportOAuthAuthenticatorHelper`. diff --git a/.changeset/pink-squids-nail.md b/.changeset/pink-squids-nail.md deleted file mode 100644 index 989b5034d5..0000000000 --- a/.changeset/pink-squids-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-unprocessed': minor ---- - -**BREAKING**: Fixing typo in exported module. You will have to rename the import to the correct spelling. `UnprocessedEntites` -> `UnprocessedEntities` diff --git a/.changeset/popular-fans-camp.md b/.changeset/popular-fans-camp.md deleted file mode 100644 index 47fc095ef8..0000000000 --- a/.changeset/popular-fans-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Updated config schema to match what was being used in code diff --git a/.changeset/pre.json b/.changeset/pre.json index 46dfaafe4e..9333a04d9f 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -2,253 +2,236 @@ "mode": "pre", "tag": "next", "initialVersions": { - "example-app": "0.2.85", - "@backstage/app-defaults": "1.4.1", - "example-backend": "0.2.85", - "@backstage/backend-app-api": "0.4.5", - "@backstage/backend-common": "0.19.1", - "@backstage/backend-defaults": "0.1.12", + "example-app": "0.2.86", + "@backstage/app-defaults": "1.4.2", + "example-backend": "0.2.86", + "@backstage/backend-app-api": "0.5.0", + "@backstage/backend-common": "0.19.2", + "@backstage/backend-defaults": "0.2.0", "@backstage/backend-dev-utils": "0.1.1", - "example-backend-next": "0.0.13", - "@backstage/backend-openapi-utils": "0.0.2", - "@backstage/backend-plugin-api": "0.5.4", - "@backstage/backend-tasks": "0.5.4", - "@backstage/backend-test-utils": "0.1.39", + "example-backend-next": "0.0.14", + "@backstage/backend-openapi-utils": "0.0.3", + "@backstage/backend-plugin-api": "0.6.0", + "@backstage/backend-tasks": "0.5.5", + "@backstage/backend-test-utils": "0.2.0", "@backstage/catalog-client": "1.4.3", "@backstage/catalog-model": "1.4.1", - "@backstage/cli": "0.22.9", + "@backstage/cli": "0.22.10", "@backstage/cli-common": "0.1.12", - "@backstage/cli-node": "0.1.2", + "@backstage/cli-node": "0.1.3", "@backstage/codemods": "0.1.45", "@backstage/config": "1.0.8", - "@backstage/config-loader": "1.3.2", - "@backstage/core-app-api": "1.9.0", - "@backstage/core-components": "0.13.3", + "@backstage/config-loader": "1.4.0", + "@backstage/core-app-api": "1.9.1", + "@backstage/core-components": "0.13.4", "@backstage/core-plugin-api": "1.5.3", - "@backstage/create-app": "0.5.3", - "@backstage/dev-utils": "1.0.17", - "e2e-test": "0.2.5", + "@backstage/create-app": "0.5.4", + "@backstage/dev-utils": "1.0.18", + "e2e-test": "0.2.6", "@backstage/errors": "1.2.1", "@backstage/eslint-plugin": "0.1.3", - "@backstage/integration": "1.5.1", + "@backstage/integration": "1.6.0", "@backstage/integration-aws-node": "0.1.5", - "@backstage/integration-react": "1.1.15", + "@backstage/integration-react": "1.1.16", "@backstage/release-manifests": "0.0.9", - "@backstage/repo-tools": "0.3.2", - "@techdocs/cli": "1.4.4", - "techdocs-cli-embedded-app": "0.2.84", - "@backstage/test-utils": "1.4.1", + "@backstage/repo-tools": "0.3.3", + "@techdocs/cli": "1.4.5", + "techdocs-cli-embedded-app": "0.2.85", + "@backstage/test-utils": "1.4.2", "@backstage/theme": "0.4.1", "@backstage/types": "1.1.0", "@backstage/version-bridge": "1.0.4", - "@backstage/plugin-adr": "0.6.3", - "@backstage/plugin-adr-backend": "0.3.5", - "@backstage/plugin-adr-common": "0.2.11", - "@backstage/plugin-airbrake": "0.3.20", - "@backstage/plugin-airbrake-backend": "0.2.20", - "@backstage/plugin-allure": "0.1.36", - "@backstage/plugin-analytics-module-ga": "0.1.31", - "@backstage/plugin-analytics-module-ga4": "0.1.2", - "@backstage/plugin-apache-airflow": "0.2.13", - "@backstage/plugin-api-docs": "0.9.6", + "@backstage/plugin-adr": "0.6.4", + "@backstage/plugin-adr-backend": "0.3.6", + "@backstage/plugin-adr-common": "0.2.12", + "@backstage/plugin-airbrake": "0.3.21", + "@backstage/plugin-airbrake-backend": "0.2.21", + "@backstage/plugin-allure": "0.1.37", + "@backstage/plugin-analytics-module-ga": "0.1.32", + "@backstage/plugin-analytics-module-ga4": "0.1.3", + "@backstage/plugin-analytics-module-newrelic-browser": "0.0.1", + "@backstage/plugin-apache-airflow": "0.2.14", + "@backstage/plugin-api-docs": "0.9.7", "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.2", - "@backstage/plugin-apollo-explorer": "0.1.13", - "@backstage/plugin-app-backend": "0.3.47", - "@backstage/plugin-auth-backend": "0.18.5", - "@backstage/plugin-auth-node": "0.2.16", - "@backstage/plugin-azure-devops": "0.3.2", - "@backstage/plugin-azure-devops-backend": "0.3.26", + "@backstage/plugin-apollo-explorer": "0.1.14", + "@backstage/plugin-app-backend": "0.3.48", + "@backstage/plugin-app-node": "0.1.0", + "@backstage/plugin-auth-backend": "0.18.6", + "@backstage/plugin-auth-node": "0.2.17", + "@backstage/plugin-azure-devops": "0.3.3", + "@backstage/plugin-azure-devops-backend": "0.3.27", "@backstage/plugin-azure-devops-common": "0.3.0", - "@backstage/plugin-azure-sites": "0.1.9", - "@backstage/plugin-azure-sites-backend": "0.1.9", + "@backstage/plugin-azure-sites": "0.1.10", + "@backstage/plugin-azure-sites-backend": "0.1.10", "@backstage/plugin-azure-sites-common": "0.1.0", - "@backstage/plugin-badges": "0.2.44", - "@backstage/plugin-badges-backend": "0.2.2", - "@backstage/plugin-bazaar": "0.2.11", - "@backstage/plugin-bazaar-backend": "0.2.10", - "@backstage/plugin-bitbucket-cloud-common": "0.2.8", - "@backstage/plugin-bitrise": "0.1.47", - "@backstage/plugin-catalog": "1.12.0", - "@backstage/plugin-catalog-backend": "1.11.0", - "@backstage/plugin-catalog-backend-module-aws": "0.2.2", - "@backstage/plugin-catalog-backend-module-azure": "0.1.18", - "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.14", - "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.14", - "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.12", - "@backstage/plugin-catalog-backend-module-gerrit": "0.1.15", - "@backstage/plugin-catalog-backend-module-github": "0.3.2", - "@backstage/plugin-catalog-backend-module-gitlab": "0.2.3", - "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.4.0", - "@backstage/plugin-catalog-backend-module-ldap": "0.5.14", - "@backstage/plugin-catalog-backend-module-msgraph": "0.5.6", - "@backstage/plugin-catalog-backend-module-openapi": "0.1.13", - "@backstage/plugin-catalog-backend-module-puppetdb": "0.1.4", - "@backstage/plugin-catalog-backend-module-unprocessed": "0.1.1", + "@backstage/plugin-badges": "0.2.45", + "@backstage/plugin-badges-backend": "0.2.3", + "@backstage/plugin-bazaar": "0.2.12", + "@backstage/plugin-bazaar-backend": "0.2.11", + "@backstage/plugin-bitbucket-cloud-common": "0.2.9", + "@backstage/plugin-bitrise": "0.1.48", + "@backstage/plugin-catalog": "1.12.1", + "@backstage/plugin-catalog-backend": "1.12.0", + "@backstage/plugin-catalog-backend-module-aws": "0.2.3", + "@backstage/plugin-catalog-backend-module-azure": "0.1.19", + "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.15", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.15", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.1.13", + "@backstage/plugin-catalog-backend-module-gcp": "0.1.0", + "@backstage/plugin-catalog-backend-module-gerrit": "0.1.16", + "@backstage/plugin-catalog-backend-module-github": "0.3.3", + "@backstage/plugin-catalog-backend-module-gitlab": "0.2.4", + "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.4.1", + "@backstage/plugin-catalog-backend-module-ldap": "0.5.15", + "@backstage/plugin-catalog-backend-module-msgraph": "0.5.7", + "@backstage/plugin-catalog-backend-module-openapi": "0.1.14", + "@backstage/plugin-catalog-backend-module-puppetdb": "0.1.5", + "@backstage/plugin-catalog-backend-module-unprocessed": "0.2.0", "@backstage/plugin-catalog-common": "1.0.15", - "@internal/plugin-catalog-customized": "0.0.12", - "@backstage/plugin-catalog-graph": "0.2.32", + "@internal/plugin-catalog-customized": "0.0.13", + "@backstage/plugin-catalog-graph": "0.2.33", "@backstage/plugin-catalog-graphql": "0.3.22", - "@backstage/plugin-catalog-import": "0.9.10", - "@backstage/plugin-catalog-node": "1.4.0", - "@backstage/plugin-catalog-react": "1.8.0", - "@backstage/plugin-catalog-unprocessed-entities": "0.1.1", - "@backstage/plugin-cicd-statistics": "0.1.22", - "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.16", - "@backstage/plugin-circleci": "0.3.20", - "@backstage/plugin-cloudbuild": "0.3.20", - "@backstage/plugin-code-climate": "0.1.20", - "@backstage/plugin-code-coverage": "0.2.13", - "@backstage/plugin-code-coverage-backend": "0.2.13", - "@backstage/plugin-codescene": "0.1.15", - "@backstage/plugin-config-schema": "0.1.43", - "@backstage/plugin-cost-insights": "0.12.9", + "@backstage/plugin-catalog-import": "0.9.11", + "@backstage/plugin-catalog-node": "1.4.1", + "@backstage/plugin-catalog-react": "1.8.1", + "@backstage/plugin-catalog-unprocessed-entities": "0.1.2", + "@backstage/plugin-cicd-statistics": "0.1.23", + "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.17", + "@backstage/plugin-circleci": "0.3.21", + "@backstage/plugin-cloudbuild": "0.3.21", + "@backstage/plugin-code-climate": "0.1.21", + "@backstage/plugin-code-coverage": "0.2.14", + "@backstage/plugin-code-coverage-backend": "0.2.14", + "@backstage/plugin-codescene": "0.1.16", + "@backstage/plugin-config-schema": "0.1.44", + "@backstage/plugin-cost-insights": "0.12.10", "@backstage/plugin-cost-insights-common": "0.1.1", - "@backstage/plugin-devtools": "0.1.2", - "@backstage/plugin-devtools-backend": "0.1.2", - "@backstage/plugin-devtools-common": "0.1.2", - "@backstage/plugin-dynatrace": "7.0.0", - "@backstage/plugin-entity-feedback": "0.2.3", - "@backstage/plugin-entity-feedback-backend": "0.1.5", - "@backstage/plugin-entity-feedback-common": "0.1.1", - "@backstage/plugin-entity-validation": "0.1.5", - "@backstage/plugin-events-backend": "0.2.8", - "@backstage/plugin-events-backend-module-aws-sqs": "0.2.2", - "@backstage/plugin-events-backend-module-azure": "0.1.9", - "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.9", - "@backstage/plugin-events-backend-module-gerrit": "0.1.9", - "@backstage/plugin-events-backend-module-github": "0.1.9", - "@backstage/plugin-events-backend-module-gitlab": "0.1.9", - "@backstage/plugin-events-backend-test-utils": "0.1.9", - "@backstage/plugin-events-node": "0.2.8", - "@internal/plugin-todo-list": "1.0.15", - "@internal/plugin-todo-list-backend": "1.0.15", + "@backstage/plugin-devtools": "0.1.3", + "@backstage/plugin-devtools-backend": "0.1.3", + "@backstage/plugin-devtools-common": "0.1.3", + "@backstage/plugin-dynatrace": "7.0.1", + "@backstage/plugin-entity-feedback": "0.2.4", + "@backstage/plugin-entity-feedback-backend": "0.1.6", + "@backstage/plugin-entity-feedback-common": "0.1.2", + "@backstage/plugin-entity-validation": "0.1.6", + "@backstage/plugin-events-backend": "0.2.9", + "@backstage/plugin-events-backend-module-aws-sqs": "0.2.3", + "@backstage/plugin-events-backend-module-azure": "0.1.10", + "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.1.10", + "@backstage/plugin-events-backend-module-gerrit": "0.1.10", + "@backstage/plugin-events-backend-module-github": "0.1.10", + "@backstage/plugin-events-backend-module-gitlab": "0.1.10", + "@backstage/plugin-events-backend-test-utils": "0.1.10", + "@backstage/plugin-events-node": "0.2.9", + "@internal/plugin-todo-list": "1.0.16", + "@internal/plugin-todo-list-backend": "1.0.16", "@internal/plugin-todo-list-common": "1.0.12", - "@backstage/plugin-explore": "0.4.6", - "@backstage/plugin-explore-backend": "0.0.9", + "@backstage/plugin-explore": "0.4.7", + "@backstage/plugin-explore-backend": "0.0.10", "@backstage/plugin-explore-common": "0.0.1", "@backstage/plugin-explore-react": "0.0.30", - "@backstage/plugin-firehydrant": "0.2.4", - "@backstage/plugin-fossa": "0.2.52", - "@backstage/plugin-gcalendar": "0.3.16", - "@backstage/plugin-gcp-projects": "0.3.39", - "@backstage/plugin-git-release-manager": "0.3.33", - "@backstage/plugin-github-actions": "0.6.1", - "@backstage/plugin-github-deployments": "0.1.51", - "@backstage/plugin-github-issues": "0.2.9", - "@backstage/plugin-github-pull-requests-board": "0.1.14", - "@backstage/plugin-gitops-profiles": "0.3.38", - "@backstage/plugin-gocd": "0.1.26", - "@backstage/plugin-graphiql": "0.2.52", - "@backstage/plugin-graphql-backend": "0.1.37", - "@backstage/plugin-graphql-voyager": "0.1.5", - "@backstage/plugin-home": "0.5.4", - "@backstage/plugin-home-react": "0.1.1", - "@backstage/plugin-ilert": "0.2.9", - "@backstage/plugin-jenkins": "0.8.2", - "@backstage/plugin-jenkins-backend": "0.2.2", - "@backstage/plugin-jenkins-common": "0.1.17", - "@backstage/plugin-kafka": "0.3.20", - "@backstage/plugin-kafka-backend": "0.2.40", - "@backstage/plugin-kubernetes": "0.9.3", - "@backstage/plugin-kubernetes-backend": "0.11.2", + "@backstage/plugin-firehydrant": "0.2.5", + "@backstage/plugin-fossa": "0.2.53", + "@backstage/plugin-gcalendar": "0.3.17", + "@backstage/plugin-gcp-projects": "0.3.40", + "@backstage/plugin-git-release-manager": "0.3.34", + "@backstage/plugin-github-actions": "0.6.2", + "@backstage/plugin-github-deployments": "0.1.52", + "@backstage/plugin-github-issues": "0.2.10", + "@backstage/plugin-github-pull-requests-board": "0.1.15", + "@backstage/plugin-gitops-profiles": "0.3.39", + "@backstage/plugin-gocd": "0.1.27", + "@backstage/plugin-graphiql": "0.2.53", + "@backstage/plugin-graphql-backend": "0.1.38", + "@backstage/plugin-graphql-voyager": "0.1.6", + "@backstage/plugin-home": "0.5.5", + "@backstage/plugin-home-react": "0.1.2", + "@backstage/plugin-ilert": "0.2.10", + "@backstage/plugin-jenkins": "0.8.3", + "@backstage/plugin-jenkins-backend": "0.2.3", + "@backstage/plugin-jenkins-common": "0.1.18", + "@backstage/plugin-kafka": "0.3.21", + "@backstage/plugin-kafka-backend": "0.2.41", + "@backstage/plugin-kubernetes": "0.10.0", + "@backstage/plugin-kubernetes-backend": "0.11.3", "@backstage/plugin-kubernetes-common": "0.6.5", - "@backstage/plugin-lighthouse": "0.4.5", - "@backstage/plugin-lighthouse-backend": "0.2.3", + "@backstage/plugin-lighthouse": "0.4.6", + "@backstage/plugin-lighthouse-backend": "0.2.4", "@backstage/plugin-lighthouse-common": "0.1.2", - "@backstage/plugin-linguist": "0.1.5", - "@backstage/plugin-linguist-backend": "0.3.1", - "@backstage/plugin-linguist-common": "0.1.0", - "@backstage/plugin-microsoft-calendar": "0.1.5", - "@backstage/plugin-newrelic": "0.3.38", - "@backstage/plugin-newrelic-dashboard": "0.2.13", - "@backstage/plugin-nomad": "0.1.1", - "@backstage/plugin-nomad-backend": "0.1.1", - "@backstage/plugin-octopus-deploy": "0.2.2", - "@backstage/plugin-org": "0.6.10", - "@backstage/plugin-org-react": "0.1.9", - "@backstage/plugin-pagerduty": "0.6.1", - "@backstage/plugin-periskop": "0.1.18", - "@backstage/plugin-periskop-backend": "0.1.18", - "@backstage/plugin-permission-backend": "0.5.22", + "@backstage/plugin-linguist": "0.1.6", + "@backstage/plugin-linguist-backend": "0.4.0", + "@backstage/plugin-linguist-common": "0.1.1", + "@backstage/plugin-microsoft-calendar": "0.1.6", + "@backstage/plugin-newrelic": "0.3.39", + "@backstage/plugin-newrelic-dashboard": "0.2.14", + "@backstage/plugin-nomad": "0.1.2", + "@backstage/plugin-nomad-backend": "0.1.2", + "@backstage/plugin-octopus-deploy": "0.2.3", + "@backstage/plugin-org": "0.6.11", + "@backstage/plugin-org-react": "0.1.10", + "@backstage/plugin-pagerduty": "0.6.2", + "@backstage/plugin-periskop": "0.1.19", + "@backstage/plugin-periskop-backend": "0.1.19", + "@backstage/plugin-permission-backend": "0.5.23", "@backstage/plugin-permission-common": "0.7.7", - "@backstage/plugin-permission-node": "0.7.10", + "@backstage/plugin-permission-node": "0.7.11", "@backstage/plugin-permission-react": "0.4.14", - "@backstage/plugin-playlist": "0.1.12", - "@backstage/plugin-playlist-backend": "0.3.3", - "@backstage/plugin-playlist-common": "0.1.8", - "@backstage/plugin-proxy-backend": "0.2.41", - "@backstage/plugin-puppetdb": "0.1.3", - "@backstage/plugin-rollbar": "0.4.20", - "@backstage/plugin-rollbar-backend": "0.1.44", - "@backstage/plugin-scaffolder": "1.14.1", - "@backstage/plugin-scaffolder-backend": "1.15.1", - "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.0", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.23", - "@backstage/plugin-scaffolder-backend-module-gitlab": "0.2.2", - "@backstage/plugin-scaffolder-backend-module-rails": "0.4.16", - "@backstage/plugin-scaffolder-backend-module-sentry": "0.1.7", - "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.20", - "@backstage/plugin-scaffolder-common": "1.3.2", - "@backstage/plugin-scaffolder-node": "0.1.5", - "@backstage/plugin-scaffolder-react": "1.5.1", - "@backstage/plugin-search": "1.3.3", - "@backstage/plugin-search-backend": "1.3.3", - "@backstage/plugin-search-backend-module-catalog": "0.1.3", - "@backstage/plugin-search-backend-module-elasticsearch": "1.3.2", - "@backstage/plugin-search-backend-module-explore": "0.1.3", - "@backstage/plugin-search-backend-module-pg": "0.5.8", - "@backstage/plugin-search-backend-module-techdocs": "0.1.3", - "@backstage/plugin-search-backend-node": "1.2.3", + "@backstage/plugin-playlist": "0.1.13", + "@backstage/plugin-playlist-backend": "0.3.4", + "@backstage/plugin-playlist-common": "0.1.9", + "@backstage/plugin-proxy-backend": "0.3.0", + "@backstage/plugin-puppetdb": "0.1.4", + "@backstage/plugin-rollbar": "0.4.21", + "@backstage/plugin-rollbar-backend": "0.1.45", + "@backstage/plugin-scaffolder": "1.14.2", + "@backstage/plugin-scaffolder-backend": "1.16.0", + "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.2.1", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.24", + "@backstage/plugin-scaffolder-backend-module-gitlab": "0.2.3", + "@backstage/plugin-scaffolder-backend-module-rails": "0.4.17", + "@backstage/plugin-scaffolder-backend-module-sentry": "0.1.8", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.21", + "@backstage/plugin-scaffolder-common": "1.4.0", + "@backstage/plugin-scaffolder-node": "0.2.0", + "@backstage/plugin-scaffolder-react": "1.5.2", + "@backstage/plugin-search": "1.3.4", + "@backstage/plugin-search-backend": "1.4.0", + "@backstage/plugin-search-backend-module-catalog": "0.1.4", + "@backstage/plugin-search-backend-module-elasticsearch": "1.3.3", + "@backstage/plugin-search-backend-module-explore": "0.1.4", + "@backstage/plugin-search-backend-module-pg": "0.5.9", + "@backstage/plugin-search-backend-module-techdocs": "0.1.4", + "@backstage/plugin-search-backend-node": "1.2.4", "@backstage/plugin-search-common": "1.2.5", - "@backstage/plugin-search-react": "1.6.3", - "@backstage/plugin-sentry": "0.5.5", - "@backstage/plugin-shortcuts": "0.3.12", - "@backstage/plugin-sonarqube": "0.7.1", - "@backstage/plugin-sonarqube-backend": "0.2.1", + "@backstage/plugin-search-react": "1.6.4", + "@backstage/plugin-sentry": "0.5.6", + "@backstage/plugin-shortcuts": "0.3.13", + "@backstage/plugin-sonarqube": "0.7.2", + "@backstage/plugin-sonarqube-backend": "0.2.2", "@backstage/plugin-sonarqube-react": "0.1.7", - "@backstage/plugin-splunk-on-call": "0.4.9", - "@backstage/plugin-stack-overflow": "0.1.18", - "@backstage/plugin-stack-overflow-backend": "0.2.3", - "@backstage/plugin-stackstorm": "0.1.4", - "@backstage/plugin-tech-insights": "0.3.12", - "@backstage/plugin-tech-insights-backend": "0.5.13", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.31", + "@backstage/plugin-splunk-on-call": "0.4.10", + "@backstage/plugin-stack-overflow": "0.1.19", + "@backstage/plugin-stack-overflow-backend": "0.2.4", + "@backstage/plugin-stackstorm": "0.1.5", + "@backstage/plugin-tech-insights": "0.3.13", + "@backstage/plugin-tech-insights-backend": "0.5.14", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.32", "@backstage/plugin-tech-insights-common": "0.2.11", - "@backstage/plugin-tech-insights-node": "0.4.5", - "@backstage/plugin-tech-radar": "0.6.6", - "@backstage/plugin-techdocs": "1.6.5", - "@backstage/plugin-techdocs-addons-test-utils": "1.0.16", - "@backstage/plugin-techdocs-backend": "1.6.4", - "@backstage/plugin-techdocs-module-addons-contrib": "1.0.15", - "@backstage/plugin-techdocs-node": "1.7.3", - "@backstage/plugin-techdocs-react": "1.1.8", - "@backstage/plugin-todo": "0.2.22", - "@backstage/plugin-todo-backend": "0.1.44", - "@backstage/plugin-user-settings": "0.7.5", - "@backstage/plugin-user-settings-backend": "0.1.11", - "@backstage/plugin-vault": "0.1.14", - "@backstage/plugin-vault-backend": "0.3.3", - "@backstage/plugin-xcmetrics": "0.2.40", - "@backstage/plugin-analytics-module-newrelic-browser": "0.0.0" + "@backstage/plugin-tech-insights-node": "0.4.6", + "@backstage/plugin-tech-radar": "0.6.7", + "@backstage/plugin-techdocs": "1.6.6", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.17", + "@backstage/plugin-techdocs-backend": "1.6.5", + "@backstage/plugin-techdocs-module-addons-contrib": "1.0.16", + "@backstage/plugin-techdocs-node": "1.7.4", + "@backstage/plugin-techdocs-react": "1.1.9", + "@backstage/plugin-todo": "0.2.23", + "@backstage/plugin-todo-backend": "0.2.0", + "@backstage/plugin-user-settings": "0.7.6", + "@backstage/plugin-user-settings-backend": "0.1.12", + "@backstage/plugin-vault": "0.1.15", + "@backstage/plugin-vault-backend": "0.3.4", + "@backstage/plugin-xcmetrics": "0.2.41" }, - "changesets": [ - "analytics-millenial-whoop", - "chatty-foxes-buy", - "create-app-1690284535", - "gorgeous-months-doubt", - "large-experts-poke", - "loud-garlics-press", - "mean-squids-relax", - "neat-lamps-press", - "pink-squids-nail", - "quiet-starfishes-kick", - "rare-pens-exist", - "rude-feet-sparkle", - "search-donuts-wash", - "selfish-coats-shout", - "serious-bats-repair", - "smart-pandas-applaud", - "stupid-berries-run", - "ten-otters-appear" - ] + "changesets": [] } diff --git a/.changeset/quiet-starfishes-kick.md b/.changeset/quiet-starfishes-kick.md deleted file mode 100644 index 2fc8ed632e..0000000000 --- a/.changeset/quiet-starfishes-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-home': patch ---- - -Now, user can provide a language (optional) as input to receive a greeting in that specific language. Example: `` diff --git a/.changeset/rare-pens-exist.md b/.changeset/rare-pens-exist.md deleted file mode 100644 index 7363f5033a..0000000000 --- a/.changeset/rare-pens-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Enable to print the config schema not merged with the `--no-merge` flag diff --git a/.changeset/renovate-76185f9.md b/.changeset/renovate-76185f9.md new file mode 100644 index 0000000000..5e23f3cfda --- /dev/null +++ b/.changeset/renovate-76185f9.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +Updated dependency `@types/pluralize` to `^0.0.30`. diff --git a/.changeset/rich-zoos-occur.md b/.changeset/rich-zoos-occur.md deleted file mode 100644 index 979acb93c6..0000000000 --- a/.changeset/rich-zoos-occur.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-plugin-api': minor -'@backstage/backend-defaults': minor ---- - -**BREAKING**: Removing shared environments concept from the new experimental backend system. diff --git a/.changeset/rude-feet-sparkle.md b/.changeset/rude-feet-sparkle.md deleted file mode 100644 index fabc83bb0b..0000000000 --- a/.changeset/rude-feet-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-analytics-module-newrelic-browser': patch ---- - -Introduced the New Relic Browser analytics module. Check out the plugins [README.md](https://github.com/backstage/backstage/tree/master/plugins/analytics-module-newrelic-browser) for more details! diff --git a/.changeset/search-donuts-wash.md b/.changeset/search-donuts-wash.md deleted file mode 100644 index 2fa3325761..0000000000 --- a/.changeset/search-donuts-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-node': patch ---- - -Fix Lunr search engine highlight by ignoring invalid metadata positions. diff --git a/.changeset/selfish-coats-shout.md b/.changeset/selfish-coats-shout.md deleted file mode 100644 index 071c47e371..0000000000 --- a/.changeset/selfish-coats-shout.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -'@backstage/config-loader': minor ---- - -Adds a new `deepVisibility` schema keyword that sets child visibility recursively to the defined value, respecting preexisting values or child `deepVisibility`. - -Example usage: - -```ts -export interface Config { - /** - * Enforces a default of `secret` instead of `backend` for this object. - * @deepVisibility secret - */ - mySecretProperty: { - type: 'object'; - properties: { - secretValue: { - type: 'string'; - }; - - verySecretProperty: { - type: 'string'; - }; - }; - }; -} -``` - -Example of a schema that would not be allowed: - -```ts -export interface Config { - /** - * Set the top level property to secret, enforcing a default of `secret` instead of `backend` for this object. - * @deepVisibility secret - */ - mySecretProperty: { - type: 'object'; - properties: { - frontendUrl: { - /** - * We can NOT override the visibility to reveal a property to the front end. - * @visibility frontend - */ - type: 'string'; - }; - - verySecretProperty: { - type: 'string'; - }; - }; - }; -} -``` diff --git a/.changeset/serious-bats-repair.md b/.changeset/serious-bats-repair.md deleted file mode 100644 index d93b49bda0..0000000000 --- a/.changeset/serious-bats-repair.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/repo-tools': patch ---- - -Update `schema openapi generate` command to now create a default router that can be imported and used directly. diff --git a/.changeset/slimy-kids-jam.md b/.changeset/slimy-kids-jam.md deleted file mode 100644 index c02daf325e..0000000000 --- a/.changeset/slimy-kids-jam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Added OpenTelemetry spans for catalog processing diff --git a/.changeset/slow-queens-visit.md b/.changeset/slow-queens-visit.md new file mode 100644 index 0000000000..2df948e477 --- /dev/null +++ b/.changeset/slow-queens-visit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-opencost': minor +--- + +New OpenCost plugin provides an port of the latest OpenCost UI to Backstage with updated dependencies. The plugin's README covers installation and configuration diff --git a/.changeset/smart-pandas-applaud.md b/.changeset/smart-pandas-applaud.md deleted file mode 100644 index 5f2c297df5..0000000000 --- a/.changeset/smart-pandas-applaud.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-todo-backend': minor -'@backstage/plugin-catalog-backend': minor -'@backstage/plugin-search-backend': minor ---- - -Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. diff --git a/.changeset/stale-wombats-talk.md b/.changeset/stale-wombats-talk.md deleted file mode 100644 index f4e0b852f8..0000000000 --- a/.changeset/stale-wombats-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-aws': patch ---- - -Added option to configure AWS `accountId` in `AwsS3EntityProvider` diff --git a/.changeset/stupid-berries-run.md b/.changeset/stupid-berries-run.md deleted file mode 100644 index f506ce2475..0000000000 --- a/.changeset/stupid-berries-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-home': patch ---- - -Make sure the widget name is never empty in the `AddWidgetDialog`. If the title was set to "", the entry would contain an empty string. Use the name as a fallback diff --git a/.changeset/sweet-hairs-complain.md b/.changeset/sweet-hairs-complain.md new file mode 100644 index 0000000000..56dede5617 --- /dev/null +++ b/.changeset/sweet-hairs-complain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-google-provider': minor +--- + +New module for `@backstage/plugin-auth-backend` that adds a Google auth provider. diff --git a/.changeset/ten-otters-appear.md b/.changeset/ten-otters-appear.md deleted file mode 100644 index 4cb3d301ae..0000000000 --- a/.changeset/ten-otters-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-react': patch ---- - -Fixed bug in Workflow component by passing a prop `templateName` down to Stepper component. diff --git a/.changeset/thin-yaks-hang.md b/.changeset/thin-yaks-hang.md deleted file mode 100644 index 1a1bdb4ab7..0000000000 --- a/.changeset/thin-yaks-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend': patch ---- - -Add missing `configSchema` to package.json diff --git a/.changeset/tough-dolphins-care.md b/.changeset/tough-dolphins-care.md deleted file mode 100644 index 7d9739ec10..0000000000 --- a/.changeset/tough-dolphins-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-gcp': minor ---- - -Added GCP catalog plugin with GKE provider diff --git a/.changeset/wild-timers-sparkle.md b/.changeset/wild-timers-sparkle.md deleted file mode 100644 index 3df14f3a69..0000000000 --- a/.changeset/wild-timers-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-microsoft-calendar': patch ---- - -README update - example of apiRef definition and fixed component name diff --git a/.clomonitor.yml b/.clomonitor.yml new file mode 100644 index 0000000000..1e217dd400 --- /dev/null +++ b/.clomonitor.yml @@ -0,0 +1,9 @@ +# CLOMonitor metadata file +# This file must be located at the root of the repository +# https://clomonitor.io/projects/cncf/backstage + +# Checks exemptions +exemptions: + - check: trademark_disclaimer # Check identifier (see https://github.com/cncf/clomonitor/blob/main/docs/checks.md#exemptions) + # Justification of this exemption (mandatory, it will be displayed on the UI) + reason: 'The Linux Foundation trademark disclaimer and link are contained within the project web site at https://backstage.io in the copyright footer. However, because the site is delivered dynamically via React through JavaScript, the check cannot currently identify it.' diff --git a/.github/labeler.yml b/.github/labeler.yml index eaba199588..dc048b1744 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -24,6 +24,8 @@ documentation: - docs/**/* microsite: - microsite/**/* +storybook: + - storybook/**/* auth: - plugins/auth-backend/**/* - packages/core-app-api/src/apis/implementations/auth/**/* diff --git a/.github/uffizzi/uffizzi.production.app-config.yaml b/.github/uffizzi/uffizzi.production.app-config.yaml index 77b093e024..4f64e5ffcb 100644 --- a/.github/uffizzi/uffizzi.production.app-config.yaml +++ b/.github/uffizzi/uffizzi.production.app-config.yaml @@ -56,72 +56,73 @@ catalog: - allow: [Template] proxy: - '/circleci/api': - target: https://circleci.com/api/v1.1 - headers: - Circle-Token: ${CIRCLECI_AUTH_TOKEN} + endpoints: + '/circleci/api': + target: https://circleci.com/api/v1.1 + headers: + Circle-Token: ${CIRCLECI_AUTH_TOKEN} - '/jenkins/api': - target: http://localhost:8080 - headers: - Authorization: ${JENKINS_BASIC_AUTH_HEADER} + '/jenkins/api': + target: http://localhost:8080 + headers: + Authorization: ${JENKINS_BASIC_AUTH_HEADER} - '/travisci/api': - target: https://api.travis-ci.com - changeOrigin: true - headers: - Authorization: ${TRAVISCI_AUTH_TOKEN} - travis-api-version: '3' + '/travisci/api': + target: https://api.travis-ci.com + changeOrigin: true + headers: + Authorization: ${TRAVISCI_AUTH_TOKEN} + travis-api-version: '3' - '/newrelic/apm/api': - target: https://api.newrelic.com/v2 - headers: - X-Api-Key: ${NEW_RELIC_REST_API_KEY} + '/newrelic/apm/api': + target: https://api.newrelic.com/v2 + headers: + X-Api-Key: ${NEW_RELIC_REST_API_KEY} - '/newrelic/api': - target: https://api.newrelic.com - headers: - X-Api-Key: ${NEW_RELIC_USER_KEY} + '/newrelic/api': + target: https://api.newrelic.com + headers: + X-Api-Key: ${NEW_RELIC_USER_KEY} - '/pagerduty': - target: https://api.pagerduty.com - headers: - Authorization: Token token=${PAGERDUTY_TOKEN} + '/pagerduty': + target: https://api.pagerduty.com + headers: + Authorization: Token token=${PAGERDUTY_TOKEN} - '/buildkite/api': - target: https://api.buildkite.com/v2/ - headers: - Authorization: ${BUILDKITE_TOKEN} + '/buildkite/api': + target: https://api.buildkite.com/v2/ + headers: + Authorization: ${BUILDKITE_TOKEN} - '/sentry/api': - target: https://sentry.io/api/ - allowedMethods: ['GET'] - headers: - Authorization: ${SENTRY_TOKEN} + '/sentry/api': + target: https://sentry.io/api/ + allowedMethods: ['GET'] + headers: + Authorization: ${SENTRY_TOKEN} - '/ilert': - target: https://api.ilert.com - allowedMethods: ['GET', 'POST', 'PUT'] - allowedHeaders: ['Authorization'] - headers: - Authorization: ${ILERT_AUTH_HEADER} + '/ilert': + target: https://api.ilert.com + allowedMethods: ['GET', 'POST', 'PUT'] + allowedHeaders: ['Authorization'] + headers: + Authorization: ${ILERT_AUTH_HEADER} - '/airflow': - target: https://your.airflow.instance.com/api/v1 - headers: - Authorization: ${AIRFLOW_BASIC_AUTH_HEADER} + '/airflow': + target: https://your.airflow.instance.com/api/v1 + headers: + Authorization: ${AIRFLOW_BASIC_AUTH_HEADER} - '/gocd': - target: https://your.gocd.instance.com/go/api - allowedMethods: ['GET'] - allowedHeaders: ['Authorization'] - headers: - Authorization: Basic ${GOCD_AUTH_CREDENTIALS} + '/gocd': + target: https://your.gocd.instance.com/go/api + allowedMethods: ['GET'] + allowedHeaders: ['Authorization'] + headers: + Authorization: Basic ${GOCD_AUTH_CREDENTIALS} - '/dynatrace': - target: https://your.dynatrace.instance.com/api/v2 - headers: - Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' + '/dynatrace': + target: https://your.dynatrace.instance.com/api/v2 + headers: + Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' techdocs: builder: 'local' # Alternatives - 'external' diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index ed09c73c91..9fa7af5585 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -123,8 +123,11 @@ FireHydrant Firekube Firestore Fiverr +Flightcontrol +flightcontrol Francesco Frontside +Gaurav gerrit Gerrit gitbeaker @@ -164,6 +167,7 @@ inlined inlinehilite integrator's interop +iterable JaCoCo JavaScript jenkins @@ -257,6 +261,7 @@ padding paddings pagerduty pageview +Pandey parallelization parseable Patrik @@ -405,6 +410,7 @@ unbreak Unconference unicode unmanaged +unmount unregister unregistering unregistration diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000..85c4b70724 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,66 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + # branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '41 8 * * 6' + push: + branches: ['master'] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: 'Checkout code' + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + with: + persist-credentials: false + + - name: 'Run analysis' + uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + publish_results: true + + # 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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: 'Upload to code-scanning' + uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/verify_e2e-kubernetes-noop.yml b/.github/workflows/verify_e2e-kubernetes-noop.yml index ee9dbc69e2..ac49fbc469 100644 --- a/.github/workflows/verify_e2e-kubernetes-noop.yml +++ b/.github/workflows/verify_e2e-kubernetes-noop.yml @@ -6,7 +6,7 @@ on: pull_request: paths-ignore: - 'yarn.lock' - - '.github/workflows/verify_kubernetes.yml' + - '.github/workflows/verify_e2e-kubernetes.yml' - 'packages/backend-common/src/**' jobs: diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index c05339f595..4c25c259c5 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -4,7 +4,7 @@ on: pull_request: paths: - 'yarn.lock' - - '.github/workflows/verify_kubernetes.yml' + - '.github/workflows/verify_e2e-kubernetes.yml' - 'packages/backend-common/src/**' jobs: @@ -35,7 +35,7 @@ jobs: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: bootstrap kind - uses: helm/kind-action@v1.7.0 + uses: helm/kind-action@v1.8.0 - name: kubernetes test working-directory: packages/backend-common diff --git a/ADOPTERS.md b/ADOPTERS.md index 9c76b01ff3..6668d471bb 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -43,7 +43,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [MavTek](https://www.mavtek.com/) | [@fgascon](https://github.com/fgascon) | Developer portal focused on standardizing practices, centralizing documentation and streamlining developer practices. | | [QuintoAndar](https://www.quintoandar.com.br/) | [@quintoandar](https://github.com/quintoandar) | Developer portal, services catalog and centralization of service metrics. | | [empathy.co](https://empathy.co/) | [@guillermotti](https://github.com/guillermotti) | Developer portal for tech docs, service catalog, plugin discovery and much more. | -| [creditas.com](https://creditas.com/) | [@aureliosaraiva](https://github.com/aureliosaraiva) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | +| [creditas.com](https://creditas.com/) | [@rebender](https://github.com/rebender) [@Creditas](https://github.com/creditas) | Centralization of all services, standards, documentation, etc. We started the deployment process. | | [Prisjakt](https://www.prisjakt.nu) / [PriceSpy](https://pricespy.co.uk) | [@kennylindahl](https://github.com/kennylindahl) | Internal developer portal - Documentation, scaffolding, software catalog, TechRadar, Gitlab org data integration | | [Powerspike](https://powerspike.tv/) | [@trelore](https://github.com/trelore) | Developer portal for documentation of core libraries and repositories. | | [2U](https://2u.com) | [@danielleEriksen](https://github.com/danielleEriksen), [@sbhatia](https://github.com/sbhatia) | Development team home-base, promoting service discoverability, resource dependencies, and tech radar | @@ -256,3 +256,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [JB Hi-Fi](https://www.jbhifi.com.au) | [@bahman](https://github.com/bahman-jb/) | We use Backstage as a central place for all of our components/apis across multiple teams. It helps us to quickly identify the ownership of a component/api, find the related links and have a quick access to CI/CD pipelines. | | [Zenklub](https://www.zenklub.com.br) | [@zenklub](https://github.com/zenklub), [@gioufop](https://github.com/gioufop) | Developer portal, services catalog and centralization of metrics from Grafana Stack and AWS. Furthermore, centralization of documentation and infra details like Tools, Network services and so on. | | [Platzi](https://platzi.com/) | [@juancarestre](https://github.com/juancarestre/), [Engineering at Platzi](https://github.com/PlatziDev/) | Backstage allow our developers to get easily engaged with all the internal components, technical documentations and software templates. All new developers reduce its onboarding time via Backstage, and after a couple of integrations it allowed us to create new components with in a couple of clicks. | +| [idealo](https://idealo.de) | [Wanis Fahmy](https://github.com/wanisfahmyDE), [Sajjad Pervaiz](https://github.com/sjvaiz), [Tim Heurich](https://github.com/theurichde) | Backstage is our Internal developer portal. Along with other plugins, We use the software catalog, TechDocs and explore plugins to manage, discover and document our software components and platform products. | diff --git a/OWNERS.md b/OWNERS.md index 5a9457d755..66575e8e35 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -42,6 +42,7 @@ Scope: Discoverability within Backstage, including the home page, information ar | Emma Indal | Spotify | BUX | [emmaindal](http://github.com/emmaindal) | emmaindal#7503 | | Raghunandan Balachandran | Spotify | BUX | [soapraj](http://github.com/soapraj) | raghunandanb#1114 | | Renan Mendes Carvalho | Spotify | BUX | [aitherios](http://github.com/aitherios) | aitherios#0593 | +| Bailey Brooks | Spotify | BUX | [bailey](http://github.com/bailey) | \_bailey4 | ### Helm Charts diff --git a/README.md b/README.md index d9da778694..ff72c41c87 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ [![Codecov](https://img.shields.io/codecov/c/github/backstage/backstage)](https://codecov.io/gh/backstage/backstage) [![](https://img.shields.io/github/v/release/backstage/backstage)](https://github.com/backstage/backstage/releases) [![Uffizzi](https://img.shields.io/endpoint?url=https%3A%2F%2Fapp.uffizzi.com%2Fapi%2Fv1%2Fpublic%2Fshields%2Fgithub.com%2Fbackstage%2Fbackstage)](https://app.uffizzi.com/ephemeral-environments/backstage/backstage) - -> 🏖️ The week beginning the 26th of June, some of the maintainers will be taking a well earned Summer Holiday break. We will be slower to respond to issues and PRs during this time. Releases will continue to be shipped weekly. Normal service will resume on the 24th of July. 🏝️ +[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/7678/badge)](https://bestpractices.coreinfrastructure.org/projects/7678) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/backstage/backstage/badge)](https://securityscorecards.dev/viewer/?uri=github.com/backstage/backstage) ## What is Backstage? diff --git a/app-config.yaml b/app-config.yaml index 8dcd052e61..37cb61f26e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -20,6 +20,8 @@ app: - url: https://discord.gg/backstage-687207715902193673 title: '#backstage' + packageName: example-app + backend: # Used for enabling authentication, secret is shared by all backend plugins # See https://backstage.io/docs/auth/service-to-service-auth for @@ -49,80 +51,83 @@ backend: # See README.md in the proxy-backend plugin for information on the configuration format proxy: - '/circleci/api': - target: https://circleci.com/api/v1.1 - headers: - Circle-Token: ${CIRCLECI_AUTH_TOKEN} + endpoints: + '/circleci/api': + target: https://circleci.com/api/v1.1 + headers: + Circle-Token: ${CIRCLECI_AUTH_TOKEN} - '/jenkins/api': - target: http://localhost:8080 - headers: - Authorization: ${JENKINS_BASIC_AUTH_HEADER} + '/jenkins/api': + target: http://localhost:8080 + headers: + Authorization: ${JENKINS_BASIC_AUTH_HEADER} - '/travisci/api': - target: https://api.travis-ci.com - changeOrigin: true - headers: - Authorization: ${TRAVISCI_AUTH_TOKEN} - travis-api-version: '3' + '/travisci/api': + target: https://api.travis-ci.com + changeOrigin: true + headers: + Authorization: ${TRAVISCI_AUTH_TOKEN} + travis-api-version: '3' - '/newrelic/apm/api': - target: https://api.newrelic.com/v2 - headers: - X-Api-Key: ${NEW_RELIC_REST_API_KEY} + '/newrelic/apm/api': + target: https://api.newrelic.com/v2 + headers: + X-Api-Key: ${NEW_RELIC_REST_API_KEY} + allowedHeaders: + - link - '/newrelic/api': - target: https://api.newrelic.com - headers: - X-Api-Key: ${NEW_RELIC_USER_KEY} + '/newrelic/api': + target: https://api.newrelic.com + headers: + X-Api-Key: ${NEW_RELIC_USER_KEY} - '/pagerduty': - target: https://api.pagerduty.com - headers: - Authorization: Token token=${PAGERDUTY_TOKEN} + '/pagerduty': + target: https://api.pagerduty.com + headers: + Authorization: Token token=${PAGERDUTY_TOKEN} - '/buildkite/api': - target: https://api.buildkite.com/v2/ - headers: - Authorization: ${BUILDKITE_TOKEN} + '/buildkite/api': + target: https://api.buildkite.com/v2/ + headers: + Authorization: ${BUILDKITE_TOKEN} - '/sentry/api': - target: https://sentry.io/api/ - allowedMethods: ['GET'] - headers: - Authorization: ${SENTRY_TOKEN} + '/sentry/api': + target: https://sentry.io/api/ + allowedMethods: ['GET'] + headers: + Authorization: ${SENTRY_TOKEN} - '/ilert': - target: https://api.ilert.com - allowedMethods: ['GET', 'POST', 'PUT'] - allowedHeaders: ['Authorization'] - headers: - Authorization: ${ILERT_AUTH_HEADER} + '/ilert': + target: https://api.ilert.com + allowedMethods: ['GET', 'POST', 'PUT'] + allowedHeaders: ['Authorization'] + headers: + Authorization: ${ILERT_AUTH_HEADER} - '/airflow': - target: https://your.airflow.instance.com/api/v1 - headers: - Authorization: ${AIRFLOW_BASIC_AUTH_HEADER} + '/airflow': + target: https://your.airflow.instance.com/api/v1 + headers: + Authorization: ${AIRFLOW_BASIC_AUTH_HEADER} - '/gocd': - target: https://your.gocd.instance.com/go/api - allowedMethods: ['GET'] - allowedHeaders: ['Authorization'] - headers: - Authorization: Basic ${GOCD_AUTH_CREDENTIALS} + '/gocd': + target: https://your.gocd.instance.com/go/api + allowedMethods: ['GET'] + allowedHeaders: ['Authorization'] + headers: + Authorization: Basic ${GOCD_AUTH_CREDENTIALS} - '/dynatrace': - target: https://your.dynatrace.instance.com/api/v2 - headers: - Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' + '/dynatrace': + target: https://your.dynatrace.instance.com/api/v2 + headers: + Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' - '/stackstorm': - target: https://your.stackstorm.instance.com/api - headers: - St2-Api-Key: ${ST2_API_KEY} + '/stackstorm': + target: https://your.stackstorm.instance.com/api + headers: + St2-Api-Key: ${ST2_API_KEY} - '/puppetdb': - target: https://your.puppetdb.instance.com + '/puppetdb': + target: https://your.puppetdb.instance.com organization: name: My Company diff --git a/docs/accessibility/index.md b/docs/accessibility/index.md index 24685b95ce..b4d63fd4e4 100644 --- a/docs/accessibility/index.md +++ b/docs/accessibility/index.md @@ -15,35 +15,35 @@ There are multiple ways to contribute to making Backstage more accessible, you'l If your plugin lives in the [Backstage main repository](https://github.com/backstage/backstage/) you can modify the [urls in the Lighthouse config](https://github.com/backstage/backstage/blob/39ba2284d73885b7ca8290cb38e2b1e4d983c8d6/lighthouserc.js#L19-L34) to run the Lighthouse checks on urls where your plugin exists as well. E.g. ```diff - ci: { - collect: { - url: [ + ci: { + collect: { + url: [ + /** Software Catalog */ + 'http://localhost:3000/catalog', + 'http://localhost:3000/catalog-import', + 'http://localhost:3000/catalog/default/component/backstage', + ... + /** Your plugin paths */ + 'http://localhost:3000/your-plugin-path, - /** Software Catalog */ - 'http://localhost:3000/catalog', - 'http://localhost:3000/catalog-import', - 'http://localhost:3000/catalog/default/component/backstage', + ], + settings: { ... - ], - settings: { - ... - }, - ... - }, - assert: { + }, ... - }, - }, + }, + assert: { + ... + }, + }, ``` -To make sure the [Accessibility Github workflow](https://github.com/backstage/backstage/blob/master/.github/workflows/verify_accessibility.yml) is running when changes are made to your plugin folders, modify the [list of paths](https://github.com/backstage/backstage/blob/10759b6ad2561bd86183ad940256f9a309c7a6b0/.github/workflows/verify_accessibility.yml#L7-L16). +To make sure the [Accessibility GitHub workflow](https://github.com/backstage/backstage/blob/master/.github/workflows/verify_accessibility.yml) is running when changes are made to your plugin folders, modify the [list of paths](https://github.com/backstage/backstage/blob/10759b6ad2561bd86183ad940256f9a309c7a6b0/.github/workflows/verify_accessibility.yml#L7-L16). ### Run the Lighthouse CLI locally when developing new features If you want to use the Lighthouse CLI and run the checks based on the config you can use the following command: -``` +```shell yarn dlx @lhci/cli@0.11.x autorun ``` diff --git a/docs/auth/auth0/provider.md b/docs/auth/auth0/provider.md index e9393cfa0b..dcd02d226f 100644 --- a/docs/auth/auth0/provider.md +++ b/docs/auth/auth0/provider.md @@ -41,14 +41,18 @@ auth: secret: ${AUTH_SESSION_SECRET} ``` -The Auth0 provider is a structure with three configuration keys: +The Auth0 provider is a structure with these configuration keys: - `clientId`: The Application client ID, found on the Auth0 Application page - `clientSecret`: The Application client secret, found on the Auth0 Application page - `domain`: The Application domain, found on the Auth0 Application page -Because Auth0 requires a session you need to give the session a secret key. +It additionally relies on the following configuration to function: + +- `session.secret`: The session secret is a key used for signing and/or encrypting cookies set by the application to maintain session state. In this case, 'your session secret' should be replaced with a long, complex, and unique string that only your application knows. + +Auth0 requires a session, so you need to give the session a secret key. ## Optional Configuration diff --git a/docs/auth/oauth2-proxy/provider.md b/docs/auth/oauth2-proxy/provider.md index 9c8f681138..5cff655c02 100644 --- a/docs/auth/oauth2-proxy/provider.md +++ b/docs/auth/oauth2-proxy/provider.md @@ -20,8 +20,10 @@ The provider configuration can be added to your `app-config.yaml` under the root ```yaml title="app-config.yaml" auth: + environment: development providers: - oauth2Proxy: {} + oauth2Proxy: + development: {} ``` Right now no configuration options are supported, but the empty object is needed @@ -58,6 +60,8 @@ providerFactories: { }, ``` +[An example on how to sign a user in without a matching user](https://github.com/backstage/backstage/blob/master/packages/backend/src/plugins/auth.ts) + ## Adding the provider to the Backstage frontend It is recommended to use the `ProxiedSignInPage` for this provider, which is diff --git a/docs/backend-system/building-backends/01-index.md b/docs/backend-system/building-backends/01-index.md index 0f23ae0006..d4b463af41 100644 --- a/docs/backend-system/building-backends/01-index.md +++ b/docs/backend-system/building-backends/01-index.md @@ -61,11 +61,11 @@ All of these services can be replaced with your own implementations if you need For example, let's say we want to customize the core configuration service to enable remote configuration loading. That would look something like this: ```ts -import { configServiceFactory } from '@backstage/backend-app-api'; +import { rootConfigServiceFactory } from '@backstage/backend-app-api'; const backend = createBackend({ services: [ - configServiceFactory({ + rootConfigServiceFactory({ remote: { reloadIntervalSeconds: 60 }, }), ], 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 7f93fab45b..fdd8cdb1e3 100644 --- a/docs/backend-system/building-plugins-and-modules/02-testing.md +++ b/docs/backend-system/building-plugins-and-modules/02-testing.md @@ -37,7 +37,7 @@ describe('myPlugin', () => { const { server } = await startTestBackend({ features: [myPlugin()], - services: [mockServices.config.factory({ data: fakeConfig })], + services: [mockServices.rootConfig.factory({ data: fakeConfig })], }); const response = await request(server).get('/api/example/get-value'); diff --git a/docs/backend-system/core-services/01-index.md b/docs/backend-system/core-services/01-index.md index c688055c96..a51db3c993 100644 --- a/docs/backend-system/core-services/01-index.md +++ b/docs/backend-system/core-services/01-index.md @@ -183,11 +183,11 @@ There's additional configuration that you can optionally pass to setup the `conf You can configure these additional options by adding an override for the core service when calling `createBackend` like follows: ```ts -import { configServiceFactory } from '@backstage/backend-app-api'; +import { rootConfigServiceFactory } from '@backstage/backend-app-api'; const backend = createBackend({ services: [ - configServiceFactory({ + rootConfigServiceFactory({ argv: [ '--config', '/backstage/app-config.development.yaml', diff --git a/docs/deployment/flightcontrol.md b/docs/deployment/flightcontrol.md new file mode 100644 index 0000000000..482e9341ea --- /dev/null +++ b/docs/deployment/flightcontrol.md @@ -0,0 +1,78 @@ +--- +id: flightcontrol +title: Deploying with Flightcontrol +sidebar_label: AWS Fargate via Flightcontrol +description: Deploying Backstage to AWS Fargate via Flightcontrol +--- + +This guide explains how to deploy Backstage to [Flightcontrol](https://www.flightcontrol.dev?ref=backstage), a platform that fully automates deployments to Amazon Web Services (AWS). Flightcontrol supports git-driven and image registry deployments. + +Before you begin, make sure you have a [Flightcontrol account](https://app.flightcontrol.dev/signup?ref=backstage) and a [Github account](https://github.com/login) to follow this guide. + +# Deployment Via Dashboard + +1. Create a new project from the Flightcontrol Dashboard + +2. Select the GitHub repo for your Backstage project + +3. Select `GUI` as the config type: + +4. Then, choose `+ Add Web Server (Fargate)` under Services before entering the following server information: + +| Field Name | Value | +| ----------------- | ----------------- | +| Build Type | Custom Dockerfile | +| Health Check Path | /catalog | +| Port | 7007 | + +5. Click `Create Project` and complete any required steps (like linking your AWS account). + +# Deployment via Code + +1. Create a new project from the Flightcontrol Dashboard + +2. Select the GitHub repo for your Backstage project + +3. Select the `flightcontrol.json` Config Type. + +```json filename="flightcontrol.json" +{ + "$schema": "https://app.flightcontrol.dev/schema.json", + "environments": [ + { + "id": "backstage", + "name": "Backstage", + "region": "us-west-2", + "source": { + "branch": "main" + }, + "services": [ + { + "id": "backstage", + "name": "Backstage", + "type": "fargate", + "buildType": "docker", + "dockerfilePath": "Dockerfile", + "dockerContext": ".", + "healthCheckPath": "/catalog", + "cpu": 0.5, + "memory": 1, + "domain": "backstage.yourapp.com", + "port": 7007, + "minInstances": 1, + "maxInstances": 1 + } + ] + } + ] +} +``` + +# Databases and Redis + +If you need a database or Redis for your Backstage plugins, you can easily add those to your Flightcontrol deployment. For more information, see [the flightcontrol docs](https://www.flightcontrol.dev/docs/guides/flightcontrol/using-code?ref=backstage#redis). + +## Troubleshooting + +- [Flightcontrol Documentation](https://www.flightcontrol.dev/docs?ref=backstage) +- [Troubleshooting](https://www.flightcontrol.dev/docs/troubleshooting?ref=backstage) diff --git a/docs/features/software-catalog/creating-the-catalog-graph.md b/docs/features/software-catalog/creating-the-catalog-graph.md index 8b0f21a52c..4dc5b461b2 100644 --- a/docs/features/software-catalog/creating-the-catalog-graph.md +++ b/docs/features/software-catalog/creating-the-catalog-graph.md @@ -64,7 +64,7 @@ The recommended approach would be to represent information in catalog-info files **Naming strategies:** -- Ldap: Internal ldap usernames as entity names. e.g., owner: user:myuser or user: my-team-name. +- Ldap: Internal ldap usernames as entity names. e.g., owner: user:my-user or user: my-team-name. **Ownership strategies:** diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 0be2bf0292..924c59a6fa 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -978,9 +978,9 @@ way. The entries of this array are [entity references](https://backstage.io/docs/features/software-catalog/references). -| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | -| --------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------- | -| [`Group`](#kind-group) (default) | Same as this entity, typically `default` | [`hasMember`, and reverse `memberOf`](well-known-relations.md#memberof-and-hasmember) | +| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type | +| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------- | +| [`Group`](#kind-group) (default) | Same as this entity, typically `default` | [`parentOf`, and reverse `childOf`](well-known-relations.md#parentof-and-childof) | ### `spec.members` [optional] diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index c2f14d0ca0..929ba150d2 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -207,5 +207,9 @@ scaffolder backend: | Azure Pipeline Actions | [scaffolder-backend-module-azure-pipelines](https://www.npmjs.com/package/@parfuemerie-douglas/scaffolder-backend-module-azure-pipelines) | [Parfümerie Douglas](https://github.com/Parfuemerie-Douglas) | | Azure Repository Actions | [scaffolder-backend-module-azure-repositories](https://www.npmjs.com/package/@parfuemerie-douglas/scaffolder-backend-module-azure-repositories) | [Parfümerie Douglas](https://github.com/Parfuemerie-Douglas) | | Snyk Import Project | [plugin-scaffolder-backend-module-snyk](https://www.npmjs.com/package/@ma11hewthomas/plugin-scaffolder-backend-module-snyk) | [Matthew Thomas](https://github.com/Ma11hewThomas) | +| JSON Merge Actions | [plugin-scaffolder-json-merge-actions](https://www.npmjs.com/package/@mdude2314/backstage-plugin-scaffolder-json-merge-actions) | [Drew Hill](https://github.com/arhill05) | +| NPM Actions | [plugin-scaffolder-npm-actions](https://www.npmjs.com/package/@mdude2314/backstage-plugin-scaffolder-npm-actions) | [Drew Hill](https://github.com/arhill05) | +| Slack Actions | [plugin-scaffolder-backend-module-slack](https://www.npmjs.com/package/@mdude2314/backstage-plugin-scaffolder-backend-module-slack) | [Drew Hill](https://github.com/arhill05) | +| Microsoft Teams Actions | [plugin-scaffolder-backend-module-ms-teams](https://www.npmjs.com/package/@grvpandey11/backstage-plugin-scaffolder-backend-module-ms-teams) | [Gaurav Pandey](https://github.com/grvpandey11) | Have fun! 🚀 diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index 20575a0b4a..f22cecd22c 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -89,10 +89,8 @@ export const validateKebabCaseValidation = ( then please use `scaffolderPlugin.provide` from there instead and export it part of your `plugin.ts` rather than re-using the `scaffolder.plugin`. */ -import { - scaffolderPlugin, - createScaffolderFieldExtension, -} from '@backstage/plugin-scaffolder'; +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; import { ValidateKebabCase, validateKebabCaseValidation, @@ -133,7 +131,7 @@ Should look something like this instead: ```tsx import { ValidateKebabCaseFieldExtension } from './scaffolder/ValidateKebabCase'; -import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder'; +import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder-react'; const routes = ( diff --git a/docs/features/software-templates/writing-custom-step-layouts.md b/docs/features/software-templates/writing-custom-step-layouts.md index 69d61b93dc..b965f60f6b 100644 --- a/docs/features/software-templates/writing-custom-step-layouts.md +++ b/docs/features/software-templates/writing-custom-step-layouts.md @@ -4,7 +4,7 @@ title: Writing custom step layouts description: How to override the default step form layout --- -Every form in each step rendered in the frontend uses the default form layout from [react-json-schema-form](https://react-jsonschema-form.readthedocs.io/). It is possible to override this behaviour by supplying a `ui:ObjectFieldTemplate` property for a particular step: +Every form in each step rendered in the frontend uses the default form layout from [react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/docs/). It is possible to override this behaviour by supplying a `ui:ObjectFieldTemplate` property for a particular step: ```yaml parameters: @@ -12,7 +12,7 @@ parameters: ui:ObjectFieldTemplate: TwoColumn ``` -This is the same [field](https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/custom-templates/#objectfieldtemplate) used by [react-json-schema-form](https://react-jsonschema-form.readthedocs.io/) but we need to add a couple of steps to ensure that the string value of `TwoColumn` above is resolved to a react component. +This is the same [field](https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-templates#objectfieldtemplate) used by [react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/docs/) but we need to add a couple of steps to ensure that the string value of `TwoColumn` above is resolved to a react component. ## Registering a React component as a custom step layout diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 6e7b66fcc9..135d508513 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -499,6 +499,7 @@ template. These follow the same standard format: - id: fetch-base # A unique id for the step name: Fetch Base # A title displayed in the frontend if: ${{ parameters.name }} # Optional condition, skip the step if not truthy + each: ${{ parameters.iterable }} # Optional iterable, run the same step multiple times action: fetch:template # An action to call input: # Input that is passed as arguments to the action handler url: ./template @@ -510,6 +511,27 @@ By default we ship some [built in actions](./builtin-actions.md) that you can take a look at, or you can [create your own custom actions](./writing-custom-actions.md). +When `each` is provided, the current iteration value is available in the `${{ each }}` input. + +Examples: + +```yaml +each: ['apples', 'oranges'] +input: + values: + fruit: ${{ each.value }} +``` + +```yaml +each: [{ name: 'apple', count: 3 }, { name: 'orange', count: 1 }] +input: + values: + fruit: ${{ each.value.name }} + count: ${{ each.value.count }} +``` + +When `each` is used, the outputs of a repeated step are returned as an array of outputs from each iteration. + ## Outputs Each individual step can output some variables that can be used in the diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index ab554db3bd..b78a1a55af 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -188,15 +188,12 @@ import { } from '@backstage/plugin-techdocs'; import { Entity } from '@backstage/catalog-model'; -import { - EntityListDocsGrid, - DocsGroupConfig, -} from '@backstage/plugin-techdocs'; +import { EntityListDocsGrid } from '@backstage/plugin-techdocs'; export type CustomTechDocsHomeProps = { groups?: Array<{ title: React.ReactNode; - filterPredicate: (entity: Entity) => boolean; + filterPredicate: ((entity: Entity) => boolean) | string; }>; }; diff --git a/docs/overview/glossary.md b/docs/overview/glossary.md index 97a2823365..ab7d224f4d 100644 --- a/docs/overview/glossary.md +++ b/docs/overview/glossary.md @@ -2,25 +2,25 @@ id: glossary title: Backstage Glossary # prettier-ignore -description: List of all the terms, abbreviations, and phrases used in Backstage, together with their explanations. +description: List of terms, abbreviations, and phrases used in Backstage, together with their explanations. --- -The Backstage Glossary lists all the terms, abbreviations, and phrases used in +The Backstage Glossary lists terms, abbreviations, and phrases used in Backstage, together with their explanations. We encourage you to use the terminology below for clarity and consistency when discussing Backstage. -### Authentication Glossary - -This [page](../auth/glossary.md) directs to the terms and phrases related to -authentication and identity section of Backstage. +See also [Authentication Glossary](../auth/glossary.md), a separate glossary of terms and phrases +specifically related to the authentication and identity section of Backstage. ### Backstage User Profiles There are three main user profiles for Backstage: the integrator, the -contributor, and the software engineer. +contributor, and the end user (typically a software engineer). -| Term | Explanation | -| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Integrator | The **integrator** hosts the Backstage app and configures which plugins are available to use in the app. | -| Contributor | The **contributor** adds functionality to the app by writing plugins. | -| Software Engineer | The **software engineer** uses the app's functionality and interacts with its plugins. In practice, this profile covers the various roles that help deliver software, from the Software Engineer themselves, to Designers, Data Scientists, Product Owners, Engineering Managers, etc. | +| Term | Explanation | +| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Integrator | The **integrator** hosts the Backstage app and configures which plugins are available to use in the app. | +| Contributor | The **contributor** adds functionality to the app by writing plugins. | +| End user | The **end user** uses the app's functionality and interacts with its plugins. This profile covers the various roles that help deliver software. The typical end user is a **software engineer**, but users might also consider themselves _designers_, _data scientists_, _product owners_, _engineering managers_, _technical writers_, and so on. | +| Software engineer | The **software engineer** is an **end user** who uses the app's functionality and interacts with its plugins in the course of writing and documenting code. This user is more likely to embed documentation in the code files they produce, and create rough drafts of conceptual pages in collaboration with a **technical writer** or _technical editor_. | +| Technical writer | The **technical writer** is an **end user** who uses the app's functionality and interacts with its plugins in the course of writing and editing documentation. This user is more likely to produce and customize templates and produce conceptual pages to supplement documentation embedded in code files. | diff --git a/docs/plugins/proxying.md b/docs/plugins/proxying.md index 3e20e37cad..442721a355 100644 --- a/docs/plugins/proxying.md +++ b/docs/plugins/proxying.md @@ -36,7 +36,7 @@ Example: ```yaml # in app-config.yaml proxy: - simple-example: http://simple.example.com:8080 + /simple-example: http://simple.example.com:8080 '/larger-example/v1': target: http://larger.example.com:8080/svc.v1 headers: diff --git a/docs/releases/v1.16.0.md b/docs/releases/v1.16.0.md index 5435d15994..37a458cc32 100644 --- a/docs/releases/v1.16.0.md +++ b/docs/releases/v1.16.0.md @@ -84,4 +84,4 @@ Below you can find a list of links and references to help you learn about and st - [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.16.0-changelog.md) - Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap) and [Plugins](https://backstage.io/plugins) -Sign up for our [newsletter](https://mailchi.mp/spotify/backstage-community) if you want to be informed about what is happening in the world of Backstage. +Sign up for our [newsletter](https://info.backstage.spotify.com/newsletter_subscribe) if you want to be informed about what is happening in the world of Backstage. diff --git a/docs/releases/v1.17.0-changelog.md b/docs/releases/v1.17.0-changelog.md new file mode 100644 index 0000000000..a67226ca9e --- /dev/null +++ b/docs/releases/v1.17.0-changelog.md @@ -0,0 +1,2999 @@ +# Release v1.17.0 + +## @backstage/backend-app-api@0.5.0 + +### Minor Changes + +- b9c57a4f857e: **BREAKING**: Renamed `configServiceFactory` to `rootConfigServiceFactory`. +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +### Patch Changes + +- e65c4896f755: Do not throw in backend.stop, if start failed +- c7aa4ff1793c: Allow modules to register extension points. +- 57a10c6c69cc: Add validation to make sure that extension points do not cross plugin boundaries. +- cc9256a33bcc: Added new experimental `featureDiscoveryServiceFactory`, available as an `/alpha` export. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config-loader@1.4.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/cli-node@0.1.3 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/backend-defaults@0.2.0 + +### Minor Changes + +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-app-api@0.5.0 + - @backstage/backend-plugin-api@0.6.0 + +## @backstage/backend-plugin-api@0.6.0 + +### Minor Changes + +- c49785f00cab: **BREAKING**: It is no longer possible to declare options as being required with `createServiceFactory`. +- 629cbd194a87: **BREAKING**: Renamed `coreServices.config` to `coreServices.rootConfig`. +- 51987dbdaf87: **BREAKING**: Removed the ability to define options for plugins and modules. Existing options should be migrated to instead use either static configuration or extension points. +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. + +### Patch Changes + +- c7aa4ff1793c: Allow modules to register extension points. + +- cc9256a33bcc: Added new experimental `featureDiscoveryServiceRef`, available as an `/alpha` export. + +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +- Updated dependencies + - @backstage/plugin-auth-node@0.2.17 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/backend-test-utils@0.2.0 + +### Minor Changes + +- b9c57a4f857e: **BREAKING**: Renamed `mockServices.config` to `mockServices.rootConfig`. +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +### Patch Changes + +- ae9304818136: Add needed constants and constructs to support PostgreSQL version 14 as test database +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-app-api@0.5.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/config-loader@1.4.0 + +### Minor Changes + +- 2f1859585998: Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. + + In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. + +- cd514545d1d0: Adds a new `deepVisibility` schema keyword that sets child visibility recursively to the defined value, respecting preexisting values or child `deepVisibility`. + + Example usage: + + ```ts + export interface Config { + /** + * Enforces a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + secretValue: { + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + + Example of a schema that would not be allowed: + + ```ts + export interface Config { + /** + * Set the top level property to secret, enforcing a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + frontendUrl: { + /** + * We can NOT override the visibility to reveal a property to the front end. + * @visibility frontend + */ + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/integration@1.6.0 + +### Minor Changes + +- 443afcf7f567: Added `buildGerritGitilesArchiveUrl()` to construct a Gitiles URL to download an archive. + Gitiles URL that uses an authenticated prefix (`/a/`) can now be parsed by the integration. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-app-node@0.1.0 + +### Minor Changes + +- 9fbe95ef6503: Added the `app` plugin node library, initially providing an extension point that can be used to configure a static fallback handler. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + +## @backstage/plugin-catalog-backend@1.12.0 + +### Minor Changes + +- b8cccd8ee858: Support configuring applicable kinds for `AnnotateScmSlugEntityProcessor` +- f32252cdf631: Added OpenTelemetry spans for catalog processing +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- b8d6b22acd57: Internal refactor for load test +- Updated dependencies + - @backstage/plugin-search-backend-module-catalog@0.1.4 + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-openapi-utils@0.0.3 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.0 + +### Minor Changes + +- 290eff6692aa: Added GCP catalog plugin with GKE provider + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/plugin-kubernetes-common@0.6.5 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.2.0 + +### Minor Changes + +- 5156a94c2e2a: **BREAKING**: Fixing typo in exported module. You will have to rename the import to the correct spelling. `UnprocessedEntites` -> `UnprocessedEntities` + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-model@1.4.1 + +## @backstage/plugin-kubernetes@0.10.0 + +### Minor Changes + +- 4b6d41737aad: **BREAKING**: Fixed typos in exported module attributes. Many users may see no changes, but if you have customized the plugin output, you may have to rename the following: + + - Component attribute: `` to `` + - Interface attribute: `DetectedError.occuranceCount` to `DetectedError.occurrenceCount`. + +### Patch Changes + +- 562e34d2693f: Fix container memory limits +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-kubernetes-common@0.6.5 + +## @backstage/plugin-linguist-backend@0.4.0 + +### Minor Changes + +- d440f1dd0e72: Adds a processor to the linguist backend which can automatically add language tags to entities +- 0896d85d5ab5: **BREAKING**: Removed the alpha export plugin options from exported `linguistPlugin()` (used by the new backend system) in favour of static config. + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- ca5e591cb86a: Fixed bug in LinguistBackendClient.ts file where if the linguistJsOptions is specified and sent over to the linguist-js package it would get changed (another attribute would be added) causing future entities of the batch to fail with an error +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-linguist-common@0.1.1 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-proxy-backend@0.3.0 + +### Minor Changes + +- 7daf65bfcfa1: Defining proxy endpoints directly under the root `proxy` configuration key is deprecated. Endpoints should now be declared under `proxy.endpoints` instead. The `skipInvalidProxies` and `reviveConsumedRequestBodies` can now also be configured through static configuration. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + +## @backstage/plugin-scaffolder-backend@1.16.0 + +### Minor Changes + +- e514aac3eac0: Introduce `each` property on action steps, allowing them to be ran repeatedly. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` + +- 349611126ae2: Removed the options from the alpha `scaffolderPlugin` export. To extend the scaffolder plugin you instead now use the available extension points, `scaffolderActionsExtensionPoint`, `scaffolderTaskBrokerExtensionPoint`, and `scaffolderTemplatingExtensionPoint`. + +- 33c76caef72a: Added examples for the `fs:delete` and `fs:rename` actions + +- e07a4914f621: Deprecated the following type exports, which have been moved to `@backstage/plugin-scaffolder-node` instead: + + - `TemplateFilter` + - `TemplateGlobal` + - `TaskStatus` + - `TaskCompletionState` + - `SerializedTask` + - `TaskEventType` + - `SerializedTaskEvent` + - `TaskBrokerDispatchResult` + - `TaskBrokerDispatchOptions` + - `TaskContext` + - `TaskBroker` + +- 0b1d775be05b: Adds examples to a few scaffolder actions. + +- 88bc6e27a213: The `concurrentTasksLimit` option can now be configured via static configuration as well. Setting it to 0 will now also disable the task worker. + +- 0f873325068d: Add examples for `github:repo:create` and `github:repo:push` scaffolder actions. + +- 5c28ebc79fd6: Updated dependency `esbuild` to `^0.19.0`. + +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-scaffolder-common@1.4.0 + +### Minor Changes + +- e514aac3eac0: Introduce `each` property on action steps, allowing them to be ran repeatedly. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-scaffolder-node@0.2.0 + +### Minor Changes + +- e514aac3eac0: Introduce `each` property on action steps, allowing them to be ran repeatedly. + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- e07a4914f621: Added several new types that were moved from `@backstage/plugin-scaffolder-backend`. +- 349611126ae2: Added two new alpha extension points, `scaffolderTaskBrokerExtensionPoint` and `scaffolderTemplatingExtensionPoint`. +- 0b1d775be05b: Export `TemplateExample` from the `createTemplateAction` type. +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-search-backend@1.4.0 + +### Minor Changes + +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 951ab6c9db58: Add missing `configSchema` to package.json +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-openapi-utils@0.0.3 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-todo-backend@0.2.0 + +### Minor Changes + +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- ad3fb5401729: Fixed a bug where filter queries from the frontend would always fail validation. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-openapi-utils@0.0.3 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/app-defaults@1.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/backend-common@0.19.2 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` + +- 443afcf7f567: To improve performance, `GerritUrlReader.readTree()` now uses Gitiles to fetch an archive instead of cloning the repository. + If `gitilesBaseUrl` is not configured, `readTree` still uses Git to clone the repository. + + Added `stripFirstDirectory` option to `ReadTreeResponseFactory.fromTarArchive()`, allowing to disable stripping first directory + for `tar` archives. + +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. + +- Updated dependencies + - @backstage/backend-app-api@0.5.0 + - @backstage/config-loader@1.4.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/integration-aws-node@0.1.5 + - @backstage/backend-dev-utils@0.1.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/backend-openapi-utils@0.0.3 + +### Patch Changes + +- ebeb77586975: Add a new `createRouter` method for generating an `express` router that validates against your spec. Also fixes a bug with the query parameters type resolution. +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/errors@1.2.1 + +## @backstage/backend-tasks@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 + +## @backstage/cli@0.22.10 + +### Patch Changes + +- 3f67cefb4780: Reload the frontend when app config changes +- cebbf8a27f3c: Enable to print the config schema not merged with the `--no-merge` flag +- 5c28ebc79fd6: Updated dependency `esbuild` to `^0.19.0`. +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/config-loader@1.4.0 + - @backstage/cli-node@0.1.3 + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/eslint-plugin@0.1.3 + - @backstage/release-manifests@0.0.9 + - @backstage/types@1.1.0 + +## @backstage/cli-node@0.1.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/core-app-api@1.9.1 + +### Patch Changes + +- 9ae4e7e63836: Fixed a bug that could cause `navigate` analytics events to be misattributed to the plugin mounted on the root route (e.g. the `home` plugin at `/`) when the route that was navigated to wasn't associated with a routable extension. +- Updated dependencies + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + +## @backstage/core-components@0.13.4 + +### Patch Changes + +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- Updated dependencies + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/version-bridge@1.0.4 + +## @backstage/create-app@0.5.4 + +### Patch Changes + +- b441642fbe0d: Bumped create-app version. +- 572abc7edf55: Bumped create-app version. +- 74f77f151a96: Bumped create-app version. +- 5cc0ac5ef3d1: Bump to a newer version of the `concurrently` library +- 46c9a798e41d: Updated the `app-config.yaml` template to use `proxy.endpoints`. +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/cli-common@0.1.12 + +## @backstage/dev-utils@1.0.18 + +### Patch Changes + +- 254ad469f053: Removed deprecated calls to `app.getProvider()` and `app.getRouter()` in `DevAppBuilder` +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/app-defaults@1.4.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/integration-react@1.1.16 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/repo-tools@0.3.3 + +### Patch Changes + +- 75702e85862a: Bumped `@microsoft/api-extractor` dependency to `^7.36.4`, and `@microsoft/api-documenter` to `^7.22.33`. +- 1f3337ebc707: Introducing a new, experimental command `backstage-repo-tools generate-catalog-info`, which can be used to create standardized `catalog-info.yaml` files for each Backstage package in a Backstage monorepo. It can also be used to automatically fix existing `catalog-info.yaml` files with the correct metadata (including `metadata.name`, `metadata.title`, and `metadata.description` introspected from the package's `package.json`, as well as `spec.owner` introspected from `CODEOWNERS`), e.g. in a post-commit hook. +- ebeb77586975: Update `schema openapi generate` command to now create a default router that can be imported and used directly. +- Updated dependencies + - @backstage/cli-node@0.1.3 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## @techdocs/cli@1.4.5 + +### Patch Changes + +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## @backstage/test-utils@1.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/plugin-adr@0.6.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-adr-common@0.2.12 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-adr-backend@0.3.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-adr-common@0.2.12 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-adr-common@0.2.12 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-airbrake@0.3.21 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/dev-utils@1.0.18 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-airbrake-backend@0.2.21 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + +## @backstage/plugin-allure@0.1.37 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-analytics-module-ga@0.1.32 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/plugin-analytics-module-ga4@0.1.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/plugin-analytics-module-newrelic-browser@0.0.1 + +### Patch Changes + +- ec7357258853: Introduced the New Relic Browser analytics module. Check out the plugins [README.md](https://github.com/backstage/backstage/tree/master/plugins/analytics-module-newrelic-browser) for more details! +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + +## @backstage/plugin-apache-airflow@0.2.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + +## @backstage/plugin-api-docs@0.9.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-apollo-explorer@0.1.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-app-backend@0.3.48 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- d564ad142b17: Migrated the alpha `appBackend` export to use static configuration and extension points rather than accepting options. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config-loader@1.4.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-app-node@0.1.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/plugin-auth-backend@0.18.6 + +### Patch Changes + +- 16452cd007ae: Updated `frameHandler` to return `undefined` when using the redirect flow instead of returning `postMessageReponse` which was causing errors +- 9dad4b0e61bd: Updated config schema to match what was being used in code +- bb70a9c3886a: Add frontend visibility to provider objects in `auth` config. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-auth-node@0.2.17 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-azure-devops@0.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-azure-devops-backend@0.3.27 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-azure-sites@0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-azure-sites-common@0.1.0 + +## @backstage/plugin-azure-sites-backend@0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + +## @backstage/plugin-badges@0.2.45 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-badges-backend@0.2.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-bazaar@0.2.12 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli@0.22.10 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-bazaar-backend@0.2.11 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-bitbucket-cloud-common@0.2.9 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.6.0 + +## @backstage/plugin-bitrise@0.1.48 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-catalog@1.12.1 + +### Patch Changes + +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- 136cea792bd4: Fixed an issue causing `EntitySwitch` to unmount its children once entity refresh was invoked +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-catalog-backend-module-aws@0.2.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- b4222908b0c3: Added option to configure AWS `accountId` in `AwsS3EntityProvider` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-kubernetes-common@0.6.5 + +## @backstage/plugin-catalog-backend-module-azure@0.1.19 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.15 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.9 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.15 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.9 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.13 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.16 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-catalog-backend-module-github@0.3.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- 81c231c9c9ee: Fixed a bug where the visibility filter was case sensitive and casting was inconsistent. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-gitlab@0.2.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- e6c721439f37: Added option to skip forked repos in GitlabDiscoveryEntityProvider +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- 2fe1f5973ff7: Filter Gitlab archived projects through APIs +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- e2b6396a1274: Export new alpha `incrementalIngestionProvidersExtensionPoint` for registering incremental providers, rather than the providers being passed as options to the backend module. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.15 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.7 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- fb93323201bf: The alpha `catalogModuleMicrosoftGraphOrgEntityProvider` export no longer accepts options. Transformers are now instead configured via the `microsoftGraphOrgEntityProviderTransformExtensionPoint`. +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.14 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.5 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-graph@0.2.33 + +### Patch Changes + +- 64ee2c0c7ca5: Propagate entity spec to EntityNode so that spec info such as type can be used for graph node customization +- 62dc7a2b1ad1: Added maximum depth parameter to the catalogGraphParams in CatalogGraphCard. +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-import@0.9.11 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-node@1.4.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-react@1.8.1 + +### Patch Changes + +- aa3feedce10a: Allow specifying screen size when catalog filters are hidden in drawer +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/plugin-catalog-unprocessed-entities@0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-cicd-statistics@0.1.23 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.17 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-cicd-statistics@0.1.23 + +## @backstage/plugin-circleci@0.3.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-cloudbuild@0.3.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-code-climate@0.1.21 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-code-coverage@0.2.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-code-coverage-backend@0.2.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/integration@1.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-codescene@0.1.16 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-config-schema@0.1.44 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-cost-insights@0.12.10 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-cost-insights-common@0.1.1 + +## @backstage/plugin-devtools@0.1.3 + +### Patch Changes + +- 48b6a04ad045: Fix readme typo +- Updated dependencies + - @backstage/plugin-devtools-common@0.1.3 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/plugin-devtools-backend@0.1.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` + +- 12a8c94eda8d: Add package repository and homepage metadata + +- 2b4f77a4e900: Add DevTools configuration to enable dependency listing to be filtered with custom prefixes. For instance, in your `app-config.yaml`: + + ```yaml + devTools: + info: + packagePrefixes: + - @backstage/ + - @roadiehq/backstage- + - @spotify/backstage- + ``` + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config-loader@1.4.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-devtools-common@0.1.3 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-devtools-common@0.1.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-dynatrace@7.0.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-entity-feedback@0.2.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-entity-feedback-common@0.1.2 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-entity-feedback-backend@0.1.6 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-entity-feedback-common@0.1.2 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-entity-feedback-common@0.1.2 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata + +## @backstage/plugin-entity-validation@0.1.6 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-events-backend@0.2.9 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + - @backstage/config@1.0.8 + +## @backstage/plugin-events-backend-module-aws-sqs@0.2.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/plugin-events-backend-module-azure@0.1.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.1.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + +## @backstage/plugin-events-backend-module-gerrit@0.1.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + +## @backstage/plugin-events-backend-module-github@0.1.10 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + - @backstage/config@1.0.8 + +## @backstage/plugin-events-backend-module-gitlab@0.1.10 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-events-node@0.2.9 + - @backstage/config@1.0.8 + +## @backstage/plugin-events-backend-test-utils@0.1.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9 + +## @backstage/plugin-events-node@0.2.9 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + +## @backstage/plugin-explore@0.4.7 + +### Patch Changes + +- 41a26248c91c: Support `material-ui` overrides in plugin-explore components +- 83467b0534ee: Don't put "?" in URL if no query parameters. +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/plugin-explore-react@0.0.30 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-explore-backend@0.0.10 + +### Patch Changes + +- eda2a699f40d: Moved the config example from the "Tools as Code" section to the "Tools as Config" section of the README +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-search-backend-module-explore@0.1.4 + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-firehydrant@0.2.5 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-fossa@0.2.53 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-gcalendar@0.3.17 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-gcp-projects@0.3.40 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-git-release-manager@0.3.34 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-github-actions@0.6.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-github-deployments@0.1.52 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-github-issues@0.2.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-github-pull-requests-board@0.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-gitops-profiles@0.3.39 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/plugin-gocd@0.1.27 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-graphiql@0.2.53 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-graphql-backend@0.1.38 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-graphql@0.3.22 + +## @backstage/plugin-graphql-voyager@0.1.6 + +### Patch Changes + +- bb1e1c2b26cc: Fix typo in install instructions. +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-home@0.5.5 + +### Patch Changes + +- a559ff68de7e: Now, user can provide a language (optional) as input to receive a greeting in that specific language. Example: `` +- 6743d3917a52: Make sure the widget name is never empty in the `AddWidgetDialog`. If the title was set to "", the entry would contain an empty string. Use the name as a fallback +- Updated dependencies + - @backstage/plugin-home-react@0.1.2 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/plugin-home-react@0.1.2 + +### Patch Changes + +- bf67dce73174: Make `title` optional when defining the `createCardExtension` +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + +## @backstage/plugin-ilert@0.2.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-jenkins@0.8.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-jenkins-common@0.1.18 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-jenkins-backend@0.2.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-jenkins-common@0.1.18 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-jenkins-common@0.1.18 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-kafka@0.3.21 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/plugin-kafka-backend@0.2.41 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-kubernetes-backend@0.11.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- bbf4e9c894b5: Fixed a bug where the proxy was not rewriting WebSocket request paths properly. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-kubernetes-common@0.6.5 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-lighthouse@0.4.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-lighthouse-common@0.1.2 + +## @backstage/plugin-lighthouse-backend@0.2.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-lighthouse-common@0.1.2 + +## @backstage/plugin-linguist@0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-linguist-common@0.1.1 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-linguist-common@0.1.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- d440f1dd0e72: Exported new LanguageType type alias + +## @backstage/plugin-microsoft-calendar@0.1.6 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- eea2922e749a: README update - example of apiRef definition and fixed component name +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-newrelic@0.3.39 + +### Patch Changes + +- 2167b7eab09b: The newrelic plugin now supports pagination when retrieving results from newrelic. It will no longer truncate results. To see all applications, the link header will need to be allowed through the proxy (see the newrelic plugin readme). +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-newrelic-dashboard@0.2.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + +## @backstage/plugin-nomad@0.1.2 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## @backstage/plugin-nomad-backend@0.1.2 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-octopus-deploy@0.2.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-org@0.6.11 + +### Patch Changes + +- 50331203e2a2: Fixed bug in MembersListCard component where the url for the members was static which was breaking the component when catalog route was changed +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-org-react@0.1.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-pagerduty@0.6.2 + +### Patch Changes + +- 5003fc966741: Add new `disableChangeEvents` prop to `EntityPagerDutyCard` to hide the Change Events tab and disable fetching of change events for the PagerDuty service. +- Updated dependencies + - @backstage/plugin-home-react@0.1.2 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-periskop@0.1.19 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-periskop-backend@0.1.19 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + +## @backstage/plugin-permission-backend@0.5.23 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 0d6ddc62d8da: Refactor backend plugin creation parameter from callback to object. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-permission-node@0.7.11 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-playlist@0.1.13 + +### Patch Changes + +- d1e0588324d1: Displaying an alert popup each time the Playlist is created or deleted +- 12a8c94eda8d: Add package repository and homepage metadata +- 8a997547f02a: Adding alert popup whenever user delete the entities from playlist entity table +- Updated dependencies + - @backstage/plugin-playlist-common@0.1.9 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/plugin-playlist-backend@0.3.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-playlist-common@0.1.9 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-playlist-common@0.1.9 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-puppetdb@0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-rollbar@0.4.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-rollbar-backend@0.1.45 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + +## @backstage/plugin-scaffolder@1.14.2 + +### Patch Changes + +- 8a0490fb669e: Fix the get entities query in the `MyGroupsPicker` to query the `kind=Group` entities. +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/plugin-scaffolder-react@1.5.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1 + +### Patch Changes + +- 0a9b3b14e904: Added example for the `confluence:transform:markdown` that will show in the installed actions list +- 12a8c94eda8d: Add package repository and homepage metadata +- 0d347efc8f18: Use `fetchContents` directly instead of a `fetchPlainAction` +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.24 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.17 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.8 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.21 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-react@1.5.2 + +### Patch Changes + +- ba9ee98a37bd: Fixed bug in Workflow component by passing a prop `templateName` down to Stepper component. +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + +## @backstage/plugin-search@1.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-catalog@0.1.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- 12a8c94eda8d: Add package repository and homepage metadata +- ec1032129e47: Breaking change in the alpha export `searchModuleCatalogCollator`: Moved collator settings from module options into app-config. You are now expected to set up the catalog collator under the `search.collators.catalog` configuration key. There is also a new `catalogCollatorExtensionPoint` extension point for the module, wherein you can set custom transformers. +- d4f19a16bd52: Add User Entity email to the search index so that users can be found by their email. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-elasticsearch@1.3.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` + +- 12a8c94eda8d: Add package repository and homepage metadata + +- 631eb3816b48: Index templates can now be configured through configuration under the `search.elasticsearch.indexTemplates`. In addition, the `ElasticSearchSearchEngine.fromConfig` now also accepts a `LoggerService` as the `logger` option as well as a new `translator` option. + + The alpha `searchModuleElasticsearchEngine` export no longer accepts options and a new alpha `elasticsearchTranslatorExtensionPoint` export has been added which lets you customize the query translator. + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/integration-aws-node@0.1.5 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-explore@0.1.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- 12a8c94eda8d: Add package repository and homepage metadata +- 6694e79ab396: Breaking change for the alpha export moved `schedule` from module options into app-config for the new backend system. You can now pass in a `TaskScheduleDefinitionConfig` through the `search.collators.explore.schedule` configuration key. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-pg@0.5.9 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-techdocs@0.1.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- 12a8c94eda8d: Add package repository and homepage metadata +- 6694e79ab396: **BREAKING:** Moved `schedule` & `collators` settings from module options into app-config for the new backend system. You can now pass in a `TaskScheduleDefinitionConfig` through the `search.collators.techdocs.schedule` configuration key & configure the `TechDocsCollatorFactory` with the key `search.collators.techdocs`. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-node@1.2.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- e3e9bc10298b: Fix Lunr search engine highlight by ignoring invalid metadata positions. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-react@1.6.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-sentry@0.5.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-shortcuts@0.3.13 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- ee52a48b63d5: Limit the use of the duplicate shortcut name when adding a shortcut +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-sonarqube@0.7.2 + +### Patch Changes + +- b2ccddefbdc6: Remove sonarQube card disable class +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-sonarqube-react@0.1.7 + +## @backstage/plugin-sonarqube-backend@0.2.2 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- a24d9df8757c: Added optional `externalBaseUrl` config for setting a different frontend URL +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-splunk-on-call@0.4.10 + +### Patch Changes + +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-stack-overflow@0.1.19 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-home-react@0.1.2 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-stack-overflow-backend@0.2.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-stackstorm@0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-tech-insights@0.3.13 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-insights-backend@0.5.14 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-tech-insights-node@0.4.6 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-tech-insights-node@0.4.6 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-insights-node@0.4.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-radar@0.6.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-techdocs@1.6.6 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-techdocs-react@1.1.9 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.17 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/plugin-search-react@1.6.4 + - @backstage/plugin-techdocs@1.6.6 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9 + +## @backstage/plugin-techdocs-backend@1.6.5 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4 + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.16 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9 + +## @backstage/plugin-techdocs-node@1.7.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/integration@1.6.0 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-techdocs-react@1.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/version-bridge@1.0.4 + +## @backstage/plugin-todo@0.2.23 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-user-settings@0.7.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-user-settings-backend@0.1.12 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-vault@0.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-vault-backend@0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-xcmetrics@0.2.41 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## example-app@0.2.86 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/plugin-catalog-graph@0.2.33 + - @backstage/plugin-playlist@0.1.13 + - @backstage/plugin-pagerduty@0.6.2 + - @backstage/plugin-scaffolder@1.14.2 + - @backstage/plugin-explore@0.4.7 + - @backstage/plugin-org@0.6.11 + - @backstage/integration-react@1.1.16 + - @backstage/plugin-microsoft-calendar@0.1.6 + - @backstage/plugin-newrelic-dashboard@0.2.14 + - @backstage/plugin-entity-feedback@0.2.4 + - @backstage/plugin-linguist-common@0.1.1 + - @backstage/plugin-apache-airflow@0.2.14 + - @backstage/plugin-octopus-deploy@0.2.3 + - @backstage/plugin-stack-overflow@0.1.19 + - @backstage/plugin-code-coverage@0.2.14 + - @backstage/plugin-tech-insights@0.3.13 + - @backstage/plugin-dynatrace@7.0.1 + - @backstage/plugin-gcalendar@0.3.17 + - @backstage/plugin-shortcuts@0.3.13 + - @backstage/plugin-airbrake@0.3.21 + - @backstage/plugin-kafka@0.3.21 + - @backstage/plugin-nomad@0.1.2 + - @backstage/plugin-adr@0.6.4 + - @backstage/plugin-newrelic@0.3.39 + - @backstage/cli@0.22.10 + - @backstage/plugin-kubernetes@0.10.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-home@0.5.5 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/plugin-scaffolder-react@1.5.2 + - @backstage/plugin-devtools@0.1.3 + - @backstage/app-defaults@1.4.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-api-docs@0.9.7 + - @backstage/plugin-azure-devops@0.3.3 + - @backstage/plugin-azure-sites@0.1.10 + - @backstage/plugin-badges@0.2.45 + - @backstage/plugin-catalog-import@0.9.11 + - @backstage/plugin-catalog-unprocessed-entities@0.1.2 + - @backstage/plugin-circleci@0.3.21 + - @backstage/plugin-cloudbuild@0.3.21 + - @backstage/plugin-cost-insights@0.12.10 + - @backstage/plugin-gcp-projects@0.3.40 + - @backstage/plugin-github-actions@0.6.2 + - @backstage/plugin-gocd@0.1.27 + - @backstage/plugin-graphiql@0.2.53 + - @backstage/plugin-jenkins@0.8.3 + - @backstage/plugin-lighthouse@0.4.6 + - @backstage/plugin-linguist@0.1.6 + - @backstage/plugin-puppetdb@0.1.4 + - @backstage/plugin-rollbar@0.4.21 + - @backstage/plugin-search@1.3.4 + - @backstage/plugin-search-react@1.6.4 + - @backstage/plugin-sentry@0.5.6 + - @backstage/plugin-stackstorm@0.1.5 + - @backstage/plugin-tech-radar@0.6.7 + - @backstage/plugin-techdocs@1.6.6 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.16 + - @backstage/plugin-todo@0.2.23 + - @backstage/plugin-user-settings@0.7.6 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-react@0.4.14 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-techdocs-react@1.1.9 + - @internal/plugin-catalog-customized@0.0.13 + +## example-backend@0.2.86 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-elasticsearch@1.3.3 + - @backstage/plugin-search-backend-module-pg@0.5.9 + - @backstage/plugin-azure-devops-backend@0.3.27 + - @backstage/plugin-kubernetes-backend@0.11.3 + - @backstage/plugin-lighthouse-backend@0.2.4 + - @backstage/plugin-permission-backend@0.5.23 + - @backstage/plugin-scaffolder-backend@1.16.0 + - @backstage/plugin-devtools-backend@0.1.3 + - @backstage/plugin-techdocs-backend@1.6.5 + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/plugin-badges-backend@0.2.3 + - @backstage/plugin-events-backend@0.2.9 + - @backstage/plugin-search-backend@1.4.0 + - @backstage/plugin-kafka-backend@0.2.41 + - @backstage/plugin-proxy-backend@0.3.0 + - @backstage/plugin-todo-backend@0.2.0 + - @backstage/plugin-app-backend@0.3.48 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1 + - @backstage/plugin-auth-backend@0.18.6 + - @backstage/plugin-explore-backend@0.0.10 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.17 + - @backstage/plugin-entity-feedback-backend@0.1.6 + - @backstage/plugin-code-coverage-backend@0.2.14 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-linguist-backend@0.4.0 + - @backstage/plugin-playlist-backend@0.3.4 + - @backstage/plugin-jenkins-backend@0.2.3 + - @backstage/plugin-nomad-backend@0.1.2 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - example-app@0.2.86 + - @backstage/plugin-adr-backend@0.3.6 + - @backstage/plugin-azure-sites-backend@0.1.10 + - @backstage/plugin-graphql-backend@0.1.38 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/plugin-rollbar-backend@0.1.45 + - @backstage/plugin-tech-insights-backend@0.5.14 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32 + - @backstage/plugin-tech-insights-node@0.4.6 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## example-backend-next@0.0.14 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4 + - @backstage/plugin-search-backend-module-catalog@0.1.4 + - @backstage/plugin-search-backend-module-explore@0.1.4 + - @backstage/plugin-azure-devops-backend@0.3.27 + - @backstage/plugin-kubernetes-backend@0.11.3 + - @backstage/plugin-lighthouse-backend@0.2.4 + - @backstage/plugin-permission-backend@0.5.23 + - @backstage/plugin-scaffolder-backend@1.16.0 + - @backstage/backend-defaults@0.2.0 + - @backstage/plugin-devtools-backend@0.1.3 + - @backstage/plugin-techdocs-backend@1.6.5 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/plugin-badges-backend@0.2.3 + - @backstage/plugin-search-backend@1.4.0 + - @backstage/plugin-proxy-backend@0.3.0 + - @backstage/plugin-todo-backend@0.2.0 + - @backstage/plugin-app-backend@0.3.48 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0 + - @backstage/plugin-entity-feedback-backend@0.1.6 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-linguist-backend@0.4.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-adr-backend@0.3.6 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/plugin-permission-common@0.7.7 + +## e2e-test@0.2.6 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## techdocs-cli-embedded-app@0.2.85 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/integration-react@1.1.16 + - @backstage/cli@0.22.10 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/app-defaults@1.4.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/plugin-techdocs@1.6.6 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9 + +## @internal/plugin-catalog-customized@0.0.13 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1 + - @backstage/plugin-catalog-react@1.8.1 + +## @internal/plugin-todo-list@1.0.16 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @internal/plugin-todo-list-backend@1.0.16 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 diff --git a/docs/releases/v1.17.0-next.1-changelog.md b/docs/releases/v1.17.0-next.1-changelog.md new file mode 100644 index 0000000000..5af9f0398b --- /dev/null +++ b/docs/releases/v1.17.0-next.1-changelog.md @@ -0,0 +1,2208 @@ +# Release v1.17.0-next.1 + +## @backstage/backend-app-api@0.5.0-next.1 + +### Minor Changes + +- b9c57a4f857e: **BREAKING**: Renamed `configServiceFactory` to `rootConfigServiceFactory`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config-loader@1.4.0-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/backend-defaults@0.2.0-next.1 + +### Minor Changes + +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-app-api@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + +## @backstage/backend-plugin-api@0.6.0-next.1 + +### Minor Changes + +- 629cbd194a87: **BREAKING**: Renamed `coreServices.config` to `coreServices.rootConfig`. +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/backend-test-utils@0.2.0-next.1 + +### Minor Changes + +- b9c57a4f857e: **BREAKING**: Renamed `mockServices.config` to `mockServices.rootConfig`. + +### Patch Changes + +- ae9304818136: Add needed constants and constructs to support PostgreSQL version 14 as test database +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-app-api@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/config-loader@1.4.0-next.1 + +### Minor Changes + +- 2f1859585998: Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. + + In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-backend@1.12.0-next.1 + +### Minor Changes + +- f32252cdf631: Added OpenTelemetry spans for catalog processing + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-openapi-utils@0.0.3-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.0-next.0 + +### Minor Changes + +- 290eff6692aa: Added GCP catalog plugin with GKE provider + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-kubernetes-common@0.6.5 + +## @backstage/backend-common@0.19.2-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/config-loader@1.4.0-next.1 + - @backstage/backend-app-api@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/backend-dev-utils@0.1.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/backend-openapi-utils@0.0.3-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/errors@1.2.1 + +## @backstage/backend-tasks@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 + +## @backstage/cli@0.22.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.4.0-next.1 + - @backstage/cli-node@0.1.3-next.0 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/eslint-plugin@0.1.3 + - @backstage/release-manifests@0.0.9 + - @backstage/types@1.1.0 + +## @backstage/cli-node@0.1.3-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/create-app@0.5.4-next.1 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.12 + +## @backstage/dev-utils@1.0.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/integration-react@1.1.16-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/repo-tools@0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.1.3-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## @techdocs/cli@1.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## @backstage/plugin-adr@0.6.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + +## @backstage/plugin-adr-backend@0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-airbrake@0.3.21-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/dev-utils@1.0.18-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-airbrake-backend@0.2.21-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-allure@0.1.37-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-analytics-module-ga@0.1.32-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-analytics-module-ga4@0.1.3-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-apache-airflow@0.2.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + +## @backstage/plugin-api-docs@0.9.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-apollo-explorer@0.1.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-app-backend@0.3.48-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config-loader@1.4.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/plugin-auth-backend@0.18.6-next.1 + +### Patch Changes + +- 9dad4b0e61bd: Updated config schema to match what was being used in code +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-auth-node@0.2.17-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-azure-devops-backend@0.3.27-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + +## @backstage/plugin-azure-sites-backend@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + +## @backstage/plugin-badges-backend@0.2.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-bazaar@0.2.12-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli@0.22.10-next.1 + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-bazaar-backend@0.2.11-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-bitbucket-cloud-common@0.2.9-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.5.1 + +## @backstage/plugin-bitrise@0.1.48-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-catalog@1.12.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.2.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- b4222908b0c3: Added option to configure AWS `accountId` in `AwsS3EntityProvider` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-kubernetes-common@0.6.5 + +## @backstage/plugin-catalog-backend-module-azure@0.1.19-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.9-next.0 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.15-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.9-next.0 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.13-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.16-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-catalog-backend-module-github@0.3.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-gitlab@0.2.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- e6c721439f37: Added option to skip forked repos in GitlabDiscoveryEntityProvider +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.1-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.7-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.5-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-model@1.4.1 + +## @backstage/plugin-catalog-graph@0.2.33-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-catalog-import@0.9.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-catalog-node@1.4.1-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.17-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-cicd-statistics@0.1.23-next.0 + +## @backstage/plugin-code-climate@0.1.21-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-code-coverage@0.2.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-code-coverage-backend@0.2.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-codescene@0.1.16-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-config-schema@0.1.44-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-devtools@0.1.3-next.1 + +### Patch Changes + +- 48b6a04ad045: Fix readme typo +- Updated dependencies + - @backstage/plugin-devtools-common@0.1.3-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-react@0.4.14 + +## @backstage/plugin-devtools-backend@0.1.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` + +- 12a8c94eda8d: Add package repository and homepage metadata + +- 2b4f77a4e900: Add DevTools configuration to enable dependency listing to be filtered with custom prefixes. For instance, in your `app-config.yaml`: + + ```yaml + devTools: + info: + packagePrefixes: + - @backstage/ + - @roadiehq/backstage- + - @spotify/backstage- + ``` + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config-loader@1.4.0-next.1 + - @backstage/plugin-devtools-common@0.1.3-next.0 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-devtools-common@0.1.3-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-dynatrace@7.0.1-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-entity-feedback@0.2.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-entity-feedback-common@0.1.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-entity-feedback-backend@0.1.6-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-entity-feedback-common@0.1.2-next.0 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-entity-feedback-common@0.1.2-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata + +## @backstage/plugin-entity-validation@0.1.6-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-events-backend@0.2.9-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-events-backend-module-aws-sqs@0.2.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/plugin-events-backend-module-azure@0.1.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.1.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.1.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + +## @backstage/plugin-events-backend-module-github@0.1.10-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-events-backend-module-gitlab@0.1.10-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-events-backend-test-utils@0.1.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.1 + +## @backstage/plugin-events-node@0.2.9-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.1 + +## @backstage/plugin-explore-backend@0.0.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-search-backend-module-explore@0.1.4-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-firehydrant@0.2.5-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-gcalendar@0.3.17-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-git-release-manager@0.3.34-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.5.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-github-actions@0.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-github-deployments@0.1.52-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-github-issues@0.2.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-graphql-backend@0.1.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-graphql@0.3.22 + +## @backstage/plugin-graphql-voyager@0.1.6-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## @backstage/plugin-ilert@0.2.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-jenkins@0.8.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-jenkins-common@0.1.18-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-jenkins-backend@0.2.3-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-jenkins-common@0.1.18-next.0 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-jenkins-common@0.1.18-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-kafka@0.3.21-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-kafka-backend@0.2.41-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-kubernetes-backend@0.11.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- bbf4e9c894b5: Fixed a bug where the proxy was not rewriting WebSocket request paths properly. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-kubernetes-common@0.6.5 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-lighthouse-backend@0.2.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-lighthouse-common@0.1.2 + +## @backstage/plugin-linguist@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-linguist-common@0.1.1-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-linguist-backend@0.3.2-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-linguist-common@0.1.1-next.0 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-linguist-common@0.1.1-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata + +## @backstage/plugin-microsoft-calendar@0.1.6-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- eea2922e749a: README update - example of apiRef definition and fixed component name +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## @backstage/plugin-newrelic-dashboard@0.2.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-nomad@0.1.2-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-nomad-backend@0.1.2-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-octopus-deploy@0.2.3-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-org@0.6.11-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-org-react@0.1.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-periskop@0.1.19-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @backstage/plugin-periskop-backend@0.1.19-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-permission-backend@0.5.23-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-permission-node@0.7.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-playlist@0.1.13-next.1 + +### Patch Changes + +- d1e0588324d1: Displaying an alert popup each time the Playlist is created or deleted +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-playlist-common@0.1.9-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + - @backstage/plugin-search-react@1.6.4-next.0 + +## @backstage/plugin-playlist-backend@0.3.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-playlist-common@0.1.9-next.0 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-playlist-common@0.1.9-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-permission-common@0.7.7 + +## @backstage/plugin-proxy-backend@0.2.42-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-rollbar-backend@0.1.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + +## @backstage/plugin-scaffolder@1.14.2-next.1 + +### Patch Changes + +- 8a0490fb669e: Fix the get entities query in the `MyGroupsPicker` to query the `kind=Group` entities. +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-permission-react@0.4.14 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-scaffolder-react@1.5.2-next.0 + +## @backstage/plugin-scaffolder-backend@1.15.2-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-scaffolder-common@1.3.2 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- 0d347efc8f18: Use `fetchContents` directly instead of a `fetchPlainAction` +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.24-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.2.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.17-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.8-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.21-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## @backstage/plugin-scaffolder-node@0.1.6-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-scaffolder-common@1.3.2 + +## @backstage/plugin-search-backend@1.4.0-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 951ab6c9db58: Add missing `configSchema` to package.json +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-openapi-utils@0.0.3-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-catalog@0.1.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-elasticsearch@1.3.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-explore@0.1.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-pg@0.5.9-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-module-techdocs@0.1.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-search-backend-node@1.2.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-shortcuts@0.3.13-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-sonarqube@0.7.2-next.1 + +### Patch Changes + +- b2ccddefbdc6: Remove sonarQube card disable class +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-sonarqube-react@0.1.7 + +## @backstage/plugin-sonarqube-backend@0.2.2-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-stack-overflow@0.1.19-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-home-react@0.1.2-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + +## @backstage/plugin-stack-overflow-backend@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-tech-insights@0.3.13-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-insights-backend@0.5.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-tech-insights-node@0.4.6-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-tech-insights-node@0.4.6-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-tech-insights-node@0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## @backstage/plugin-techdocs@1.6.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/plugin-techdocs@1.6.6-next.1 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## @backstage/plugin-techdocs-backend@1.6.5-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## @backstage/plugin-techdocs-node@1.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-search-common@1.2.5 + +## @backstage/plugin-todo-backend@0.2.0-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-openapi-utils@0.0.3-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-user-settings-backend@0.1.12-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## @backstage/plugin-vault-backend@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## @backstage/plugin-xcmetrics@0.2.41-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## example-app@0.2.86-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-playlist@0.1.13-next.1 + - @backstage/plugin-scaffolder@1.14.2-next.1 + - @backstage/integration-react@1.1.16-next.1 + - @backstage/plugin-microsoft-calendar@0.1.6-next.1 + - @backstage/plugin-newrelic-dashboard@0.2.14-next.1 + - @backstage/plugin-entity-feedback@0.2.4-next.1 + - @backstage/plugin-linguist-common@0.1.1-next.0 + - @backstage/plugin-apache-airflow@0.2.14-next.1 + - @backstage/plugin-octopus-deploy@0.2.3-next.1 + - @backstage/plugin-stack-overflow@0.1.19-next.1 + - @backstage/plugin-catalog-graph@0.2.33-next.1 + - @backstage/plugin-code-coverage@0.2.14-next.1 + - @backstage/plugin-tech-insights@0.3.13-next.1 + - @backstage/plugin-dynatrace@7.0.1-next.1 + - @backstage/plugin-gcalendar@0.3.17-next.1 + - @backstage/plugin-shortcuts@0.3.13-next.1 + - @backstage/plugin-airbrake@0.3.21-next.1 + - @backstage/plugin-kafka@0.3.21-next.1 + - @backstage/plugin-nomad@0.1.2-next.1 + - @backstage/plugin-adr@0.6.4-next.1 + - @backstage/plugin-org@0.6.11-next.1 + - @backstage/plugin-devtools@0.1.3-next.1 + - @backstage/cli@0.22.10-next.1 + - @backstage/plugin-catalog-import@0.9.11-next.1 + - @backstage/plugin-github-actions@0.6.2-next.1 + - @backstage/plugin-techdocs@1.6.6-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.16-next.1 + - @backstage/plugin-linguist@0.1.6-next.1 + - @backstage/plugin-jenkins@0.8.3-next.1 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-api-docs@0.9.7-next.1 + - @backstage/plugin-azure-devops@0.3.3-next.0 + - @backstage/plugin-azure-sites@0.1.10-next.0 + - @backstage/plugin-badges@0.2.45-next.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.1.2-next.0 + - @backstage/plugin-circleci@0.3.21-next.0 + - @backstage/plugin-cloudbuild@0.3.21-next.0 + - @backstage/plugin-cost-insights@0.12.10-next.0 + - @backstage/plugin-explore@0.4.7-next.0 + - @backstage/plugin-gcp-projects@0.3.40-next.0 + - @backstage/plugin-gocd@0.1.27-next.0 + - @backstage/plugin-graphiql@0.2.53-next.0 + - @backstage/plugin-home@0.5.5-next.0 + - @backstage/plugin-kubernetes@0.9.4-next.0 + - @backstage/plugin-lighthouse@0.4.6-next.0 + - @backstage/plugin-newrelic@0.3.39-next.0 + - @backstage/plugin-pagerduty@0.6.2-next.0 + - @backstage/plugin-permission-react@0.4.14 + - @backstage/plugin-puppetdb@0.1.4-next.0 + - @backstage/plugin-rollbar@0.4.21-next.0 + - @backstage/plugin-scaffolder-react@1.5.2-next.0 + - @backstage/plugin-search@1.3.4-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/plugin-sentry@0.5.6-next.0 + - @backstage/plugin-stackstorm@0.1.5-next.0 + - @backstage/plugin-tech-radar@0.6.7-next.0 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + - @backstage/plugin-todo@0.2.23-next.0 + - @backstage/plugin-user-settings@0.7.6-next.0 + - @internal/plugin-catalog-customized@0.0.13-next.1 + +## example-backend@0.2.86-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-elasticsearch@1.3.3-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.9-next.1 + - @backstage/plugin-azure-devops-backend@0.3.27-next.1 + - @backstage/plugin-kubernetes-backend@0.11.3-next.1 + - @backstage/plugin-lighthouse-backend@0.2.4-next.1 + - @backstage/plugin-permission-backend@0.5.23-next.1 + - @backstage/plugin-scaffolder-backend@1.15.2-next.1 + - @backstage/plugin-devtools-backend@0.1.3-next.1 + - @backstage/plugin-techdocs-backend@1.6.5-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-badges-backend@0.2.3-next.1 + - @backstage/plugin-events-backend@0.2.9-next.1 + - @backstage/plugin-search-backend@1.4.0-next.1 + - @backstage/plugin-kafka-backend@0.2.41-next.1 + - @backstage/plugin-proxy-backend@0.2.42-next.1 + - @backstage/plugin-todo-backend@0.2.0-next.1 + - @backstage/plugin-app-backend@0.3.48-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.17-next.1 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.1 + - @backstage/plugin-code-coverage-backend@0.2.14-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/plugin-linguist-backend@0.3.2-next.1 + - @backstage/plugin-playlist-backend@0.3.4-next.1 + - @backstage/plugin-explore-backend@0.0.10-next.1 + - @backstage/plugin-jenkins-backend@0.2.3-next.1 + - @backstage/plugin-nomad-backend@0.1.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/plugin-auth-backend@0.18.6-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-adr-backend@0.3.6-next.1 + - @backstage/plugin-azure-sites-backend@0.1.10-next.1 + - @backstage/plugin-graphql-backend@0.1.38-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/plugin-rollbar-backend@0.1.45-next.1 + - @backstage/plugin-tech-insights-backend@0.5.14-next.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32-next.1 + - @backstage/plugin-tech-insights-node@0.4.6-next.1 + - example-app@0.2.86-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## example-backend-next@0.0.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.4-next.1 + - @backstage/plugin-azure-devops-backend@0.3.27-next.1 + - @backstage/plugin-kubernetes-backend@0.11.3-next.1 + - @backstage/plugin-lighthouse-backend@0.2.4-next.1 + - @backstage/plugin-permission-backend@0.5.23-next.1 + - @backstage/plugin-scaffolder-backend@1.15.2-next.1 + - @backstage/backend-defaults@0.2.0-next.1 + - @backstage/plugin-devtools-backend@0.1.3-next.1 + - @backstage/plugin-techdocs-backend@1.6.5-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-badges-backend@0.2.3-next.1 + - @backstage/plugin-search-backend@1.4.0-next.1 + - @backstage/plugin-todo-backend@0.2.0-next.1 + - @backstage/plugin-app-backend@0.3.48-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.1 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/plugin-linguist-backend@0.3.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-adr-backend@0.3.6-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/plugin-permission-common@0.7.7 + +## e2e-test@0.2.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## techdocs-cli-embedded-app@0.2.85-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/cli@0.22.10-next.1 + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/plugin-techdocs@1.6.6-next.1 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## @internal/plugin-catalog-customized@0.0.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + +## @internal/plugin-todo-list-backend@1.0.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 diff --git a/docs/releases/v1.17.0-next.2-changelog.md b/docs/releases/v1.17.0-next.2-changelog.md new file mode 100644 index 0000000000..d1da6fa2be --- /dev/null +++ b/docs/releases/v1.17.0-next.2-changelog.md @@ -0,0 +1,1430 @@ +# Release v1.17.0-next.2 + +## @backstage/plugin-app-node@0.1.0-next.0 + +### Minor Changes + +- 9fbe95ef6503: Added the `app` plugin node library, initially providing an extension point that can be used to configure a static fallback handler. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + +## @backstage/plugin-catalog-backend@1.12.0-next.2 + +### Minor Changes + +- b8cccd8ee858: Support configuring applicable kinds for `AnnotateScmSlugEntityProcessor` + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-linguist-backend@0.4.0-next.2 + +### Minor Changes + +- d440f1dd0e72: Adds a processor to the linguist backend which can automatically add language tags to entities + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-linguist-common@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-proxy-backend@0.3.0-next.2 + +### Minor Changes + +- 7daf65bfcfa1: Defining proxy endpoints directly under the root `proxy` configuration key is deprecated. Endpoints should now be declared under `proxy.endpoints` instead. The `skipInvalidProxies` and `reviveConsumedRequestBodies` can now also be configured through static configuration. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/backend-app-api@0.5.0-next.2 + +### Patch Changes + +- e65c4896f755: Do not throw in backend.stop, if start failed +- cc9256a33bcc: Added new experimental `featureDiscoveryServiceFactory`, available as an `/alpha` export. +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + - @backstage/config-loader@1.4.0-next.1 + +## @backstage/backend-common@0.19.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/config-loader@1.4.0-next.1 + +## @backstage/backend-defaults@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/backend-plugin-api@0.6.0-next.2 + +### Patch Changes + +- cc9256a33bcc: Added new experimental `featureDiscoveryServiceRef`, available as an `/alpha` export. +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/backend-tasks@0.5.5-next.2 + +### Patch Changes + +- dfd1b6b2fc33: Make `readTaskScheduleDefinitionFromConfig` properly handle bad inputs +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/backend-test-utils@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/create-app@0.5.4-next.2 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/dev-utils@1.0.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @techdocs/cli@1.4.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## @backstage/plugin-adr@0.6.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-adr-backend@0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-airbrake@0.3.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/dev-utils@1.0.18-next.2 + +## @backstage/plugin-airbrake-backend@0.2.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-allure@0.1.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-api-docs@0.9.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-catalog@1.12.1-next.2 + +## @backstage/plugin-app-backend@0.3.48-next.2 + +### Patch Changes + +- d564ad142b17: Migrated the alpha `appBackend` export to use static configuration and extension points rather than accepting options. +- Updated dependencies + - @backstage/plugin-app-node@0.1.0-next.0 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/config-loader@1.4.0-next.1 + +## @backstage/plugin-auth-backend@0.18.6-next.2 + +### Patch Changes + +- 16452cd007ae: Updated `frameHandler` to return `undefined` when using the redirect flow instead of returning `postMessageReponse` which was causing errors +- bb70a9c3886a: Add frontend visibility to provider objects in `auth` config. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-auth-node@0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-azure-devops@0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-azure-devops-backend@0.3.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-azure-sites@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-azure-sites-backend@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-badges@0.2.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-badges-backend@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-bazaar@0.2.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-catalog@1.12.1-next.2 + - @backstage/cli@0.22.10-next.1 + +## @backstage/plugin-bazaar-backend@0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-bitrise@0.1.48-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-catalog@1.12.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-catalog-backend-module-aws@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-azure@0.1.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-github@0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-catalog-backend-module-gitlab@0.2.4-next.2 + +### Patch Changes + +- 2fe1f5973ff7: Filter Gitlab archived projects through APIs +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-catalog-graph@0.2.33-next.2 + +### Patch Changes + +- 62dc7a2b1ad1: Added maximum depth parameter to the catalogGraphParams in CatalogGraphCard. +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-catalog-import@0.9.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-catalog-node@1.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + +## @backstage/plugin-catalog-react@1.8.1-next.1 + +### Patch Changes + +- aa3feedce10a: Allow specifying screen size when catalog filters are hidden in drawer + +## @backstage/plugin-cicd-statistics@0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-cicd-statistics@0.1.23-next.1 + +## @backstage/plugin-circleci@0.3.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-cloudbuild@0.3.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-code-climate@0.1.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-code-coverage@0.2.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-code-coverage-backend@0.2.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-cost-insights@0.12.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-devtools-backend@0.1.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + - @backstage/config-loader@1.4.0-next.1 + +## @backstage/plugin-dynatrace@7.0.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-entity-feedback@0.2.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-entity-feedback-backend@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-entity-validation@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-events-backend@0.2.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-module-aws-sqs@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-module-azure@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-module-gerrit@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-module-github@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-module-gitlab@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-backend-test-utils@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.9-next.2 + +## @backstage/plugin-events-node@0.2.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + +## @backstage/plugin-explore@0.4.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-explore-react@0.0.30 + +## @backstage/plugin-explore-backend@0.0.10-next.2 + +### Patch Changes + +- eda2a699f40d: Moved the config example from the "Tools as Code" section to the "Tools as Config" section of the README +- Updated dependencies + - @backstage/plugin-search-backend-module-explore@0.1.4-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-firehydrant@0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-fossa@0.2.53-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-github-actions@0.6.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-github-deployments@0.1.52-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-github-issues@0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-github-pull-requests-board@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-gocd@0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-graphql-backend@0.1.38-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-graphql-voyager@0.1.6-next.2 + +### Patch Changes + +- bb1e1c2b26cc: Fix typo in install instructions. + +## @backstage/plugin-home@0.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-home-react@0.1.2-next.0 + +## @backstage/plugin-ilert@0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-jenkins@0.8.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-jenkins-backend@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-kafka@0.3.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-kafka-backend@0.2.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-kubernetes@0.9.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-kubernetes-backend@0.11.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-lighthouse@0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-lighthouse-backend@0.2.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-linguist@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-linguist-common@0.1.1-next.1 + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-linguist-common@0.1.1-next.1 + +### Patch Changes + +- d440f1dd0e72: Exported new LanguageType type alias + +## @backstage/plugin-newrelic-dashboard@0.2.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-nomad@0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-nomad-backend@0.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-octopus-deploy@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-org@0.6.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-org-react@0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-pagerduty@0.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-home-react@0.1.2-next.0 + +## @backstage/plugin-periskop@0.1.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-periskop-backend@0.1.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-permission-backend@0.5.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-permission-node@0.7.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-playlist@0.1.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-playlist-backend@0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-puppetdb@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-rollbar@0.4.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-rollbar-backend@0.1.45-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-scaffolder@1.14.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-scaffolder-react@1.5.2-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-scaffolder-backend@1.15.2-next.2 + +### Patch Changes + +- 33c76caef72a: Added examples for the fs:delete and fs:rename actions +- 0b1d775be05b: Adds examples to a few scaffolder actions. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.24-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + +## @backstage/plugin-scaffolder-node@0.1.6-next.2 + +### Patch Changes + +- 0b1d775be05b: Export `TemplateExample` from the `createTemplateAction` type. +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-scaffolder-react@1.5.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-search@1.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-search-backend@1.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-search-backend-module-catalog@0.1.4-next.2 + +### Patch Changes + +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## @backstage/plugin-search-backend-module-elasticsearch@1.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## @backstage/plugin-search-backend-module-explore@0.1.4-next.2 + +### Patch Changes + +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## @backstage/plugin-search-backend-module-pg@0.5.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## @backstage/plugin-search-backend-module-techdocs@0.1.4-next.2 + +### Patch Changes + +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## @backstage/plugin-search-backend-node@1.2.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-sentry@0.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-sonarqube@0.7.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-sonarqube-backend@0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-splunk-on-call@0.4.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-stack-overflow-backend@0.2.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-tech-insights@0.3.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-tech-insights-backend@0.5.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-tech-insights-node@0.4.6-next.2 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-tech-insights-node@0.4.6-next.2 + +## @backstage/plugin-tech-insights-node@0.4.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-techdocs@1.6.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.2 + - @backstage/plugin-techdocs@1.6.6-next.2 + - @backstage/integration-react@1.1.16-next.1 + +## @backstage/plugin-techdocs-backend@1.6.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## @backstage/plugin-techdocs-node@1.7.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## @backstage/plugin-todo@0.2.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-todo-backend@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## @backstage/plugin-user-settings@0.7.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-user-settings-backend@0.1.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## @backstage/plugin-vault@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## @backstage/plugin-vault-backend@0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## example-app@0.2.86-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-graph@0.2.33-next.2 + - @backstage/plugin-linguist-common@0.1.1-next.1 + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-linguist@0.1.6-next.2 + - @backstage/plugin-adr@0.6.4-next.2 + - @backstage/plugin-airbrake@0.3.21-next.2 + - @backstage/plugin-api-docs@0.9.7-next.2 + - @backstage/plugin-azure-devops@0.3.3-next.1 + - @backstage/plugin-azure-sites@0.1.10-next.1 + - @backstage/plugin-badges@0.2.45-next.1 + - @internal/plugin-catalog-customized@0.0.13-next.2 + - @backstage/plugin-catalog-import@0.9.11-next.2 + - @backstage/plugin-circleci@0.3.21-next.1 + - @backstage/plugin-cloudbuild@0.3.21-next.1 + - @backstage/plugin-code-coverage@0.2.14-next.2 + - @backstage/plugin-cost-insights@0.12.10-next.1 + - @backstage/plugin-dynatrace@7.0.1-next.2 + - @backstage/plugin-entity-feedback@0.2.4-next.2 + - @backstage/plugin-explore@0.4.7-next.1 + - @backstage/plugin-github-actions@0.6.2-next.2 + - @backstage/plugin-gocd@0.1.27-next.1 + - @backstage/plugin-home@0.5.5-next.1 + - @backstage/plugin-jenkins@0.8.3-next.2 + - @backstage/plugin-kafka@0.3.21-next.2 + - @backstage/plugin-kubernetes@0.9.4-next.1 + - @backstage/plugin-lighthouse@0.4.6-next.1 + - @backstage/plugin-newrelic-dashboard@0.2.14-next.2 + - @backstage/plugin-nomad@0.1.2-next.2 + - @backstage/plugin-octopus-deploy@0.2.3-next.2 + - @backstage/plugin-org@0.6.11-next.2 + - @backstage/plugin-pagerduty@0.6.2-next.1 + - @backstage/plugin-playlist@0.1.13-next.2 + - @backstage/plugin-puppetdb@0.1.4-next.1 + - @backstage/plugin-rollbar@0.4.21-next.1 + - @backstage/plugin-scaffolder@1.14.2-next.2 + - @backstage/plugin-scaffolder-react@1.5.2-next.1 + - @backstage/plugin-search@1.3.4-next.1 + - @backstage/plugin-sentry@0.5.6-next.1 + - @backstage/plugin-tech-insights@0.3.13-next.2 + - @backstage/plugin-techdocs@1.6.6-next.2 + - @backstage/plugin-todo@0.2.23-next.1 + - @backstage/plugin-user-settings@0.7.6-next.1 + - @backstage/cli@0.22.10-next.1 + - @backstage/integration-react@1.1.16-next.1 + - @backstage/plugin-apache-airflow@0.2.14-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.2-next.0 + - @backstage/plugin-devtools@0.1.3-next.1 + - @backstage/plugin-gcalendar@0.3.17-next.1 + - @backstage/plugin-gcp-projects@0.3.40-next.0 + - @backstage/plugin-graphiql@0.2.53-next.0 + - @backstage/plugin-microsoft-calendar@0.1.6-next.1 + - @backstage/plugin-newrelic@0.3.39-next.0 + - @backstage/plugin-shortcuts@0.3.13-next.1 + - @backstage/plugin-stack-overflow@0.1.19-next.1 + - @backstage/plugin-stackstorm@0.1.5-next.0 + - @backstage/plugin-tech-radar@0.6.7-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.16-next.1 + +## example-backend@0.2.86-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.18.6-next.2 + - @backstage/plugin-scaffolder-backend@1.15.2-next.2 + - @backstage/plugin-explore-backend@0.0.10-next.2 + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/plugin-proxy-backend@0.3.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-app-backend@0.3.48-next.2 + - @backstage/plugin-linguist-backend@0.4.0-next.2 + - @backstage/plugin-techdocs-backend@1.6.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - example-app@0.2.86-next.2 + - @backstage/plugin-adr-backend@0.3.6-next.2 + - @backstage/plugin-azure-devops-backend@0.3.27-next.2 + - @backstage/plugin-badges-backend@0.2.3-next.2 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-devtools-backend@0.1.3-next.2 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.2 + - @backstage/plugin-events-backend@0.2.9-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + - @backstage/plugin-kafka-backend@0.2.41-next.2 + - @backstage/plugin-kubernetes-backend@0.11.3-next.2 + - @backstage/plugin-lighthouse-backend@0.2.4-next.2 + - @backstage/plugin-permission-backend@0.5.23-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-search-backend@1.4.0-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.3-next.2 + - @backstage/plugin-search-backend-module-pg@0.5.9-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-todo-backend@0.2.0-next.2 + - @backstage/plugin-tech-insights-backend@0.5.14-next.2 + - @backstage/plugin-tech-insights-node@0.4.6-next.2 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.17-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + - @backstage/plugin-azure-sites-backend@0.1.10-next.2 + - @backstage/plugin-code-coverage-backend@0.2.14-next.2 + - @backstage/plugin-graphql-backend@0.1.38-next.2 + - @backstage/plugin-jenkins-backend@0.2.3-next.2 + - @backstage/plugin-nomad-backend@0.1.2-next.2 + - @backstage/plugin-playlist-backend@0.3.4-next.2 + - @backstage/plugin-rollbar-backend@0.1.45-next.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32-next.2 + +## example-backend-next@0.0.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.2 + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.2 + - @backstage/plugin-search-backend-module-explore@0.1.4-next.2 + - @backstage/plugin-scaffolder-backend@1.15.2-next.2 + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-proxy-backend@0.3.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-app-backend@0.3.48-next.2 + - @backstage/plugin-linguist-backend@0.4.0-next.2 + - @backstage/plugin-techdocs-backend@1.6.5-next.2 + - @backstage/backend-defaults@0.2.0-next.2 + - @backstage/plugin-adr-backend@0.3.6-next.2 + - @backstage/plugin-azure-devops-backend@0.3.27-next.2 + - @backstage/plugin-badges-backend@0.2.3-next.2 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.2 + - @backstage/plugin-devtools-backend@0.1.3-next.2 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.2 + - @backstage/plugin-kubernetes-backend@0.11.3-next.2 + - @backstage/plugin-lighthouse-backend@0.2.4-next.2 + - @backstage/plugin-permission-backend@0.5.23-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-search-backend@1.4.0-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-todo-backend@0.2.0-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## e2e-test@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4-next.2 + +## techdocs-cli-embedded-app@0.2.85-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.2 + - @backstage/plugin-techdocs@1.6.6-next.2 + - @backstage/cli@0.22.10-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## @internal/plugin-catalog-customized@0.0.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-catalog@1.12.1-next.2 + +## @internal/plugin-todo-list-backend@1.0.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 diff --git a/docs/releases/v1.17.0.md b/docs/releases/v1.17.0.md new file mode 100644 index 0000000000..36414e6608 --- /dev/null +++ b/docs/releases/v1.17.0.md @@ -0,0 +1,72 @@ +--- +id: v1.17.0 +title: v1.17.0 +description: Backstage Release v1.17.0 +--- + +These are the release notes for the v1.17.0 release of [Backstage](https://backstage.io/). + +A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for the hard work in getting this release developed and done. + +## Highlights + +### Changes to the New Backend System + +For this release we had another look at the new backend system, in particular how well it fits together with the concept of plugin installation without code modification, and we identified a number of breaking changes that we wanted to make. + +We have removed the ability to define options for plugins and modules, and force them to be optional for services. Options have been moved either to static configuration or extension points, and modules now also have the ability to register extension points. + +Service factories are now considered `BackendFeature`s and are installed via `backend.add(...)` rather than via the `services` option, which has been removed. + +The configuration service has also been renamed to `rootConfig` in order to match other root-scoped services and make space for a potential plugin-scoped configuration service in the future. + +The shared environments concept has been removed. We are still interested in having some way to define reusable presets in the new system, but shared environments were too limited. + +We expect these to be the last broad breaking changes to the new backend system, and are likely to start encouraging use of it in the next release. + +### Catalog Telemetry with Processing Tracing + +The Catalog has been instrumented with some simple OpenTelemetry Spans so you can now dig into the processing loop and see it in action using tracing. + +Contributed by [@mikebryant](https://github.com/mikebryant) in [#17534](https://github.com/backstage/backstage/pull/17534) + +### New `each` property for Scaffolder Actions + +You can now define an `each` iterable in the `steps` for a template, which will run a given action for each entry in the iterable. + +Contributed by [@alexef](https://github.com/alexef) in [#18157](https://github.com/backstage/backstage/pull/18157) + +### Updated proxy configuration + +The shape of the proxy plugin configuration has been changed in order to make space for additional keys. Rather than defining the proxy endpoints directly under `proxy`, they should now be defined at `proxy.endpoints` instead. + +### OpenAPI Support for Backend Plugins + +You can now use OpenAPI specs to generate typed routers for your backend plugins with request validation using `createOpenApiRouter()`. There’s been some movement to support this for some of the plugins in the monorepo like `todo`, `search` and `catalog`. + +Contributed by [@sennyeya](https://github.com/sennyeya) in [#17875](https://github.com/backstage/backstage/pull/17875) + +### New plugin: `@backstage/plugin-catalog-backend-module-gcp` + +Allows ingestion of GKE Clusters as Resources into the Catalog. Contributed by [@mclarke47](https://github.com/mclarke47) in [#18759](https://github.com/backstage/backstage/pull/18759) + +## Security Fixes + +This release does not contain any security fixes. + +## Upgrade path + +We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated). + +## Links and References + +Below you can find a list of links and references to help you learn about and start using this new release. + +- [Backstage official website](https://backstage.io/), [documentation](https://backstage.io/docs/), and [getting started guide](https://backstage.io/docs/getting-started/) +- [GitHub repository](https://github.com/backstage/backstage) +- Backstage's [versioning and support policy](https://backstage.io/docs/overview/versioning-policy) +- [Community Discord](https://discord.gg/backstage-687207715902193673) for discussions and support +- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.17.0-changelog.md) +- Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap) and [Plugins](https://backstage.io/plugins) + +Sign up for our [newsletter](https://info.backstage.spotify.com/newsletter_subscribe) if you want to be informed about what is happening in the world of Backstage. diff --git a/lighthouserc.js b/lighthouserc.js index 90a15abbd3..70136ee158 100644 --- a/lighthouserc.js +++ b/lighthouserc.js @@ -32,6 +32,17 @@ module.exports = { 'http://localhost:3000/create/templates/default/react-ssr-template', /** Search */ 'http://localhost:3000/search', + /** Miscellaneous */ + 'http://localhost:3000/settings', + /** plugin-devtools */ + 'http://localhost:3000/devtools', + 'http://localhost:3000/devtools/config', + /** plugin-explore */ + 'http://localhost:3000/explore', + 'http://localhost:3000/explore/groups', + 'http://localhost:3000/explore/tools', + /** plugin-tech-radar */ + 'http://localhost:3000/tech-radar', ], settings: { onlyCategories: ['accessibility'], diff --git a/microsite/data/plugins/3scale.yaml b/microsite/data/plugins/3scale.yaml index c190ed7e9e..fa568c17bf 100644 --- a/microsite/data/plugins/3scale.yaml +++ b/microsite/data/plugins/3scale.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: Discovery description: Synchronize 3scale content into the Backstage catalog. documentation: https://janus-idp.io/plugins/3scale -iconUrl: http://janus-idp.io/images/plugins/3scale.svg +iconUrl: https://janus-idp.io/images/plugins/3scale.svg npmPackageName: '@janus-idp/backstage-plugin-3scale-backend' addedDate: '2023-05-15' diff --git a/microsite/data/plugins/api-sdk-generator.yaml b/microsite/data/plugins/api-sdk-generator.yaml new file mode 100644 index 0000000000..5553089807 --- /dev/null +++ b/microsite/data/plugins/api-sdk-generator.yaml @@ -0,0 +1,10 @@ +--- +title: API SDK Generator +author: Konfig +authorUrl: https://konfigthis.com/ +category: Development +description: Generate SDKs for your REST API to accelerate integration +documentation: https://github.com/konfig-dev/backstage-plugin-konfig/tree/main/plugins/backstage-plugin-konfig +iconUrl: https://raw.githubusercontent.com/konfig-dev/backstage-plugin-konfig/main/plugins/backstage-plugin-konfig/docs/logo.png +npmPackageName: 'backstage-plugin-konfig' +addedDate: '2023-08-07' diff --git a/microsite/data/plugins/jfrog-artifactory-libs.yaml b/microsite/data/plugins/jfrog-artifactory-libs.yaml new file mode 100644 index 0000000000..22583cbfbd --- /dev/null +++ b/microsite/data/plugins/jfrog-artifactory-libs.yaml @@ -0,0 +1,13 @@ +--- +title: JFrog Artifactory Libs plugin +author: Vity +authorUrl: https://github.com/Vity01 +category: Discovery +description: + frontend plugin provides a simple way to display generated artifact (library) details like + - group, artifact, repository, what is the latest version, and it simply + allows to copy library definition for the package managers. +documentation: https://github.com/Vity01/backstage-jfrog-artifactory-libs +iconUrl: https://raw.githubusercontent.com/Vity01/backstage-jfrog-artifactory-libs/main/doc/artifactory.svg +npmPackageName: 'backstage-plugin-jfrog-artifactory-libs' +addedDate: '2023-08-14' diff --git a/microsite/data/plugins/jfrog-artifactory.yaml b/microsite/data/plugins/jfrog-artifactory.yaml index 279cbc161e..6765fe3d8d 100644 --- a/microsite/data/plugins/jfrog-artifactory.yaml +++ b/microsite/data/plugins/jfrog-artifactory.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: Image description: View container image details from JFrog Artifactory in Backstage. documentation: https://janus-idp.io/plugins/jfrog-artifactory -iconUrl: http://janus-idp.io/images/plugins/jfrog-artifactory.svg +iconUrl: https://janus-idp.io/images/plugins/jfrog-artifactory.svg npmPackageName: '@janus-idp/backstage-plugin-jfrog-artifactory' addedDate: '2023-05-15' diff --git a/microsite/data/plugins/keycloak.yaml b/microsite/data/plugins/keycloak.yaml index ea657c74f2..3da89c3d0a 100644 --- a/microsite/data/plugins/keycloak.yaml +++ b/microsite/data/plugins/keycloak.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: Authentication/Authorization description: Load users and groups from Keycloak, enabling use of multiple authentication providers to be applied to Backstage entities. documentation: https://janus-idp.io/plugins/keycloak -iconUrl: http://janus-idp.io/images/plugins/keycloak.svg +iconUrl: https://janus-idp.io/images/plugins/keycloak.svg npmPackageName: '@janus-idp/backstage-plugin-keycloak-backend' addedDate: '2023-05-15' diff --git a/microsite/data/plugins/kiali.yaml b/microsite/data/plugins/kiali.yaml new file mode 100644 index 0000000000..a85aa6444a --- /dev/null +++ b/microsite/data/plugins/kiali.yaml @@ -0,0 +1,10 @@ +--- +title: Kiali Service Mesh +author: Red Hat +authorUrl: https://redhat.com +category: Istio +description: Configure, visualize, validate and troubleshoot your mesh with Istio +documentation: https://janus-idp.io/plugins/kiali +iconUrl: https://janus-idp.io/images/plugins/kiali.svg +npmPackageName: '@janus-idp/backstage-plugin-kiali' +addedDate: '2023-07-25' diff --git a/microsite/data/plugins/ocm.yaml b/microsite/data/plugins/ocm.yaml index 9e9b8ff1b7..6cf77ca678 100644 --- a/microsite/data/plugins/ocm.yaml +++ b/microsite/data/plugins/ocm.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: Infrastructure description: View clusters from OCM's MultiClusterHub and MultiCluster Engine in Backstage. documentation: https://janus-idp.io/plugins/ocm -iconUrl: http://janus-idp.io/images/plugins/ocm.svg +iconUrl: https://janus-idp.io/images/plugins/ocm.svg npmPackageName: '@janus-idp/backstage-plugin-ocm' addedDate: '2023-05-15' diff --git a/microsite/data/plugins/quay.yaml b/microsite/data/plugins/quay.yaml index e4aeb1ec1a..79ae1c3408 100644 --- a/microsite/data/plugins/quay.yaml +++ b/microsite/data/plugins/quay.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: Image description: View container image details from Quay in Backstage. documentation: https://janus-idp.io/plugins/quay -iconUrl: http://janus-idp.io/images/plugins/quay.svg +iconUrl: https://janus-idp.io/images/plugins/quay.svg npmPackageName: '@janus-idp/backstage-plugin-quay' addedDate: '2023-05-15' diff --git a/microsite/data/plugins/scaffolder-backend-ms-teams.yaml b/microsite/data/plugins/scaffolder-backend-ms-teams.yaml new file mode 100644 index 0000000000..6d16b1300b --- /dev/null +++ b/microsite/data/plugins/scaffolder-backend-ms-teams.yaml @@ -0,0 +1,9 @@ +--- +title: Microsoft Teams Scaffolder Actions +author: Gaurav Pandey +authorUrl: https://github.com/grvpandey11 +category: Scaffolder +description: Interact with Microsoft Teams from Scaffolder templates +documentation: https://github.com/grvpandey11/backstage-plugin-scaffolder-backend-module-ms-teams/tree/main#readme +npmPackageName: '@grvpandey11/backstage-plugin-scaffolder-backend-module-ms-teams' +addedDate: '2023-08-11' diff --git a/microsite/data/plugins/scaffolder-backend-slack.yaml b/microsite/data/plugins/scaffolder-backend-slack.yaml new file mode 100644 index 0000000000..dce7b60eb7 --- /dev/null +++ b/microsite/data/plugins/scaffolder-backend-slack.yaml @@ -0,0 +1,10 @@ +--- +title: Slack Scaffolder Actions +author: Drew Hill +authorUrl: https://github.com/arhill05 +category: Scaffolder +description: Interact with Slack from scaffolder templates +documentation: https://github.com/arhill05/backstage-plugin-scaffolder-backend-module-slack#readme +iconUrl: img/Slack-mark-RGB.png +npmPackageName: '@mdude2314/backstage-plugin-scaffolder-backend-module-slack' +addedDate: '2023-08-04' diff --git a/microsite/data/plugins/solo-io-gloo-platform-portal.yaml b/microsite/data/plugins/solo-io-gloo-platform-portal.yaml index fe7f4d78da..2f17b84f17 100644 --- a/microsite/data/plugins/solo-io-gloo-platform-portal.yaml +++ b/microsite/data/plugins/solo-io-gloo-platform-portal.yaml @@ -4,7 +4,7 @@ author: Solo.io authorUrl: https://solo.io category: Infrastructure description: View, test, and manage your Gloo Platform Portal APIs through our Backstage plugin. -documentation: https://github.com/solo-io/dev-portal-backstage-public#readme +documentation: https://github.com/solo-io/backstage-plugins-overview#readme iconUrl: /img/solo-io-glooy-circle.png.webp npmPackageName: '@solo.io/dev-portal-backstage-plugin' addedDate: '2023-05-19' diff --git a/microsite/data/plugins/tekton.yaml b/microsite/data/plugins/tekton.yaml index fa94b89c35..27540a81d3 100644 --- a/microsite/data/plugins/tekton.yaml +++ b/microsite/data/plugins/tekton.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: CI/CD description: Easily view Tekton PipelineRun status for your services in Backstage. documentation: https://janus-idp.io/plugins/tekton -iconUrl: http://janus-idp.io/images/plugins/tekton.svg +iconUrl: https://janus-idp.io/images/plugins/tekton.svg npmPackageName: '@janus-idp/backstage-plugin-tekton' addedDate: '2023-05-15' diff --git a/microsite/data/plugins/topology.yaml b/microsite/data/plugins/topology.yaml index 2f26f7c755..9d3b816bd4 100644 --- a/microsite/data/plugins/topology.yaml +++ b/microsite/data/plugins/topology.yaml @@ -5,6 +5,6 @@ authorUrl: https://redhat.com category: Kubernetes description: Visualize the deployment status and related resources of your applications deployed on any Kubernetes cluster. documentation: https://janus-idp.io/plugins/topology -iconUrl: http://janus-idp.io/images/plugins/topology.svg +iconUrl: https://janus-idp.io/images/plugins/topology.svg npmPackageName: '@janus-idp/backstage-plugin-topology' addedDate: '2023-05-15' diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js index 23438762cc..a55ad01a35 100644 --- a/microsite/docusaurus.config.js +++ b/microsite/docusaurus.config.js @@ -177,7 +177,7 @@ module.exports = { position: 'left', }, { - to: 'docs/releases/v1.16.0', + to: 'docs/releases/v1.17.0', label: 'Releases', position: 'left', }, diff --git a/microsite/sidebars.json b/microsite/sidebars.json index d9178396e8..0e73bd94f2 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -1,6 +1,7 @@ { "releases": { "Release Notes": [ + "releases/v1.17.0", "releases/v1.16.0", "releases/v1.15.0", "releases/v1.14.0", @@ -331,6 +332,7 @@ "deployment/k8s", "deployment/heroku", "deployment/koyeb", + "deployment/flightcontrol", { "type": "category", "label": "Using Backstage Deploy (alpha)", diff --git a/microsite/src/pages/home/_home.tsx b/microsite/src/pages/home/_home.tsx index 07e05068c6..ddcfed7d03 100644 --- a/microsite/src/pages/home/_home.tsx +++ b/microsite/src/pages/home/_home.tsx @@ -32,27 +32,6 @@ const HomePage = () => { -
-
- CFP is now open for BackstageCon 2023 🚀 -
- -
{!hiddenNewsletterBanner && (
Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha'; +import { ServiceFactory } from '@backstage/backend-plugin-api'; + +// @alpha (undocumented) +export const featureDiscoveryServiceFactory: () => ServiceFactory< + FeatureDiscoveryService, + 'root' +>; + +// (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 5f452f9e03..0fa1ff376e 100644 --- a/packages/backend-app-api/api-report.md +++ b/packages/backend-app-api/api-report.md @@ -9,7 +9,6 @@ import type { AppConfig } from '@backstage/config'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { CacheClient } from '@backstage/backend-common'; import { Config } from '@backstage/config'; -import { ConfigService } from '@backstage/backend-plugin-api'; import { CorsOptions } from 'cors'; import { ErrorRequestHandler } from 'express'; import { Express as Express_2 } from 'express'; @@ -30,6 +29,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { RemoteConfigSourceOptions } from '@backstage/config-loader'; import { RequestHandler } from 'express'; import { RequestListener } from 'http'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { RootHttpRouterService } from '@backstage/backend-plugin-api'; import { RootLifecycleService } from '@backstage/backend-plugin-api'; import { RootLoggerService } from '@backstage/backend-plugin-api'; @@ -43,7 +43,7 @@ import { UrlReader } from '@backstage/backend-common'; // @public (undocumented) export interface Backend { // (undocumented) - add(feature: BackendFeature): void; + add(feature: BackendFeature | (() => BackendFeature)): void; // (undocumented) start(): Promise; // (undocumented) @@ -53,17 +53,6 @@ export interface Backend { // @public (undocumented) export const cacheServiceFactory: () => ServiceFactory; -// @public (undocumented) -export interface ConfigFactoryOptions { - argv?: string[]; - remote?: Pick; -} - -// @public (undocumented) -export const configServiceFactory: ( - options?: ConfigFactoryOptions | undefined, -) => ServiceFactory; - // @public (undocumented) export function createConfigSecretEnumerator(options: { logger: LoggerService; @@ -92,7 +81,7 @@ export function createSpecializedBackend( // @public (undocumented) export interface CreateSpecializedBackendOptions { // (undocumented) - services: ServiceFactoryOrFunction[]; + defaultServiceFactories: ServiceFactoryOrFunction[]; } // @public (undocumented) @@ -224,7 +213,7 @@ export interface MiddlewareFactoryErrorOptions { // @public export interface MiddlewareFactoryOptions { // (undocumented) - config: ConfigService; + config: RootConfigService; // (undocumented) logger: LoggerService; } @@ -244,12 +233,23 @@ export function readHelmetOptions(config?: Config): HelmetOptions; // @public export function readHttpServerOptions(config?: Config): HttpServerOptions; +// @public (undocumented) +export interface RootConfigFactoryOptions { + argv?: string[]; + remote?: Pick; +} + +// @public (undocumented) +export const rootConfigServiceFactory: ( + options?: RootConfigFactoryOptions | undefined, +) => ServiceFactory; + // @public (undocumented) export interface RootHttpRouterConfigureContext { // (undocumented) app: Express_2; // (undocumented) - config: ConfigService; + config: RootConfigService; // (undocumented) lifecycle: LifecycleService; // (undocumented) diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 6f8a5cf9e8..82e32311aa 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,17 +1,30 @@ { "name": "@backstage/backend-app-api", "description": "Core API used by Backstage backend apps", - "version": "0.4.6-next.0", + "version": "0.5.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" }, "backstage": { "role": "node-library" }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "homepage": "https://backstage.io", "repository": { "type": "git", @@ -36,6 +49,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/cli-common": "workspace:^", + "@backstage/cli-node": "workspace:^", "@backstage/config": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", @@ -73,6 +87,7 @@ "@types/node-forge": "^1.3.0", "@types/stoppable": "^1.1.0", "http-errors": "^2.0.0", + "mock-fs": "^5.2.0", "supertest": "^6.1.3" }, "files": [ diff --git a/packages/backend-app-api/src/alpha.ts b/packages/backend-app-api/src/alpha.ts new file mode 100644 index 0000000000..a334214738 --- /dev/null +++ b/packages/backend-app-api/src/alpha.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { featureDiscoveryServiceFactory } from './alpha/featureDiscoveryServiceFactory'; diff --git a/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts new file mode 100644 index 0000000000..5d4b1fc551 --- /dev/null +++ b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.test.ts @@ -0,0 +1,114 @@ +/* + * 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 mockFs from 'mock-fs'; +import { resolve as resolvePath, dirname } from 'path'; +import { startTestBackend, mockServices } from '@backstage/backend-test-utils'; +import { featureDiscoveryServiceFactory } from './featureDiscoveryServiceFactory'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; + +const rootDir = dirname(process.argv[1]); + +describe('featureDiscoveryServiceFactory', () => { + beforeEach(() => { + mockFs({ + [rootDir]: { + 'package.json': JSON.stringify({ + name: 'example-app', + dependencies: { + 'detected-plugin': '0.0.0', + 'detected-module': '0.0.0', + }, + }), + }, + [resolvePath(rootDir, 'node_modules/detected-plugin')]: { + 'package.json': JSON.stringify({ + name: 'detected-plugin', + main: 'index.js', + backstage: { + role: 'backend-plugin', + }, + }), + 'index.js': ` + const { createBackendPlugin, coreServices } = require('@backstage/backend-plugin-api'); + exports.detectedPlugin = createBackendPlugin({ + pluginId: 'detected', + register(env) { + env.registerInit({ + deps: { identity: coreServices.identity }, + async init({ identity }) { + identity.getIdentity('detected-plugin'); + }, + }); + }, + }); + `, + }, + [resolvePath(rootDir, 'node_modules/detected-module')]: { + 'package.json': JSON.stringify({ + name: 'detected-module', + main: 'index.js', + backstage: { + role: 'backend-module', + }, + }), + 'index.js': ` + const { createBackendModule, coreServices } = require('@backstage/backend-plugin-api'); + exports.detectedModuleDerp = createBackendModule({ + pluginId: 'detected', + moduleId: 'derp', + register(env) { + env.registerInit({ + deps: { identity: coreServices.identity }, + async init({ identity }) { + identity.getIdentity('detected-module'); + }, + }); + }, + }); + `, + }, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should detect plugin and module packages', async () => { + const fn = jest.fn().mockResolvedValue({}); + + await startTestBackend({ + features: [ + createServiceFactory({ + service: coreServices.identity, + deps: {}, + factory: () => ({ getIdentity: fn }), + }), + featureDiscoveryServiceFactory(), + mockServices.rootConfig.factory({ + data: { backend: { packages: 'all' } }, + }), + ], + }); + + expect(fn).toHaveBeenCalledWith('detected-plugin'); + expect(fn).toHaveBeenCalledWith('detected-module'); + }); +}); diff --git a/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts new file mode 100644 index 0000000000..f9ee7edc5f --- /dev/null +++ b/packages/backend-app-api/src/alpha/featureDiscoveryServiceFactory.ts @@ -0,0 +1,129 @@ +/* + * 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 { + BackendFeature, + RootConfigService, + 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'; + +const LOADED_PACKAGE_ROLES = ['backend-plugin', 'backend-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) {} + + async getBackendFeatures(): Promise<{ features: Array }> { + if (this.config.getOptionalString('backend.packages') !== 'all') { + return { features: [] }; + } + + const packageDir = await findClosestPackageDir(process.argv[1]); + if (!packageDir) { + throw new Error('Package discovery failed to find package.json'); + } + const { dependencies } = require(resolvePath( + packageDir, + 'package.json', + )) as BackstagePackageJson; + const dependencyNames = Object.keys(dependencies || {}); + + const features: BackendFeature[] = []; + + for (const name of dependencyNames) { + const depPkg = require(require.resolve(`${name}/package.json`, { + paths: [packageDir], + })) as BackstagePackageJson; + if (!LOADED_PACKAGE_ROLES.includes(depPkg?.backstage?.role ?? '')) { + continue; + } + const depModule = require(require.resolve(name, { paths: [packageDir] })); + for (const exportValue of Object.values(depModule)) { + if (isBackendFeature(exportValue)) { + features.push(exportValue); + } + if (isBackendFeatureFactory(exportValue)) { + features.push(exportValue()); + } + } + } + + return { features }; + } +} + +/** @alpha */ +export const featureDiscoveryServiceFactory = createServiceFactory({ + service: featureDiscoveryServiceRef, + deps: { + config: coreServices.rootConfig, + }, + factory({ config }) { + return new PackageDiscoveryService(config); + }, +}); + +function isBackendFeature(value: unknown): value is BackendFeature { + return ( + !!value && + typeof value === 'object' && + (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/config/ObservableConfigProxy.ts b/packages/backend-app-api/src/config/ObservableConfigProxy.ts index dd3334c9a5..8c8d3b553f 100644 --- a/packages/backend-app-api/src/config/ObservableConfigProxy.ts +++ b/packages/backend-app-api/src/config/ObservableConfigProxy.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { ConfigService } from '@backstage/backend-plugin-api'; -import { ConfigReader } from '@backstage/config'; +import { Config, ConfigReader } from '@backstage/config'; import { JsonValue } from '@backstage/types'; -export class ObservableConfigProxy implements ConfigService { - private config: ConfigService = new ConfigReader({}); +export class ObservableConfigProxy implements Config { + private config: Config = new ConfigReader({}); private readonly subscribers: (() => void)[] = []; @@ -32,7 +31,7 @@ export class ObservableConfigProxy implements ConfigService { } } - setConfig(config: ConfigService) { + setConfig(config: Config) { if (this.parent) { throw new Error('immutable'); } @@ -62,9 +61,9 @@ export class ObservableConfigProxy implements ConfigService { }; } - private select(required: true): ConfigService; - private select(required: false): ConfigService | undefined; - private select(required: boolean): ConfigService | undefined { + private select(required: true): Config; + private select(required: false): Config | undefined; + private select(required: boolean): Config | undefined { if (this.parent && this.parentKey) { if (required) { return this.parent.select(true).getConfig(this.parentKey); @@ -87,19 +86,19 @@ export class ObservableConfigProxy implements ConfigService { getOptional(key?: string): T | undefined { return this.select(false)?.getOptional(key); } - getConfig(key: string): ConfigService { + getConfig(key: string): Config { return new ObservableConfigProxy(this, key); } - getOptionalConfig(key: string): ConfigService | undefined { + getOptionalConfig(key: string): Config | undefined { if (this.select(false)?.has(key)) { return new ObservableConfigProxy(this, key); } return undefined; } - getConfigArray(key: string): ConfigService[] { + getConfigArray(key: string): Config[] { return this.select(true).getConfigArray(key); } - getOptionalConfigArray(key: string): ConfigService[] | undefined { + getOptionalConfigArray(key: string): Config[] | undefined { return this.select(false)?.getOptionalConfigArray(key); } getNumber(key: string): number { diff --git a/packages/backend-app-api/src/http/MiddlewareFactory.ts b/packages/backend-app-api/src/http/MiddlewareFactory.ts index 774e784d1d..bd78d77045 100644 --- a/packages/backend-app-api/src/http/MiddlewareFactory.ts +++ b/packages/backend-app-api/src/http/MiddlewareFactory.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { ConfigService, LoggerService } from '@backstage/backend-plugin-api'; +import { + RootConfigService, + LoggerService, +} from '@backstage/backend-plugin-api'; import { Request, Response, @@ -47,7 +50,7 @@ import { NotImplementedError } from '@backstage/errors'; * @public */ export interface MiddlewareFactoryOptions { - config: ConfigService; + config: RootConfigService; logger: LoggerService; } @@ -78,7 +81,7 @@ export interface MiddlewareFactoryErrorOptions { * @public */ export class MiddlewareFactory { - #config: ConfigService; + #config: RootConfigService; #logger: LoggerService; /** diff --git a/packages/backend-app-api/src/lib/DependencyGraph.test.ts b/packages/backend-app-api/src/lib/DependencyGraph.test.ts new file mode 100644 index 0000000000..65efac0252 --- /dev/null +++ b/packages/backend-app-api/src/lib/DependencyGraph.test.ts @@ -0,0 +1,245 @@ +/* + * 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 { DependencyGraph } from './DependencyGraph'; + +describe('DependencyGraph', () => { + it('should be empty', async () => { + const empty = DependencyGraph.fromMap({}); + expect(empty.findUnsatisfiedDeps()).toEqual([]); + expect(empty.detectCircularDependency()).toBeUndefined(); + await expect( + empty.parallelTopologicalTraversal(async id => id), + ).resolves.toEqual([]); + }); + + describe('detectCircularDependency', () => { + it('should return undefined with not deps', () => { + expect( + DependencyGraph.fromMap({ + 1: {}, + 2: {}, + 3: {}, + 4: {}, + }).detectCircularDependency(), + ).toBeUndefined(); + }); + + it('should return undefined with no circular deps', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { consumes: ['a'], provides: ['b', 'c'] }, + 3: { consumes: ['b'] }, + 4: { consumes: ['c'] }, + }).detectCircularDependency(), + ).toBeUndefined(); + }); + + it('should detect an immediate circular dep', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'], consumes: ['a'] }, + }).detectCircularDependency(), + ).toEqual(['1', '1']); + }); + + it('should detect a small circular dep', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'], consumes: ['b'] }, + 2: { provides: ['b'], consumes: ['a'] }, + }).detectCircularDependency(), + ).toEqual(['1', '2', '1']); + }); + + it('should detect a larger distant circular dep', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { provides: ['b'], consumes: ['a', 'e'] }, + 3: { provides: ['c'], consumes: ['b'] }, + 4: { provides: ['d', 'e'], consumes: ['c', 'a'] }, + }).detectCircularDependency(), + ).toEqual(['2', '3', '4', '2']); + }); + }); + + describe('findUnsatisfiedDeps', () => { + it('should return nothing with no deps', () => { + expect( + DependencyGraph.fromMap({ + 1: {}, + 2: {}, + 3: {}, + 4: {}, + }).findUnsatisfiedDeps(), + ).toEqual([]); + }); + + it('should return nothing when all deps are satisfied', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { consumes: ['a'], provides: ['b', 'c'] }, + 3: { consumes: ['b'] }, + 4: { consumes: ['c'] }, + }).findUnsatisfiedDeps(), + ).toEqual([]); + }); + + it('should find a single unsatisfied dep', async () => { + expect( + DependencyGraph.fromMap({ + 1: { consumes: ['a'] }, + }).findUnsatisfiedDeps(), + ).toEqual([{ value: '1', unsatisfied: ['a'] }]); + }); + + it('should handle circular dependencies', async () => { + expect( + DependencyGraph.fromMap({ + 1: { consumes: ['a'], provides: ['a'] }, + }).findUnsatisfiedDeps(), + ).toEqual([]); + + expect( + DependencyGraph.fromMap({ + 1: { consumes: ['a'], provides: ['b'] }, + 2: { consumes: ['b'], provides: ['a'] }, + }).findUnsatisfiedDeps(), + ).toEqual([]); + + expect( + DependencyGraph.fromMap({ + 1: { consumes: ['a'] }, + 2: { consumes: ['b'], provides: ['c'] }, + 3: { consumes: ['c'], provides: ['a', 'b'] }, + }).findUnsatisfiedDeps(), + ).toEqual([]); + }); + + it('should find multiple unsatisfied deps for one node', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'], consumes: ['b'] }, + 2: { provides: ['b'], consumes: ['a', 'd', 'e'] }, + }).findUnsatisfiedDeps(), + ).toEqual([{ value: '2', unsatisfied: ['d', 'e'] }]); + }); + + it('should find multiple unsatisfied deps for multiple nodes', async () => { + expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { provides: ['b'], consumes: ['a', 'd', 'e'] }, + 3: { provides: [], consumes: ['b'] }, + 4: { provides: [], consumes: ['c', 'a'] }, + }).findUnsatisfiedDeps(), + ).toEqual([ + { value: '2', unsatisfied: ['d', 'e'] }, + { value: '4', unsatisfied: ['c'] }, + ]); + }); + }); + + describe('parallelTopologicalTraversal', () => { + it('should traverse with no deps', async () => { + await expect( + DependencyGraph.fromMap({ + 1: {}, + 2: {}, + 3: {}, + 4: {}, + }).parallelTopologicalTraversal(async id => id), + ).resolves.toEqual(['1', '2', '3', '4']); + }); + + it('should traverse with a few deps', async () => { + await expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { consumes: ['a'], provides: ['b', 'c'] }, + 3: { consumes: ['b'] }, + 4: { consumes: ['c'] }, + }).parallelTopologicalTraversal(async id => id), + ).resolves.toEqual(['1', '2', '3', '4']); + }); + + it('should traverse in reverse', async () => { + await expect( + DependencyGraph.fromMap({ + 1: { consumes: ['c'] }, + 2: { provides: ['c'], consumes: ['b'] }, + 3: { provides: ['b'], consumes: ['a'] }, + 4: { provides: ['a'] }, + }).parallelTopologicalTraversal(async id => id), + ).resolves.toEqual(['4', '3', '2', '1']); + }); + + it('should execute in parallel', async () => { + await expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { provides: ['b'], consumes: ['a'] }, + 3: { provides: ['c'], consumes: ['a'] }, + 4: { consumes: ['b'] }, + 5: { consumes: ['c'] }, + }).parallelTopologicalTraversal(async id => id), + ).resolves.toEqual(['1', '2', '3', '4', '5']); + + // Same as above, but with 2 being delayed + await expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { provides: ['b'], consumes: ['a'] }, + 3: { provides: ['c'], consumes: ['a'] }, + 4: { consumes: ['b'] }, + 5: { consumes: ['c'] }, + }).parallelTopologicalTraversal(async id => { + // When delaying 2 we expect 3 and 5 to complete before 2 and 4 + if (id === '2') { + await new Promise(resolve => setTimeout(resolve, 100)); + } + return id; + }), + ).resolves.toEqual(['1', '3', '5', '2', '4']); + }); + + it('should detect circular dependencies', async () => { + await expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'], consumes: ['a'] }, + }).parallelTopologicalTraversal(async id => id), + ).rejects.toThrow('Circular dependency detected'); + + await expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'], consumes: ['b'] }, + 2: { provides: ['b'], consumes: ['a'] }, + }).parallelTopologicalTraversal(async id => id), + ).rejects.toThrow('Circular dependency detected'); + + await expect( + DependencyGraph.fromMap({ + 1: { provides: ['a'] }, + 2: { provides: ['c'], consumes: ['a', 'b'] }, + 3: { provides: ['b'], consumes: ['a', 'c'] }, + }).parallelTopologicalTraversal(async id => id), + ).rejects.toThrow('Circular dependency detected'); + }); + }); +}); diff --git a/packages/backend-app-api/src/lib/DependencyGraph.ts b/packages/backend-app-api/src/lib/DependencyGraph.ts new file mode 100644 index 0000000000..8fd679f555 --- /dev/null +++ b/packages/backend-app-api/src/lib/DependencyGraph.ts @@ -0,0 +1,197 @@ +/* + * 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. + */ + +interface NodeInput { + value: T; + consumes?: Iterable; + provides?: Iterable; +} + +/** @internal */ +class Node { + static from(input: NodeInput) { + return new Node( + input.value, + input.consumes ? new Set(input.consumes) : new Set(), + input.provides ? new Set(input.provides) : new Set(), + ); + } + + private constructor( + readonly value: T, + readonly consumes: Set, + readonly provides: Set, + ) {} +} + +/** + * Internal helper to help validate and traverse a dependency graph. + * @internal + */ +export class DependencyGraph { + static fromMap( + nodes: Record, 'value'>>, + ): DependencyGraph { + return this.fromIterable( + Object.entries(nodes).map(([key, node]) => ({ + value: String(key), + ...node, + })), + ); + } + + static fromIterable( + nodeInputs: Iterable>, + ): DependencyGraph { + const nodes = new Array>(); + for (const nodeInput of nodeInputs) { + nodes.push(Node.from(nodeInput)); + } + + return new DependencyGraph(nodes); + } + + #nodes: Array>; + #allProvided: Set; + + private constructor(nodes: Array>) { + this.#nodes = nodes; + this.#allProvided = new Set(); + + for (const node of this.#nodes.values()) { + for (const produced of node.provides) { + this.#allProvided.add(produced); + } + } + } + + // Find all nodes that consume dependencies that are not provided by any other node + findUnsatisfiedDeps(): Array<{ value: T; unsatisfied: string[] }> { + const unsatisfiedDependencies = []; + for (const node of this.#nodes.values()) { + const unsatisfied = Array.from(node.consumes).filter( + id => !this.#allProvided.has(id), + ); + if (unsatisfied.length > 0) { + unsatisfiedDependencies.push({ value: node.value, unsatisfied }); + } + } + return unsatisfiedDependencies; + } + + // Detect circular dependencies within the graph, returning the path of nodes that + // form a cycle, with the same node as the first and last element of the array. + detectCircularDependency(): T[] | undefined { + for (const startNode of this.#nodes) { + const visited = new Set>(); + const stack = new Array<[node: Node, path: T[]]>([ + startNode, + [startNode.value], + ]); + + while (stack.length > 0) { + const [node, path] = stack.pop()!; + if (visited.has(node)) { + continue; + } + visited.add(node); + for (const produced of node.provides) { + const consumerNodes = this.#nodes.filter(other => + other.consumes.has(produced), + ); + for (const consumer of consumerNodes) { + if (consumer === startNode) { + return [...path, startNode.value]; + } + if (!visited.has(consumer)) { + stack.push([consumer, [...path, consumer.value]]); + } + } + } + } + } + return undefined; + } + + /** + * Traverses the dependency graph in topological order, calling the provided + * function for each node and waiting for it to resolve. + * + * The nodes are traversed in parallel, but in such a way that no node is + * visited before all of its dependencies. + * + * Dependencies of nodes that are not produced by any other nodes will be ignored. + */ + async parallelTopologicalTraversal( + fn: (value: T) => Promise, + ): Promise { + const allProvided = this.#allProvided; + const producedSoFar = new Set(); + const waiting = new Set(this.#nodes.values()); + const visited = new Set>(); + const results = new Array(); + let inFlight = 0; // Keep track of how many callbacks are in flight, so that we know if we got stuck + + // Find all nodes that have no dependencies that have not already been produced by visited nodes + async function processMoreNodes() { + if (waiting.size === 0) { + return; + } + const nodesToProcess = []; + for (const node of waiting) { + let ready = true; + for (const consumed of node.consumes) { + if (allProvided.has(consumed) && !producedSoFar.has(consumed)) { + ready = false; + continue; + } + } + if (ready) { + nodesToProcess.push(node); + } + } + + for (const node of nodesToProcess) { + waiting.delete(node); + } + + if (nodesToProcess.length === 0 && inFlight === 0) { + // We expect the caller to check for circular dependencies before + // traversal, so this error should never happen + throw new Error('Circular dependency detected'); + } + + await Promise.all(nodesToProcess.map(processNode)); + } + + // Process an individual node, and then add its produced dependencies to the set of available products + async function processNode(node: Node) { + visited.add(node); + inFlight += 1; + + const result = await fn(node.value); + results.push(result); + + node.provides.forEach(produced => producedSoFar.add(produced)); + inFlight -= 1; + await processMoreNodes(); + } + + await processMoreNodes(); + + return results; + } +} diff --git a/packages/backend-app-api/src/services/implementations/cache/cacheServiceFactory.ts b/packages/backend-app-api/src/services/implementations/cache/cacheServiceFactory.ts index 2208249280..b91356a13a 100644 --- a/packages/backend-app-api/src/services/implementations/cache/cacheServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/cache/cacheServiceFactory.ts @@ -24,7 +24,7 @@ import { export const cacheServiceFactory = createServiceFactory({ service: coreServices.cache, deps: { - config: coreServices.config, + config: coreServices.rootConfig, plugin: coreServices.pluginMetadata, }, async createRootContext({ config }) { diff --git a/packages/backend-app-api/src/services/implementations/config/index.ts b/packages/backend-app-api/src/services/implementations/config/index.ts index 690f1e2d36..1775ef2efc 100644 --- a/packages/backend-app-api/src/services/implementations/config/index.ts +++ b/packages/backend-app-api/src/services/implementations/config/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { configServiceFactory } from './configServiceFactory'; -export type { ConfigFactoryOptions } from './configServiceFactory'; +export { rootConfigServiceFactory } from './rootConfigServiceFactory'; +export type { RootConfigFactoryOptions } from './rootConfigServiceFactory'; diff --git a/packages/backend-app-api/src/services/implementations/config/configServiceFactory.ts b/packages/backend-app-api/src/services/implementations/config/rootConfigServiceFactory.ts similarity index 87% rename from packages/backend-app-api/src/services/implementations/config/configServiceFactory.ts rename to packages/backend-app-api/src/services/implementations/config/rootConfigServiceFactory.ts index d3b29ce545..1fa8d792ad 100644 --- a/packages/backend-app-api/src/services/implementations/config/configServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/config/rootConfigServiceFactory.ts @@ -24,7 +24,7 @@ import { } from '@backstage/config-loader'; /** @public */ -export interface ConfigFactoryOptions { +export interface RootConfigFactoryOptions { /** * Process arguments to use instead of the default `process.argv()`. */ @@ -37,9 +37,9 @@ export interface ConfigFactoryOptions { } /** @public */ -export const configServiceFactory = createServiceFactory( - (options?: ConfigFactoryOptions) => ({ - service: coreServices.config, +export const rootConfigServiceFactory = createServiceFactory( + (options?: RootConfigFactoryOptions) => ({ + service: coreServices.rootConfig, deps: {}, async factory() { const source = ConfigSources.default({ diff --git a/packages/backend-app-api/src/services/implementations/database/databaseServiceFactory.ts b/packages/backend-app-api/src/services/implementations/database/databaseServiceFactory.ts index 05e2f1deb9..139609b6c1 100644 --- a/packages/backend-app-api/src/services/implementations/database/databaseServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/database/databaseServiceFactory.ts @@ -25,7 +25,7 @@ import { ConfigReader } from '@backstage/config'; export const databaseServiceFactory = createServiceFactory({ service: coreServices.database, deps: { - config: coreServices.config, + config: coreServices.rootConfig, lifecycle: coreServices.lifecycle, pluginMetadata: coreServices.pluginMetadata, }, diff --git a/packages/backend-app-api/src/services/implementations/discovery/discoveryServiceFactory.ts b/packages/backend-app-api/src/services/implementations/discovery/discoveryServiceFactory.ts index 7e2bb6b21e..6bdc4b4856 100644 --- a/packages/backend-app-api/src/services/implementations/discovery/discoveryServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/discovery/discoveryServiceFactory.ts @@ -24,7 +24,7 @@ import { export const discoveryServiceFactory = createServiceFactory({ service: coreServices.discovery, deps: { - config: coreServices.config, + config: coreServices.rootConfig, }, async factory({ config }) { return HostDiscovery.fromConfig(config); diff --git a/packages/backend-app-api/src/services/implementations/permissions/permissionsServiceFactory.ts b/packages/backend-app-api/src/services/implementations/permissions/permissionsServiceFactory.ts index 38cba73eda..3e5b7da9dd 100644 --- a/packages/backend-app-api/src/services/implementations/permissions/permissionsServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/permissions/permissionsServiceFactory.ts @@ -24,7 +24,7 @@ import { ServerPermissionClient } from '@backstage/plugin-permission-node'; export const permissionsServiceFactory = createServiceFactory({ service: coreServices.permissions, deps: { - config: coreServices.config, + config: coreServices.rootConfig, discovery: coreServices.discovery, tokenManager: coreServices.tokenManager, }, diff --git a/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterServiceFactory.ts b/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterServiceFactory.ts index 5eaf8aa152..4c410e25ea 100644 --- a/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/rootHttpRouter/rootHttpRouterServiceFactory.ts @@ -15,7 +15,7 @@ */ import { - ConfigService, + RootConfigService, coreServices, createServiceFactory, LifecycleService, @@ -36,7 +36,7 @@ export interface RootHttpRouterConfigureContext { app: Express; middleware: MiddlewareFactory; routes: RequestHandler; - config: ConfigService; + config: RootConfigService; logger: LoggerService; lifecycle: LifecycleService; } @@ -70,7 +70,7 @@ export const rootHttpRouterServiceFactory = createServiceFactory( (options?: RootHttpRouterFactoryOptions) => ({ service: coreServices.rootHttpRouter, deps: { - config: coreServices.config, + config: coreServices.rootConfig, rootLogger: coreServices.rootLogger, lifecycle: coreServices.rootLifecycle, }, diff --git a/packages/backend-app-api/src/services/implementations/rootLogger/rootLoggerServiceFactory.ts b/packages/backend-app-api/src/services/implementations/rootLogger/rootLoggerServiceFactory.ts index 11bbd58050..bd45df383f 100644 --- a/packages/backend-app-api/src/services/implementations/rootLogger/rootLoggerServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/rootLogger/rootLoggerServiceFactory.ts @@ -26,7 +26,7 @@ import { createConfigSecretEnumerator } from '../../../config'; export const rootLoggerServiceFactory = createServiceFactory({ service: coreServices.rootLogger, deps: { - config: coreServices.config, + config: coreServices.rootConfig, }, async factory({ config }) { const logger = WinstonLogger.create({ diff --git a/packages/backend-app-api/src/services/implementations/scheduler/schedulerServiceFactory.test.ts b/packages/backend-app-api/src/services/implementations/scheduler/schedulerServiceFactory.test.ts index 7af9de44da..d771c5dd11 100644 --- a/packages/backend-app-api/src/services/implementations/scheduler/schedulerServiceFactory.test.ts +++ b/packages/backend-app-api/src/services/implementations/scheduler/schedulerServiceFactory.test.ts @@ -61,8 +61,7 @@ describe('schedulerFactory', () => { }); await startTestBackend({ - features: [plugin()], - services: [subject], + features: [plugin(), subject], }); }); }); diff --git a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts index b3726176b6..7417c18e23 100644 --- a/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/tokenManager/tokenManagerServiceFactory.ts @@ -24,7 +24,7 @@ import { ServerTokenManager } from '@backstage/backend-common'; export const tokenManagerServiceFactory = createServiceFactory({ service: coreServices.tokenManager, deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.rootLogger, }, createRootContext({ config, logger }) { diff --git a/packages/backend-app-api/src/services/implementations/urlReader/urlReaderServiceFactory.ts b/packages/backend-app-api/src/services/implementations/urlReader/urlReaderServiceFactory.ts index 83e594aa0b..6da71ac0fc 100644 --- a/packages/backend-app-api/src/services/implementations/urlReader/urlReaderServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/urlReader/urlReaderServiceFactory.ts @@ -24,7 +24,7 @@ import { export const urlReaderServiceFactory = createServiceFactory({ service: coreServices.urlReader, deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, }, async factory({ config, logger }) { diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index b05194f594..791b9b1c3a 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -20,10 +20,15 @@ import { coreServices, createBackendPlugin, createBackendModule, + createExtensionPoint, } from '@backstage/backend-plugin-api'; import { BackendInitializer } from './BackendInitializer'; -import { ServiceRegistry } from './ServiceRegistry'; -import { rootLifecycleServiceFactory } from '../services/implementations'; + +import { + lifecycleServiceFactory, + loggerServiceFactory, + rootLifecycleServiceFactory, +} from '../services/implementations'; const rootRef = createServiceRef<{ x: number }>({ id: '1', @@ -44,12 +49,23 @@ class MockLogger { } } +const baseFactories = [ + lifecycleServiceFactory(), + rootLifecycleServiceFactory(), + createServiceFactory({ + service: coreServices.rootLogger, + deps: {}, + factory: () => new MockLogger(), + })(), + loggerServiceFactory(), +]; + describe('BackendInitializer', () => { it('should initialize root scoped services', async () => { const rootFactory = jest.fn(); const pluginFactory = jest.fn(); - const registry = new ServiceRegistry([ + const services = [ createServiceFactory({ service: rootRef, deps: {}, @@ -66,17 +82,76 @@ describe('BackendInitializer', () => { deps: {}, factory: () => new MockLogger(), })(), - ]); + ]; - const init = new BackendInitializer(registry); + const init = new BackendInitializer(services); await init.start(); expect(rootFactory).toHaveBeenCalled(); expect(pluginFactory).not.toHaveBeenCalled(); }); + it('should initialize modules with extension points', async () => { + expect.assertions(3); + + const extensionPoint = createExtensionPoint<{ values: string[] }>({ + id: 'a', + }); + const init = new BackendInitializer(baseFactories); + + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'modA', + register(reg) { + reg.registerInit({ + deps: { extension: extensionPoint }, + async init({ extension }) { + expect(extension.values).toEqual(['b']); + extension.values.push('a'); + }, + }); + }, + })(), + ); + + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'modB', + register(reg) { + const values = ['b']; + reg.registerExtensionPoint(extensionPoint, { values }); + reg.registerInit({ + deps: {}, + async init() { + expect(values).toEqual(['b', 'a', 'c']); + }, + }); + }, + })(), + ); + + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'modC', + register(reg) { + reg.registerInit({ + deps: { extension: extensionPoint }, + async init({ extension }) { + expect(extension.values).toEqual(['b', 'a']); + extension.values.push('c'); + }, + }); + }, + })(), + ); + await init.start(); + }); + it('should forward errors when plugins fail to start', async () => { - const init = new BackendInitializer(new ServiceRegistry([])); + const init = new BackendInitializer([]); init.add( createBackendPlugin({ pluginId: 'test', @@ -96,7 +171,7 @@ describe('BackendInitializer', () => { }); it('should forward errors when modules fail to start', async () => { - const init = new BackendInitializer(new ServiceRegistry([])); + const init = new BackendInitializer([]); init.add( createBackendModule({ pluginId: 'test', @@ -117,7 +192,7 @@ describe('BackendInitializer', () => { }); it('should reject duplicate plugins', async () => { - const init = new BackendInitializer(new ServiceRegistry([])); + const init = new BackendInitializer([]); init.add( createBackendPlugin({ pluginId: 'test', @@ -146,7 +221,7 @@ describe('BackendInitializer', () => { }); it('should reject duplicate modules', async () => { - const init = new BackendInitializer(new ServiceRegistry([])); + const init = new BackendInitializer([]); init.add( createBackendModule({ pluginId: 'test', @@ -175,4 +250,78 @@ describe('BackendInitializer', () => { "Module 'mod' for plugin 'test' is already registered", ); }); + + it('should reject modules with circular dependencies', async () => { + const extA = createExtensionPoint({ id: 'a' }); + const extB = createExtensionPoint({ id: 'b' }); + const init = new BackendInitializer([ + rootLifecycleServiceFactory(), + createServiceFactory({ + service: coreServices.rootLogger, + deps: {}, + factory: () => new MockLogger(), + })(), + ]); + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'modA', + register(reg) { + reg.registerExtensionPoint(extA, 'a'); + reg.registerInit({ + deps: { ext: extB }, + async init() {}, + }); + }, + })(), + ); + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'modB', + register(reg) { + reg.registerExtensionPoint(extB, 'b'); + reg.registerInit({ + deps: { ext: extA }, + async init() {}, + }); + }, + })(), + ); + await expect(init.start()).rejects.toThrow( + "Circular dependency detected for modules of plugin 'test', 'modA' -> 'modB' -> 'modA'", + ); + }); + + it('should reject modules that depend on extension points other plugins', async () => { + const init = new BackendInitializer(baseFactories); + const extA = createExtensionPoint({ id: 'a' }); + init.add( + createBackendPlugin({ + pluginId: 'testA', + register(reg) { + reg.registerExtensionPoint(extA, 'a'); + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + })(), + ); + init.add( + createBackendModule({ + pluginId: 'testB', + moduleId: 'mod', + register(reg) { + reg.registerInit({ + deps: { ext: extA }, + async init() {}, + }); + }, + })(), + ); + await expect(init.start()).rejects.toThrow( + "Extension point registered for plugin 'testA' may not be used by module for plugin 'testB'", + ); + }); }); diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index 8c2b75e887..93d2f6942a 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -19,6 +19,7 @@ import { ExtensionPoint, coreServices, ServiceRef, + ServiceFactory, } from '@backstage/backend-plugin-api'; import { BackendLifecycleImpl } from '../services/implementations/rootLifecycle/rootLifecycleServiceFactory'; import { BackendPluginLifecycleImpl } from '../services/implementations/lifecycle/lifecycleServiceFactory'; @@ -26,7 +27,10 @@ import { EnumerableServiceHolder, ServiceOrExtensionPoint } from './types'; // Direct internal import to avoid duplication // eslint-disable-next-line @backstage/no-forbidden-package-imports import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring/types'; -import { ForwardedError } from '@backstage/errors'; +import { ForwardedError, ConflictError } from '@backstage/errors'; +import { featureDiscoveryServiceRef } from '@backstage/backend-plugin-api/alpha'; +import { DependencyGraph } from '../lib/DependencyGraph'; +import { ServiceRegistry } from './ServiceRegistry'; export interface BackendRegisterInit { consumes: Set; @@ -40,11 +44,16 @@ export interface BackendRegisterInit { export class BackendInitializer { #startPromise?: Promise; #features = new Array(); - #extensionPoints = new Map, unknown>(); - #serviceHolder: EnumerableServiceHolder; + #extensionPoints = new Map< + ExtensionPoint, + { impl: unknown; pluginId: string } + >(); + #serviceHolder: EnumerableServiceHolder | undefined; + #providedServiceFactories = new Array(); + #defaultApiFactories: ServiceFactory[]; - constructor(serviceHolder: EnumerableServiceHolder) { - this.#serviceHolder = serviceHolder; + constructor(defaultApiFactories: ServiceFactory[]) { + this.#defaultApiFactories = defaultApiFactories; } async #getInitDeps( @@ -55,13 +64,16 @@ export class BackendInitializer { const missingRefs = new Set(); for (const [name, ref] of Object.entries(deps)) { - const extensionPoint = this.#extensionPoints.get( - ref as ExtensionPoint, - ); - if (extensionPoint) { - result.set(name, extensionPoint); + const ep = this.#extensionPoints.get(ref as ExtensionPoint); + if (ep) { + if (ep.pluginId !== pluginId) { + throw new Error( + `Extension point registered for plugin '${ep.pluginId}' may not be used by module for plugin '${pluginId}'`, + ); + } + result.set(name, ep.impl); } else { - const impl = await this.#serviceHolder.get( + const impl = await this.#serviceHolder!.get( ref as ServiceRef, pluginId, ); @@ -87,18 +99,44 @@ export class BackendInitializer { if (this.#startPromise) { throw new Error('feature can not be added after the backend has started'); } + this.#addFeature(feature); + } + + #addFeature(feature: BackendFeature) { if (feature.$$type !== '@backstage/BackendFeature') { throw new Error( `Failed to add feature, invalid type '${feature.$$type}'`, ); } - const internalFeature = feature as InternalBackendFeature; - if (internalFeature.version !== 'v1') { + + if (isServiceFactory(feature)) { + if (feature.service.id === coreServices.pluginMetadata.id) { + throw new Error( + `The ${coreServices.pluginMetadata.id} service cannot be overridden`, + ); + } + if ( + this.#providedServiceFactories.find( + sf => sf.service.id === feature.service.id, + ) + ) { + throw new Error( + `Duplicate service implementations provided for ${feature.service.id}`, + ); + } + this.#providedServiceFactories.push(feature); + } else if (isInternalBackendFeature(feature)) { + if (feature.version !== 'v1') { + throw new Error( + `Failed to add feature, invalid version '${feature.version}'`, + ); + } + this.#features.push(feature); + } else { throw new Error( - `Failed to add feature, invalid version '${internalFeature.version}'`, + `Failed to add feature, invalid feature ${JSON.stringify(feature)}`, ); } - this.#features.push(internalFeature); } async start(): Promise { @@ -129,6 +167,23 @@ export class BackendInitializer { } async #doStart(): Promise { + this.#serviceHolder = new ServiceRegistry([ + ...this.#defaultApiFactories, + ...this.#providedServiceFactories, + ]); + + const featureDiscovery = await this.#serviceHolder.get( + featureDiscoveryServiceRef, + 'root', + ); + + if (featureDiscovery) { + const { features } = await featureDiscovery.getBackendFeatures(); + for (const feature of features) { + this.#addFeature(feature); + } + } + // Initialize all root scoped services for (const ref of this.#serviceHolder.getServiceRefs()) { if (ref.scope === 'root') { @@ -144,14 +199,17 @@ export class BackendInitializer { for (const r of feature.getRegistrations()) { const provides = new Set>(); - if (r.type === 'plugin') { + if (r.type === 'plugin' || r.type === 'module') { for (const [extRef, extImpl] of r.extensionPoints) { if (this.#extensionPoints.has(extRef)) { throw new Error( `ExtensionPoint with ID '${extRef.id}' is already registered`, ); } - this.#extensionPoints.set(extRef, extImpl); + this.#extensionPoints.set(extRef, { + impl: extImpl, + pluginId: r.pluginId, + }); provides.add(extRef); } } @@ -193,21 +251,41 @@ export class BackendInitializer { await Promise.all( allPluginIds.map(async pluginId => { // Modules are initialized before plugins, so that they can provide extension to the plugin - const modules = moduleInits.get(pluginId) ?? []; - await Promise.all( - Array.from(modules).map(async ([moduleId, moduleInit]) => { - const moduleDeps = await this.#getInitDeps( - moduleInit.init.deps, - pluginId, + const modules = moduleInits.get(pluginId); + if (modules) { + const tree = DependencyGraph.fromIterable( + Array.from(modules).map(([moduleId, moduleInit]) => ({ + value: { moduleId, moduleInit }, + // Relationships are reversed at this point since we're only interested in the extension points. + // If a modules provides extension point A we want it to be initialized AFTER all modules + // that depend on extension point A, so that they can provide their extensions. + consumes: Array.from(moduleInit.provides).map(p => p.id), + provides: Array.from(moduleInit.consumes).map(c => c.id), + })), + ); + const circular = tree.detectCircularDependency(); + if (circular) { + throw new ConflictError( + `Circular dependency detected for modules of plugin '${pluginId}', ${circular + .map(({ moduleId }) => `'${moduleId}'`) + .join(' -> ')}`, ); - await moduleInit.init.func(moduleDeps).catch(error => { - throw new ForwardedError( - `Module '${moduleId}' for plugin '${pluginId}' startup failed`, - error, + } + await tree.parallelTopologicalTraversal( + async ({ moduleId, moduleInit }) => { + const moduleDeps = await this.#getInitDeps( + moduleInit.init.deps, + pluginId, ); - }); - }), - ); + await moduleInit.init.func(moduleDeps).catch(error => { + throw new ForwardedError( + `Module '${moduleId}' for plugin '${pluginId}' startup failed`, + error, + ); + }); + }, + ); + } // Once all modules have been initialized, we can initialize the plugin itself const pluginInit = pluginInits.get(pluginId); @@ -259,7 +337,12 @@ export class BackendInitializer { if (!this.#startPromise) { return; } - await this.#startPromise; + + try { + await this.#startPromise; + } catch (error) { + // The startup failed, but we may still want to do cleanup so we continue silently + } const lifecycleService = await this.#getRootLifecycleImpl(); await lifecycleService.shutdown(); @@ -267,7 +350,7 @@ export class BackendInitializer { // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this async #getRootLifecycleImpl(): Promise { - const lifecycleService = await this.#serviceHolder.get( + const lifecycleService = await this.#serviceHolder!.get( coreServices.rootLifecycle, 'root', ); @@ -280,7 +363,7 @@ export class BackendInitializer { async #getPluginLifecycleImpl( pluginId: string, ): Promise { - const lifecycleService = await this.#serviceHolder.get( + const lifecycleService = await this.#serviceHolder!.get( coreServices.lifecycle, pluginId, ); @@ -290,3 +373,15 @@ export class BackendInitializer { throw new Error('Unexpected plugin lifecycle service implementation'); } } + +function isServiceFactory(feature: BackendFeature): feature is ServiceFactory { + return !!(feature as ServiceFactory).service; +} + +function isInternalBackendFeature( + feature: BackendFeature, +): feature is InternalBackendFeature { + return ( + typeof (feature as InternalBackendFeature).getRegistrations === 'function' + ); +} diff --git a/packages/backend-app-api/src/wiring/BackstageBackend.ts b/packages/backend-app-api/src/wiring/BackstageBackend.ts index c53f47b685..0ea55bc1c7 100644 --- a/packages/backend-app-api/src/wiring/BackstageBackend.ts +++ b/packages/backend-app-api/src/wiring/BackstageBackend.ts @@ -14,22 +14,19 @@ * limitations under the License. */ -import { ServiceFactory, BackendFeature } from '@backstage/backend-plugin-api'; +import { BackendFeature, ServiceFactory } from '@backstage/backend-plugin-api'; import { BackendInitializer } from './BackendInitializer'; -import { ServiceRegistry } from './ServiceRegistry'; import { Backend } from './types'; export class BackstageBackend implements Backend { - #services: ServiceRegistry; #initializer: BackendInitializer; - constructor(apiFactories: ServiceFactory[]) { - this.#services = new ServiceRegistry(apiFactories); - this.#initializer = new BackendInitializer(this.#services); + constructor(defaultServiceFactories: ServiceFactory[]) { + this.#initializer = new BackendInitializer(defaultServiceFactories); } - add(feature: BackendFeature): void { - this.#initializer.add(feature); + add(feature: BackendFeature | (() => BackendFeature)): void { + this.#initializer.add(typeof feature === 'function' ? feature() : feature); } async start(): Promise { diff --git a/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts b/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts index e698ade348..b60a24c8d8 100644 --- a/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts +++ b/packages/backend-app-api/src/wiring/ServiceRegistry.test.ts @@ -31,7 +31,7 @@ const sf1 = createServiceFactory({ async factory() { return { x: 1 }; }, -})(); +}); const ref2 = createServiceRef<{ x: number }>({ scope: 'root', @@ -43,14 +43,14 @@ const sf2 = createServiceFactory({ async factory() { return { x: 2 }; }, -})(); +}); const sf2b = createServiceFactory({ service: ref2, deps: {}, async factory() { return { x: 22 }; }, -})(); +}); const refDefault1 = createServiceRef<{ x: number }>({ id: '1', @@ -61,7 +61,7 @@ const refDefault1 = createServiceRef<{ x: number }>({ async factory() { return { x: 10 }; }, - })(), + }), }); const refDefault2a = createServiceRef<{ x: number }>({ @@ -95,7 +95,7 @@ describe('ServiceRegistry', () => { }); it('should return an implementation for a registered ref', async () => { - const registry = new ServiceRegistry([sf1]); + const registry = new ServiceRegistry([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 = new ServiceRegistry([sf1, sf2]); + const registry = new ServiceRegistry([sf1(), sf2()]); await expect(registry.get(ref1, 'catalog')).resolves.toEqual({ x: 1, @@ -131,7 +131,7 @@ describe('ServiceRegistry', () => { return { x: 2 }; }, }); - const registry = new ServiceRegistry([factory(), sf1]); + const registry = new ServiceRegistry([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'.", ); @@ -145,7 +145,7 @@ describe('ServiceRegistry', () => { return { x: rootDep.x }; }, }); - const registry = new ServiceRegistry([factory(), sf2]); + const registry = new ServiceRegistry([factory(), sf2()]); await expect(registry.get(ref1, 'catalog')).resolves.toEqual({ x: 2, }); @@ -160,7 +160,7 @@ describe('ServiceRegistry', () => { return { x: rootDep.x }; }, }); - const registry = new ServiceRegistry([factory(), sf2]); + const registry = new ServiceRegistry([factory(), sf2()]); await expect(registry.get(ref, 'catalog')).resolves.toEqual({ x: 2, }); @@ -182,7 +182,7 @@ describe('ServiceRegistry', () => { }); it('should use the last factory for each ref', async () => { - const registry = new ServiceRegistry([sf2, sf2b]); + const registry = new ServiceRegistry([sf2(), sf2b()]); await expect(registry.get(ref2, 'catalog')).resolves.toEqual({ x: 22, }); @@ -196,7 +196,7 @@ describe('ServiceRegistry', () => { }); it('should not use the defaultFactory from the ref if provided to the registry', async () => { - const registry = new ServiceRegistry([sf1]); + const registry = new ServiceRegistry([sf1()]); await expect(registry.get(refDefault1, 'catalog')).resolves.toEqual({ x: 1, }); diff --git a/packages/backend-app-api/src/wiring/ServiceRegistry.ts b/packages/backend-app-api/src/wiring/ServiceRegistry.ts index b5b55f67cf..b6de05b361 100644 --- a/packages/backend-app-api/src/wiring/ServiceRegistry.ts +++ b/packages/backend-app-api/src/wiring/ServiceRegistry.ts @@ -39,7 +39,7 @@ function toInternalServiceFactory( factory: ServiceFactory, ): InternalServiceFactory { const f = factory as InternalServiceFactory; - if (f.$$type !== '@backstage/ServiceFactory') { + if (f.$$type !== '@backstage/BackendFeature') { throw new Error(`Invalid service factory, bad type '${f.$$type}'`); } if (f.version !== 'v1') { @@ -49,10 +49,10 @@ function toInternalServiceFactory( } const pluginMetadataServiceFactory = createServiceFactory( - (options: { pluginId: string }) => ({ + (options?: { pluginId: string }) => ({ service: coreServices.pluginMetadata, deps: {}, - factory: async () => ({ getId: () => options.pluginId }), + factory: async () => ({ getId: () => options?.pluginId! }), }), ); diff --git a/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts b/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts index dc9a0cc085..1fd7fcbbec 100644 --- a/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts +++ b/packages/backend-app-api/src/wiring/createSpecializedBackend.test.ts @@ -22,13 +22,15 @@ import { createSpecializedBackend } from './createSpecializedBackend'; describe('createSpecializedBackend', () => { it('should create a backend without services', () => { - expect(() => createSpecializedBackend({ services: [] })).not.toThrow(); + expect(() => + createSpecializedBackend({ defaultServiceFactories: [] }), + ).not.toThrow(); }); it('should throw on duplicate service implementations', () => { expect(() => createSpecializedBackend({ - services: [ + defaultServiceFactories: [ createServiceFactory({ service: coreServices.rootLifecycle, deps: {}, @@ -55,7 +57,7 @@ describe('createSpecializedBackend', () => { it('should throw when providing a plugin metadata service implementation', () => { expect(() => createSpecializedBackend({ - services: [ + defaultServiceFactories: [ createServiceFactory({ service: coreServices.pluginMetadata, deps: {}, diff --git a/packages/backend-app-api/src/wiring/createSpecializedBackend.ts b/packages/backend-app-api/src/wiring/createSpecializedBackend.ts index 15e51f7c4c..f5c4d8b152 100644 --- a/packages/backend-app-api/src/wiring/createSpecializedBackend.ts +++ b/packages/backend-app-api/src/wiring/createSpecializedBackend.ts @@ -24,7 +24,7 @@ import { Backend, CreateSpecializedBackendOptions } from './types'; export function createSpecializedBackend( options: CreateSpecializedBackendOptions, ): Backend { - const services = options.services.map(sf => + const services = options.defaultServiceFactories.map(sf => typeof sf === 'function' ? sf() : sf, ); diff --git a/packages/backend-app-api/src/wiring/types.ts b/packages/backend-app-api/src/wiring/types.ts index ffedb40324..552a69a792 100644 --- a/packages/backend-app-api/src/wiring/types.ts +++ b/packages/backend-app-api/src/wiring/types.ts @@ -25,7 +25,7 @@ import { * @public */ export interface Backend { - add(feature: BackendFeature): void; + add(feature: BackendFeature | (() => BackendFeature)): void; start(): Promise; stop(): Promise; } @@ -34,7 +34,7 @@ export interface Backend { * @public */ export interface CreateSpecializedBackendOptions { - services: ServiceFactoryOrFunction[]; + defaultServiceFactories: ServiceFactoryOrFunction[]; } export interface ServiceHolder { diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 2977d54c01..030223606f 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/backend-common +## 0.19.2 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 443afcf7f567: To improve performance, `GerritUrlReader.readTree()` now uses Gitiles to fetch an archive instead of cloning the repository. + If `gitilesBaseUrl` is not configured, `readTree` still uses Git to clone the repository. + + Added `stripFirstDirectory` option to `ReadTreeResponseFactory.fromTarArchive()`, allowing to disable stripping first directory + for `tar` archives. + +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-app-api@0.5.0 + - @backstage/config-loader@1.4.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/integration-aws-node@0.1.5 + - @backstage/backend-dev-utils@0.1.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.19.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/config-loader@1.4.0-next.1 + +## 0.19.2-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/config-loader@1.4.0-next.1 + - @backstage/backend-app-api@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/backend-dev-utils@0.1.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.19.2-next.0 ### Patch Changes diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index d40027cba9..f8e535c8a7 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -18,7 +18,6 @@ import { CacheService as CacheClient } from '@backstage/backend-plugin-api'; import { CacheServiceOptions as CacheClientOptions } from '@backstage/backend-plugin-api'; import { CacheServiceSetOptions as CacheClientSetOptions } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; -import { ConfigService } from '@backstage/backend-plugin-api'; import cors from 'cors'; import Docker from 'dockerode'; import { ErrorRequestHandler } from 'express'; @@ -51,6 +50,7 @@ import { ReadTreeResponseFile } from '@backstage/backend-plugin-api'; import { ReadUrlOptions } from '@backstage/backend-plugin-api'; import { ReadUrlResponse } from '@backstage/backend-plugin-api'; import { RequestHandler } from 'express'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { Router } from 'express'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { SearchOptions } from '@backstage/backend-plugin-api'; @@ -525,7 +525,7 @@ export const legacyPlugin: ( TransformedEnv< { cache: CacheClient; - config: ConfigService; + config: RootConfigService; database: PluginDatabaseManager; discovery: PluginEndpointDiscovery; logger: LoggerService; @@ -610,7 +610,9 @@ export interface ReadTreeResponseFactory { ): Promise; // (undocumented) fromTarArchive( - options: ReadTreeResponseFactoryOptions, + options: ReadTreeResponseFactoryOptions & { + stripFirstDirectory?: boolean; + }, ): Promise; // (undocumented) fromZipArchive( diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 6e561fa564..fd9b9cb0df 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.19.2-next.0", + "version": "0.19.2", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-common/src/legacy.ts b/packages/backend-common/src/legacy.ts index 408966f3bc..df1030e6b6 100644 --- a/packages/backend-common/src/legacy.ts +++ b/packages/backend-common/src/legacy.ts @@ -108,7 +108,7 @@ export function makeLegacyPlugin< export const legacyPlugin = makeLegacyPlugin( { cache: coreServices.cache, - config: coreServices.config, + config: coreServices.rootConfig, database: coreServices.database, discovery: coreServices.discovery, logger: coreServices.logger, diff --git a/packages/backend-common/src/reading/GerritUrlReader.test.ts b/packages/backend-common/src/reading/GerritUrlReader.test.ts index 4e0b97fc58..9de9aac33d 100644 --- a/packages/backend-common/src/reading/GerritUrlReader.test.ts +++ b/packages/backend-common/src/reading/GerritUrlReader.test.ts @@ -37,14 +37,16 @@ const treeResponseFactory = DefaultReadTreeResponseFactory.create({ config: new ConfigReader({}), }); +const cloneMock = jest.fn(() => Promise.resolve()); jest.mock('../scm', () => ({ Git: { fromAuth: () => ({ - clone: jest.fn(() => Promise.resolve({})), + clone: cloneMock, }), }, })); +// Gerrit processor without a gitilesBaseUrl configured const gerritProcessor = new GerritUrlReader( new GerritIntegration( readGerritIntegrationConfig( @@ -57,6 +59,21 @@ const gerritProcessor = new GerritUrlReader( '/tmp', ); +// Gerrit processor with a gitilesBaseUrl configured. +// Use to test readTree with Gitiles archive download. +const gerritProcessorWithGitiles = new GerritUrlReader( + new GerritIntegration( + readGerritIntegrationConfig( + new ConfigReader({ + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.com/gitiles', + }), + ), + ), + { treeResponseFactory }, + '/tmp', +); + const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => { return GerritUrlReader.factory({ config: new ConfigReader(config), @@ -217,9 +234,17 @@ describe('GerritUrlReader', () => { path.resolve(__dirname, '__fixtures__/gerrit/branch-info-response.txt'), ); const treeUrl = 'https://gerrit.com/app/web/+/refs/heads/master/'; + const treeUrlGitiles = + 'https://gerrit.com/gitiles/app/web/+/refs/heads/master/'; const etag = '52432507a70b677b5674b019c9a46b2e9f29d0a1'; - const mkdocsContent = 'great content'; + const mkdocsContent = 'a repo fetched using git clone'; const mdContent = 'doc'; + const repoArchiveBuffer = fs.readFileSync( + path.resolve(__dirname, '__fixtures__/gerrit/gerrit-master.tar.gz'), + ); + const repoArchiveDocsBuffer = fs.readFileSync( + path.resolve(__dirname, '__fixtures__/gerrit/gerrit-master-docs.tar.gz'), + ); beforeEach(() => { mockFs({ @@ -229,6 +254,35 @@ describe('GerritUrlReader', () => { }); const spy = jest.spyOn(fs, 'mkdtemp'); spy.mockImplementation(() => '/tmp/gerrit-clone-123abc'); + + worker.use( + rest.get( + 'https://gerrit.com/gitiles/app/web/\\+archive/refs/heads/master.tar.gz', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.set( + 'content-disposition', + 'attachment; filename=web-refs/heads/master.tar.gz', + ), + ctx.body(repoArchiveBuffer), + ), + ), + rest.get( + 'https://gerrit.com/gitiles/app/web/\\+archive/refs/heads/master/docs.tar.gz', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.set( + 'content-disposition', + 'attachment; filename=web-refs/heads/master-docs.tar.gz', + ), + ctx.body(repoArchiveDocsBuffer), + ), + ), + ); }); afterEach(() => { @@ -236,7 +290,32 @@ describe('GerritUrlReader', () => { jest.clearAllMocks(); }); - it('reads the wanted files correctly.', async () => { + it('reads the wanted files correctly using gitiles.', async () => { + worker.use( + rest.get(branchAPIUrl, (_, res, ctx) => { + return res(ctx.status(200), ctx.body(branchAPIresponse)); + }), + ); + + const response = await gerritProcessorWithGitiles.readTree( + treeUrlGitiles, + ); + + expect(response.etag).toBe(etag); + + const files = await response.files(); + expect(files.length).toBe(2); + + const docsYaml = await files[0].content(); + expect(docsYaml.toString()).toBe('# Test\n'); + + const mdFile = await files[1].content(); + expect(mdFile.toString()).toBe('site_name: Test\n'); + + expect(cloneMock).not.toHaveBeenCalled(); + }); + + it('reads the wanted files correctly using git clone.', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { return res(ctx.status(200), ctx.body(branchAPIresponse)); @@ -255,6 +334,8 @@ describe('GerritUrlReader', () => { const mdFile = await files[1].content(); expect(mdFile.toString()).toBe(mdContent); + + expect(cloneMock).toHaveBeenCalled(); }); it('throws NotModifiedError for matching etags.', async () => { @@ -291,7 +372,29 @@ describe('GerritUrlReader', () => { await expect(gerritProcessor.readTree(treeUrl)).rejects.toThrow(Error); }); - it('should returns wanted files with a subpath', async () => { + it('should returns wanted files with a subpath using gitiles', async () => { + worker.use( + rest.get(branchAPIUrl, (_, res, ctx) => { + return res(ctx.status(200), ctx.body(branchAPIresponse)); + }), + ); + + const response = await gerritProcessorWithGitiles.readTree( + `${treeUrlGitiles}/docs`, + ); + + expect(response.etag).toBe(etag); + + const files = await response.files(); + expect(files.length).toBe(1); + + const mdFile = await files[0].content(); + expect(mdFile.toString()).toBe('# Test\n'); + + expect(cloneMock).not.toHaveBeenCalled(); + }); + + it('should returns wanted files with a subpath using git clone', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { return res(ctx.status(200), ctx.body(branchAPIresponse)); @@ -307,6 +410,8 @@ describe('GerritUrlReader', () => { const mdFile = await files[0].content(); expect(mdFile.toString()).toBe(mdContent); + + expect(cloneMock).toHaveBeenCalled(); }); }); }); diff --git a/packages/backend-common/src/reading/GerritUrlReader.ts b/packages/backend-common/src/reading/GerritUrlReader.ts index 8ac5452a4c..f4a5fdb9f5 100644 --- a/packages/backend-common/src/reading/GerritUrlReader.ts +++ b/packages/backend-common/src/reading/GerritUrlReader.ts @@ -14,16 +14,17 @@ * limitations under the License. */ -import { Git } from '../scm'; import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { GerritIntegration, - getGerritCloneRepoUrl, + ScmIntegrations, + buildGerritGitilesArchiveUrl, getGerritBranchApiUrl, + getGerritCloneRepoUrl, getGerritFileContentsApiUrl, getGerritRequestOptions, - parseGerritJsonResponse, parseGerritGitilesUrl, + parseGerritJsonResponse, } from '@backstage/integration'; import { Base64Decode } from 'base64-stream'; import concatStream from 'concat-stream'; @@ -31,20 +32,20 @@ import fs from 'fs-extra'; import fetch, { Response } from 'node-fetch'; import os from 'os'; import { join as joinPath } from 'path'; +import { Readable, pipeline as pipelineCb } from 'stream'; import tar from 'tar'; -import { pipeline as pipelineCb, Readable } from 'stream'; import { promisify } from 'util'; +import { Git } from '../scm'; import { - ReaderFactory, ReadTreeOptions, ReadTreeResponse, ReadTreeResponseFactory, ReadUrlOptions, ReadUrlResponse, + ReaderFactory, SearchResponse, UrlReader, } from './types'; -import { ScmIntegrations } from '@backstage/integration'; const pipeline = promisify(pipelineCb); @@ -59,6 +60,8 @@ const createTemporaryDirectory = async (workDir: string): Promise => * way we are depending on that there is a Gitiles installation somewhere * that we can link to. It is perfectly possible to integrate Gerrit with * Backstage without Gitiles since all API calls goes directly to Gerrit. + * However if Gitiles is configured, readTree will use it to fetch + * an archive instead of cloning the repository. * * The "host" variable in the config is the Gerrit host. The address where * Gitiles is installed may be on the same host but it could be on a @@ -125,6 +128,7 @@ export class GerritUrlReader implements UrlReader { } catch (e) { throw new Error(`Unable to read gerrit file ${url}, ${e}`); } + if (response.ok) { let responseBody: string; return { @@ -152,7 +156,6 @@ export class GerritUrlReader implements UrlReader { url: string, options?: ReadTreeOptions, ): Promise { - const { filePath } = parseGerritGitilesUrl(this.integration.config, url); const apiUrl = getGerritBranchApiUrl(this.integration.config, url); let response: Response; try { @@ -180,6 +183,30 @@ export class GerritUrlReader implements UrlReader { throw new NotModifiedError(); } + if ( + this.integration.config.gitilesBaseUrl !== this.integration.config.baseUrl + ) { + return this.readTreeFromGitiles(url, branchInfo.revision, options); + } + return this.readTreeFromGitClone(url, branchInfo.revision, options); + } + + async search(): Promise { + throw new Error('GerritReader does not implement search'); + } + + toString() { + const { host, password } = this.integration.config; + return `gerrit{host=${host},authed=${Boolean(password)}}`; + } + + private async readTreeFromGitClone( + url: string, + revision: string, + options?: ReadTreeOptions, + ) { + const { filePath } = parseGerritGitilesUrl(this.integration.config, url); + const git = Git.fromAuth({ username: this.integration.config.username, password: this.integration.config.password, @@ -192,7 +219,7 @@ export class GerritUrlReader implements UrlReader { await git.clone({ url: cloneUrl, dir: joinPath(tempDir, 'repo'), - ref: branchInfo.revision, + ref: revision, depth: 1, }); @@ -206,7 +233,7 @@ export class GerritUrlReader implements UrlReader { return await this.deps.treeResponseFactory.fromTarArchive({ stream: tarArchive, subpath: filePath === '/' ? undefined : filePath, - etag: branchInfo.revision, + etag: revision, filter: options?.filter, }); } catch (error) { @@ -216,12 +243,47 @@ export class GerritUrlReader implements UrlReader { } } - async search(): Promise { - throw new Error('GerritReader does not implement search'); - } + private async readTreeFromGitiles( + url: string, + revision: string, + options?: ReadTreeOptions, + ) { + const { branch, filePath, project } = parseGerritGitilesUrl( + this.integration.config, + url, + ); + const archiveUrl = buildGerritGitilesArchiveUrl( + this.integration.config, + project, + branch, + filePath, + ); + const archiveResponse = await fetch(archiveUrl, { + ...getGerritRequestOptions(this.integration.config), + // TODO(freben): The signal cast is there because pre-3.x versions of + // node-fetch have a very slightly deviating AbortSignal type signature. + // The difference does not affect us in practice however. The cast can + // be removed after we support ESM for CLI dependencies and migrate to + // version 3 of node-fetch. + // https://github.com/backstage/backstage/issues/8242 + signal: options?.signal as any, + }); - toString() { - const { host, password } = this.integration.config; - return `gerrit{host=${host},authed=${Boolean(password)}}`; + if (archiveResponse.status === 404) { + throw new NotFoundError(`Not found: ${archiveUrl}`); + } + + if (!archiveResponse.ok) { + throw new Error( + `${url} could not be read as ${archiveUrl}, ${archiveResponse.status} ${archiveResponse.statusText}`, + ); + } + + return await this.deps.treeResponseFactory.fromTarArchive({ + stream: archiveResponse.body as unknown as Readable, + etag: revision, + filter: options?.filter, + stripFirstDirectory: false, + }); } } diff --git a/packages/backend-common/src/reading/__fixtures__/gerrit/gerrit-master-docs.tar.gz b/packages/backend-common/src/reading/__fixtures__/gerrit/gerrit-master-docs.tar.gz new file mode 100644 index 0000000000..425cf0876b Binary files /dev/null and b/packages/backend-common/src/reading/__fixtures__/gerrit/gerrit-master-docs.tar.gz differ diff --git a/packages/backend-common/src/reading/__fixtures__/gerrit/gerrit-master.tar.gz b/packages/backend-common/src/reading/__fixtures__/gerrit/gerrit-master.tar.gz new file mode 100644 index 0000000000..8a27502734 Binary files /dev/null and b/packages/backend-common/src/reading/__fixtures__/gerrit/gerrit-master.tar.gz differ diff --git a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts index 11a5ef168a..90c58c0efa 100644 --- a/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts +++ b/packages/backend-common/src/reading/tree/ReadTreeResponseFactory.ts @@ -37,7 +37,9 @@ export class DefaultReadTreeResponseFactory implements ReadTreeResponseFactory { constructor(private readonly workDir: string) {} async fromTarArchive( - options: ReadTreeResponseFactoryOptions, + options: ReadTreeResponseFactoryOptions & { + stripFirstDirectory?: boolean; + }, ): Promise { return new TarArchiveResponse( options.stream, @@ -45,6 +47,7 @@ export class DefaultReadTreeResponseFactory implements ReadTreeResponseFactory { this.workDir, options.etag, options.filter, + options.stripFirstDirectory ?? true, ); } diff --git a/packages/backend-common/src/reading/tree/TarArchiveResponse.ts b/packages/backend-common/src/reading/tree/TarArchiveResponse.ts index e1529c196a..294ec22609 100644 --- a/packages/backend-common/src/reading/tree/TarArchiveResponse.ts +++ b/packages/backend-common/src/reading/tree/TarArchiveResponse.ts @@ -44,6 +44,7 @@ export class TarArchiveResponse implements ReadTreeResponse { private readonly workDir: string, public readonly etag: string, private readonly filter?: (path: string, info: { size: number }) => boolean, + private readonly stripFirstDirectory: boolean = true, ) { if (subPath) { if (!subPath.endsWith('/')) { @@ -81,7 +82,9 @@ export class TarArchiveResponse implements ReadTreeResponse { // File path relative to the root extracted directory. Will remove the // top level dir name from the path since its name is hard to predetermine. - const relativePath = stripFirstDirectoryFromPath(entry.path); + const relativePath = this.stripFirstDirectory + ? stripFirstDirectoryFromPath(entry.path) + : entry.path; if (this.subPath) { if (!relativePath.startsWith(this.subPath)) { @@ -148,7 +151,10 @@ export class TarArchiveResponse implements ReadTreeResponse { // Equivalent of tar --strip-components=N // When no subPath is given, remove just 1 top level directory - const strip = this.subPath ? this.subPath.split('/').length : 1; + let strip = this.subPath ? this.subPath.split('/').length : 1; + if (!this.stripFirstDirectory) { + strip--; + } let filterError: Error | undefined = undefined; await pipeline( @@ -164,7 +170,9 @@ export class TarArchiveResponse implements ReadTreeResponse { // File path relative to the root extracted directory. Will remove the // top level dir name from the path since its name is hard to predetermine. - const relativePath = stripFirstDirectoryFromPath(path); + const relativePath = this.stripFirstDirectory + ? stripFirstDirectoryFromPath(path) + : path; if (this.subPath && !relativePath.startsWith(this.subPath)) { return false; } diff --git a/packages/backend-common/src/reading/types.ts b/packages/backend-common/src/reading/types.ts index 7f4d407fd0..90a3c6287e 100644 --- a/packages/backend-common/src/reading/types.ts +++ b/packages/backend-common/src/reading/types.ts @@ -116,7 +116,13 @@ export type FromReadableArrayOptions = Array<{ */ export interface ReadTreeResponseFactory { fromTarArchive( - options: ReadTreeResponseFactoryOptions, + options: ReadTreeResponseFactoryOptions & { + /** + * Strip the first parent directory of a tar archive. + * Defaults to true. + */ + stripFirstDirectory?: boolean; + }, ): Promise; fromZipArchive( options: ReadTreeResponseFactoryOptions, diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 1ff580965f..af86313d11 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,54 @@ # @backstage/backend-defaults +## 0.2.0 + +### Minor Changes + +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-app-api@0.5.0 + - @backstage/backend-plugin-api@0.6.0 + +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.0-next.1 + +### Minor Changes + +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-app-api@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + ## 0.1.13-next.0 ### Patch Changes diff --git a/packages/backend-defaults/api-report.md b/packages/backend-defaults/api-report.md index 6cc58de663..ab9cc5775c 100644 --- a/packages/backend-defaults/api-report.md +++ b/packages/backend-defaults/api-report.md @@ -4,14 +4,7 @@ ```ts import { Backend } from '@backstage/backend-app-api'; -import { ServiceFactoryOrFunction } from '@backstage/backend-plugin-api'; // @public (undocumented) -export function createBackend(options?: CreateBackendOptions): Backend; - -// @public (undocumented) -export interface CreateBackendOptions { - // (undocumented) - services?: ServiceFactoryOrFunction[]; -} +export function createBackend(): Backend; ``` diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 318b00c7f2..48464ac81e 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.1.13-next.0", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-defaults/src/CreateBackend.test.ts b/packages/backend-defaults/src/CreateBackend.test.ts index de04d4e350..0b36b57069 100644 --- a/packages/backend-defaults/src/CreateBackend.test.ts +++ b/packages/backend-defaults/src/CreateBackend.test.ts @@ -22,60 +22,63 @@ import { createBackend } from './CreateBackend'; describe('createBackend', () => { it('should not throw when overriding a default service implementation', () => { - expect(() => - createBackend({ - services: [ - createServiceFactory({ - service: coreServices.rootLifecycle, - deps: {}, - factory: async () => ({ - addStartupHook: () => {}, - addShutdownHook: () => {}, - }), + const backend = createBackend(); + + expect(() => { + backend.add( + createServiceFactory({ + service: coreServices.rootLifecycle, + deps: {}, + factory: async () => ({ + addStartupHook: () => {}, + addShutdownHook: () => {}, }), - ], - }), - ).not.toThrow(); + }), + ); + }).not.toThrow(); }); it('should throw on duplicate service implementations', () => { - expect(() => - createBackend({ - services: [ - createServiceFactory({ - service: coreServices.rootLifecycle, - deps: {}, - factory: async () => ({ - addStartupHook: () => {}, - addShutdownHook: () => {}, - }), - }), - createServiceFactory({ - service: coreServices.rootLifecycle, - deps: {}, - factory: async () => ({ - addStartupHook: () => {}, - addShutdownHook: () => {}, - }), - }), - ], + const backend = createBackend(); + + backend.add( + createServiceFactory({ + service: coreServices.rootLifecycle, + deps: {}, + factory: async () => ({ + addStartupHook: () => {}, + addShutdownHook: () => {}, + }), }), - ).toThrow( + ); + + expect(() => { + backend.add( + createServiceFactory({ + service: coreServices.rootLifecycle, + deps: {}, + factory: async () => ({ + addStartupHook: () => {}, + addShutdownHook: () => {}, + }), + }), + ); + }).toThrow( 'Duplicate service implementations provided for core.rootLifecycle', ); }); it('should throw when providing a plugin metadata service implementation', () => { + const backend = createBackend(); + expect(() => - createBackend({ - services: [ - createServiceFactory({ - service: coreServices.pluginMetadata, - deps: {}, - factory: async () => ({ getId: () => 'test' }), - }), - ], - }), + backend.add( + createServiceFactory({ + service: coreServices.pluginMetadata, + deps: {}, + factory: async () => ({ getId: () => 'test' }), + }), + ), ).toThrow('The core.pluginMetadata service cannot be overridden'); }); }); diff --git a/packages/backend-defaults/src/CreateBackend.ts b/packages/backend-defaults/src/CreateBackend.ts index 7051b58ae5..c07d1cc38a 100644 --- a/packages/backend-defaults/src/CreateBackend.ts +++ b/packages/backend-defaults/src/CreateBackend.ts @@ -17,7 +17,7 @@ import { Backend, cacheServiceFactory, - configServiceFactory, + rootConfigServiceFactory, createSpecializedBackend, databaseServiceFactory, discoveryServiceFactory, @@ -33,14 +33,10 @@ import { urlReaderServiceFactory, identityServiceFactory, } from '@backstage/backend-app-api'; -import { - ServiceFactory, - ServiceFactoryOrFunction, -} from '@backstage/backend-plugin-api'; export const defaultServiceFactories = [ cacheServiceFactory(), - configServiceFactory(), + rootConfigServiceFactory(), databaseServiceFactory(), discoveryServiceFactory(), httpRouterServiceFactory(), @@ -59,27 +55,6 @@ export const defaultServiceFactories = [ /** * @public */ -export interface CreateBackendOptions { - services?: ServiceFactoryOrFunction[]; -} - -/** - * @public - */ -export function createBackend(options?: CreateBackendOptions): Backend { - const services = new Array(); - - // Highest priority: Services passed directly to createBackend - const providedServices = (options?.services ?? []).map(sf => - typeof sf === 'function' ? sf() : sf, - ); - services.push(...providedServices); - - // Lowest priority: Default services that are not already provided by environment or directly to createBackend - const defaultServices = defaultServiceFactories.filter( - sf => !services.some(({ service }) => service.id === sf.service.id), - ); - services.push(...defaultServices); - - return createSpecializedBackend({ services }); +export function createBackend(): Backend { + return createSpecializedBackend({ defaultServiceFactories }); } diff --git a/packages/backend-defaults/src/index.ts b/packages/backend-defaults/src/index.ts index 833878744e..d3e36cf0fb 100644 --- a/packages/backend-defaults/src/index.ts +++ b/packages/backend-defaults/src/index.ts @@ -25,5 +25,4 @@ // TODO(Rugvip): Remove this once backend-common is no longer used by backend-app-api import '@backstage/backend-common'; -export type { CreateBackendOptions } from './CreateBackend'; export { createBackend } from './CreateBackend'; diff --git a/packages/backend-next/.snyk b/packages/backend-next/.snyk new file mode 100644 index 0000000000..ea5abd653d --- /dev/null +++ b/packages/backend-next/.snyk @@ -0,0 +1,10 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-ISOLATEDVM-3037320: + - '*': + reason: We do not pass any V8 cache data, and are therefore unaffected by this vulnerability + expires: 2033-07-02T16:55:57.077Z + created: 2023-07-02T16:55:57.077Z +patch: {} diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index 9a9be3b3e2..2c0fefb9f3 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,101 @@ # example-backend-next +## 0.0.14 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4 + - @backstage/plugin-search-backend-module-catalog@0.1.4 + - @backstage/plugin-search-backend-module-explore@0.1.4 + - @backstage/plugin-azure-devops-backend@0.3.27 + - @backstage/plugin-kubernetes-backend@0.11.3 + - @backstage/plugin-lighthouse-backend@0.2.4 + - @backstage/plugin-permission-backend@0.5.23 + - @backstage/plugin-scaffolder-backend@1.16.0 + - @backstage/backend-defaults@0.2.0 + - @backstage/plugin-devtools-backend@0.1.3 + - @backstage/plugin-techdocs-backend@1.6.5 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/plugin-badges-backend@0.2.3 + - @backstage/plugin-search-backend@1.4.0 + - @backstage/plugin-proxy-backend@0.3.0 + - @backstage/plugin-todo-backend@0.2.0 + - @backstage/plugin-app-backend@0.3.48 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0 + - @backstage/plugin-entity-feedback-backend@0.1.6 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-linguist-backend@0.4.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-adr-backend@0.3.6 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/plugin-permission-common@0.7.7 + +## 0.0.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.2 + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.2 + - @backstage/plugin-search-backend-module-explore@0.1.4-next.2 + - @backstage/plugin-scaffolder-backend@1.15.2-next.2 + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-proxy-backend@0.3.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-app-backend@0.3.48-next.2 + - @backstage/plugin-linguist-backend@0.4.0-next.2 + - @backstage/plugin-techdocs-backend@1.6.5-next.2 + - @backstage/backend-defaults@0.2.0-next.2 + - @backstage/plugin-adr-backend@0.3.6-next.2 + - @backstage/plugin-azure-devops-backend@0.3.27-next.2 + - @backstage/plugin-badges-backend@0.2.3-next.2 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.2 + - @backstage/plugin-devtools-backend@0.1.3-next.2 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.2 + - @backstage/plugin-kubernetes-backend@0.11.3-next.2 + - @backstage/plugin-lighthouse-backend@0.2.4-next.2 + - @backstage/plugin-permission-backend@0.5.23-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-search-backend@1.4.0-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-todo-backend@0.2.0-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.0.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.4-next.1 + - @backstage/plugin-azure-devops-backend@0.3.27-next.1 + - @backstage/plugin-kubernetes-backend@0.11.3-next.1 + - @backstage/plugin-lighthouse-backend@0.2.4-next.1 + - @backstage/plugin-permission-backend@0.5.23-next.1 + - @backstage/plugin-scaffolder-backend@1.15.2-next.1 + - @backstage/backend-defaults@0.2.0-next.1 + - @backstage/plugin-devtools-backend@0.1.3-next.1 + - @backstage/plugin-techdocs-backend@1.6.5-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-badges-backend@0.2.3-next.1 + - @backstage/plugin-search-backend@1.4.0-next.1 + - @backstage/plugin-todo-backend@0.2.0-next.1 + - @backstage/plugin-app-backend@0.3.48-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.1 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/plugin-linguist-backend@0.3.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-adr-backend@0.3.6-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/plugin-permission-common@0.7.7 + ## 0.0.14-next.0 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 3043a96ebd..c2b923ed26 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.14-next.0", + "version": "0.0.14", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -26,6 +26,7 @@ }, "dependencies": { "@backstage/backend-defaults": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/plugin-adr-backend": "workspace:^", "@backstage/plugin-app-backend": "workspace:^", @@ -42,6 +43,7 @@ "@backstage/plugin-permission-backend": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-permission-node": "workspace:^", + "@backstage/plugin-proxy-backend": "workspace:^", "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-search-backend": "workspace:^", "@backstage/plugin-search-backend-module-catalog": "workspace:^", diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index fa7561e211..f3b1ea06f8 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -36,13 +36,13 @@ import { badgesPlugin } from '@backstage/plugin-badges-backend'; import { azureDevOpsPlugin } from '@backstage/plugin-azure-devops-backend'; import { linguistPlugin } from '@backstage/plugin-linguist-backend'; import { devtoolsPlugin } from '@backstage/plugin-devtools-backend'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; import { adrPlugin } from '@backstage/plugin-adr-backend'; import { lighthousePlugin } from '@backstage/plugin-lighthouse-backend'; +import { proxyPlugin } from '@backstage/plugin-proxy-backend'; const backend = createBackend(); -backend.add(appPlugin({ appPackageName: 'example-app' })); +backend.add(appPlugin()); // Badges backend.add(badgesPlugin()); @@ -57,20 +57,7 @@ backend.add(devtoolsPlugin()); backend.add(entityFeedbackPlugin()); // Linguist -const linguistSchedule: TaskScheduleDefinition = { - frequency: { minutes: 2 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 15 }, -}; - -backend.add( - linguistPlugin({ - schedule: linguistSchedule, - age: { days: 30 }, - batchSize: 2, - useSourceLocation: false, - }), -); +backend.add(linguistPlugin()); // Todo backend.add(todoPlugin()); @@ -98,6 +85,9 @@ backend.add(kubernetesPlugin()); // Lighthouse backend.add(lighthousePlugin()); +// Proxy +backend.add(proxyPlugin()); + // Permissions backend.add(permissionPlugin()); backend.add(permissionModuleAllowAllPolicy()); diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index bd1d7f380a..fc3e52fa92 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/backend-openapi-utils +## 0.0.3 + +### Patch Changes + +- ebeb77586975: Add a new `createRouter` method for generating an `express` router that validates against your spec. Also fixes a bug with the query parameters type resolution. +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/errors@1.2.1 + +## 0.0.3-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/errors@1.2.1 + ## 0.0.3-next.0 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 9c83356c83..102d8c36d6 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.0.3-next.0", + "version": "0.0.3", "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 a77cdb71eb..9ea8b95c03 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,63 @@ # @backstage/backend-plugin-api +## 0.6.0 + +### Minor Changes + +- c49785f00cab: **BREAKING**: It is no longer possible to declare options as being required with `createServiceFactory`. +- 629cbd194a87: **BREAKING**: Renamed `coreServices.config` to `coreServices.rootConfig`. +- 51987dbdaf87: **BREAKING**: Removed the ability to define options for plugins and modules. Existing options should be migrated to instead use either static configuration or extension points. +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. + +### Patch Changes + +- c7aa4ff1793c: Allow modules to register extension points. +- cc9256a33bcc: Added new experimental `featureDiscoveryServiceRef`, available as an `/alpha` export. +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +- Updated dependencies + - @backstage/plugin-auth-node@0.2.17 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + +## 0.6.0-next.2 + +### Patch Changes + +- cc9256a33bcc: Added new experimental `featureDiscoveryServiceRef`, available as an `/alpha` export. +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.6.0-next.1 + +### Minor Changes + +- 629cbd194a87: **BREAKING**: Renamed `coreServices.config` to `coreServices.rootConfig`. +- d008aefef808: **BREAKING**: Removing shared environments concept from the new experimental backend system. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + ## 0.5.5-next.0 ### Patch Changes diff --git a/packages/backend-plugin-api/alpha-api-report.md b/packages/backend-plugin-api/alpha-api-report.md new file mode 100644 index 0000000000..81b378a671 --- /dev/null +++ b/packages/backend-plugin-api/alpha-api-report.md @@ -0,0 +1,24 @@ +## API Report File for "@backstage/backend-plugin-api" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { ServiceRef } from '@backstage/backend-plugin-api'; + +// @alpha (undocumented) +export interface FeatureDiscoveryService { + // (undocumented) + getBackendFeatures(): Promise<{ + features: Array; + }>; +} + +// @alpha +export const featureDiscoveryServiceRef: ServiceRef< + FeatureDiscoveryService, + 'root' +>; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 093b4acec4..17cc521499 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -31,6 +31,11 @@ export interface BackendModuleConfig { // @public export interface BackendModuleRegistrationPoints { + // (undocumented) + registerExtensionPoint( + ref: ExtensionPoint, + impl: TExtensionPoint, + ): void; // (undocumented) registerInit< Deps extends { @@ -93,13 +98,10 @@ export type CacheServiceSetOptions = { ttl?: number; }; -// @public (undocumented) -export interface ConfigService extends Config {} - // @public export namespace coreServices { const cache: ServiceRef; - const config: ServiceRef; + const rootConfig: ServiceRef; const database: ServiceRef; const discovery: ServiceRef; const httpRouter: ServiceRef; @@ -117,14 +119,14 @@ export namespace coreServices { } // @public -export function createBackendModule( - config: BackendModuleConfig | ((...params: TOptions) => BackendModuleConfig), -): (...params: TOptions) => BackendFeature; +export function createBackendModule( + config: BackendModuleConfig, +): () => BackendFeature; // @public -export function createBackendPlugin( - config: BackendPluginConfig | ((...params: TOptions) => BackendPluginConfig), -): (...params: TOptions) => BackendFeature; +export function createBackendPlugin( + config: BackendPluginConfig, +): () => BackendFeature; // @public export function createExtensionPoint( @@ -155,18 +157,6 @@ export function createServiceFactory< config: (options?: TOpts) => RootServiceFactoryConfig, ): (options?: TOpts) => ServiceFactory; -// @public -export function createServiceFactory< - TService, - TImpl extends TService, - TDeps extends { - [name in string]: ServiceRef; - }, - TOpts extends object | undefined = undefined, ->( - config: (options: TOpts) => RootServiceFactoryConfig, -): (options: TOpts) => ServiceFactory; - // @public export function createServiceFactory< TService, @@ -195,23 +185,6 @@ export function createServiceFactory< ) => PluginServiceFactoryConfig, ): (options?: TOpts) => ServiceFactory; -// @public -export function createServiceFactory< - TService, - TImpl extends TService, - TDeps extends { - [name in string]: ServiceRef; - }, - TContext = undefined, - TOpts extends object | undefined = undefined, ->( - config: - | PluginServiceFactoryConfig - | (( - options: TOpts, - ) => PluginServiceFactoryConfig), -): (options: TOpts) => ServiceFactory; - // @public export function createServiceRef( config: ServiceRefConfig, @@ -380,6 +353,9 @@ export type ReadUrlResponse = { lastModifiedAt?: Date; }; +// @public (undocumented) +export interface RootConfigService extends Config {} + // @public (undocumented) export interface RootHttpRouterService { use(path: string, handler: Handler): void; @@ -433,9 +409,7 @@ export type SearchResponseFile = { export interface ServiceFactory< TService = unknown, TScope extends 'plugin' | 'root' = 'plugin' | 'root', -> { - // (undocumented) - $$type: '@backstage/ServiceFactory'; +> extends BackendFeature { // (undocumented) service: ServiceRef; } diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 91936a4c16..44a9d5c69c 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,17 +1,30 @@ { "name": "@backstage/backend-plugin-api", "description": "Core API used by Backstage backend plugins", - "version": "0.5.5-next.0", + "version": "0.6.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" }, "backstage": { "role": "node-library" }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "homepage": "https://backstage.io", "repository": { "type": "git", diff --git a/packages/backend-plugin-api/src/alpha.ts b/packages/backend-plugin-api/src/alpha.ts new file mode 100644 index 0000000000..baee739f49 --- /dev/null +++ b/packages/backend-plugin-api/src/alpha.ts @@ -0,0 +1,35 @@ +/* + * 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 { + BackendFeature, + createServiceRef, +} from '@backstage/backend-plugin-api'; + +/** @alpha */ +export interface FeatureDiscoveryService { + getBackendFeatures(): Promise<{ features: Array }>; +} + +/** + * An optional service that can be used to dynamically load in additional BackendFeatures at runtime. + * @alpha + */ +export const featureDiscoveryServiceRef = + createServiceRef({ + id: 'core.featureDiscovery', + scope: 'root', + }); diff --git a/packages/backend-plugin-api/src/index.ts b/packages/backend-plugin-api/src/index.ts index 452fa046f9..8e0b23d20b 100644 --- a/packages/backend-plugin-api/src/index.ts +++ b/packages/backend-plugin-api/src/index.ts @@ -21,4 +21,5 @@ */ export * from './services'; +export type { BackendFeature } from './types'; export * from './wiring'; diff --git a/packages/backend-plugin-api/src/services/definitions/ConfigService.ts b/packages/backend-plugin-api/src/services/definitions/RootConfigService.ts similarity index 92% rename from packages/backend-plugin-api/src/services/definitions/ConfigService.ts rename to packages/backend-plugin-api/src/services/definitions/RootConfigService.ts index db9aa88dda..02343e4ee8 100644 --- a/packages/backend-plugin-api/src/services/definitions/ConfigService.ts +++ b/packages/backend-plugin-api/src/services/definitions/RootConfigService.ts @@ -19,4 +19,4 @@ import { Config } from '@backstage/config'; /** * @public */ -export interface ConfigService extends Config {} +export interface RootConfigService extends Config {} diff --git a/packages/backend-plugin-api/src/services/definitions/coreServices.ts b/packages/backend-plugin-api/src/services/definitions/coreServices.ts index 7dad94cebf..b42795d052 100644 --- a/packages/backend-plugin-api/src/services/definitions/coreServices.ts +++ b/packages/backend-plugin-api/src/services/definitions/coreServices.ts @@ -32,13 +32,13 @@ export namespace coreServices { }); /** - * The service reference for the root scoped {@link ConfigService}. + * The service reference for the root scoped {@link RootConfigService}. * * @public */ - export const config = createServiceRef< - import('./ConfigService').ConfigService - >({ id: 'core.config', scope: 'root' }); + export const rootConfig = createServiceRef< + import('./RootConfigService').RootConfigService + >({ id: 'core.rootConfig', scope: 'root' }); /** * The service reference for the plugin scoped {@link DatabaseService}. diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index ab0f0b84b1..98be8211db 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -20,7 +20,7 @@ export type { CacheServiceOptions, CacheServiceSetOptions, } from './CacheService'; -export type { ConfigService } from './ConfigService'; +export type { RootConfigService } from './RootConfigService'; export type { DatabaseService } from './DatabaseService'; export type { DiscoveryService } from './DiscoveryService'; export type { HttpRouterService } from './HttpRouterService'; 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 d3e9b9dd4e..4d0432505e 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -26,7 +26,7 @@ interface TestOptions { function unused(..._any: any[]) {} describe('createServiceFactory', () => { - it('should create a sync factory with no options', () => { + it('should create a sync factory', () => { const metaFactory = createServiceFactory({ service: ref, deps: {}, @@ -51,7 +51,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a sync root factory with no options', () => { + it('should create a sync root factory', () => { const metaFactory = createServiceFactory({ service: rootDep, deps: {}, @@ -75,7 +75,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a factory with no options', () => { + it('should create a factory', () => { const metaFactory = createServiceFactory({ service: ref, deps: {}, @@ -100,7 +100,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a factory with optional options', () => { + it('should create a factory with options', () => { const metaFactory = createServiceFactory((_opts?: { x: number }) => ({ service: ref, deps: {}, @@ -124,7 +124,8 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a factory with required options', () => { + it('should not be allowed to require options', () => { + // @ts-expect-error const metaFactory = createServiceFactory((_opts: { x: number }) => ({ service: ref, deps: {}, @@ -134,23 +135,9 @@ describe('createServiceFactory', () => { }, })); 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); - // @ts-expect-error - metaFactory(undefined); - // @ts-expect-error - metaFactory(); }); - it('should create a factory with optional options as interface', () => { + it('should create a factory with options as interface', () => { const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: {}, @@ -174,32 +161,6 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create a factory with required 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); - // @ts-expect-error - metaFactory(undefined); - // @ts-expect-error - metaFactory(); - }); - it('should create root scoped factory with dependencies', () => { const metaFactory = createServiceFactory({ service: createServiceRef({ id: 'foo', scope: 'root' }), @@ -226,7 +187,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create root scoped factory with dependencies and optional options', () => { + it('should create root scoped factory with dependencies and options', () => { const metaFactory = createServiceFactory((_options?: TestOptions) => ({ service: createServiceRef({ id: 'foo', scope: 'root' }), deps: { @@ -325,49 +286,7 @@ describe('createServiceFactory', () => { metaFactory(); }); - it('should create factory with required 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); - // @ts-expect-error - metaFactory(undefined); - // @ts-expect-error - metaFactory(); - }); - - it('should create factory with optional options and dependencies', () => { + it('should create factory with options and dependencies', () => { const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: { diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 190ef7c13e..bea53dc1ed 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { BackendFeature } from '../../types'; + /** * TODO * @@ -51,9 +53,7 @@ export type ServiceRef< export interface ServiceFactory< TService = unknown, TScope extends 'plugin' | 'root' = 'plugin' | 'root', -> { - $$type: '@backstage/ServiceFactory'; - +> extends BackendFeature { service: ServiceRef; } @@ -191,20 +191,6 @@ export function createServiceFactory< >( config: (options?: TOpts) => RootServiceFactoryConfig, ): (options?: TOpts) => ServiceFactory; -/** - * Creates a root scoped service factory with required options. - * - * @public - * @param config - The service factory configuration. - */ -export function createServiceFactory< - TService, - TImpl extends TService, - TDeps extends { [name in string]: ServiceRef }, - TOpts extends object | undefined = undefined, ->( - config: (options: TOpts) => RootServiceFactoryConfig, -): (options: TOpts) => ServiceFactory; /** * Creates a plugin scoped service factory without options. * @@ -237,25 +223,6 @@ export function createServiceFactory< options?: TOpts, ) => PluginServiceFactoryConfig, ): (options?: TOpts) => ServiceFactory; -/** - * Creates a plugin scoped service factory with required options. - * - * @public - * @param config - The service factory configuration. - */ -export function createServiceFactory< - TService, - TImpl extends TService, - TDeps extends { [name in string]: ServiceRef }, - TContext = undefined, - TOpts extends object | undefined = undefined, ->( - config: - | PluginServiceFactoryConfig - | (( - options: TOpts, - ) => PluginServiceFactoryConfig), -): (options: TOpts) => ServiceFactory; export function createServiceFactory< TService, TImpl extends TService, @@ -281,7 +248,7 @@ export function createServiceFactory< if (anyConf.service.scope === 'root') { const c = anyConf as RootServiceFactoryConfig; return { - $$type: '@backstage/ServiceFactory', + $$type: '@backstage/BackendFeature', version: 'v1', service: c.service, deps: c.deps, @@ -295,7 +262,7 @@ export function createServiceFactory< TDeps >; return { - $$type: '@backstage/ServiceFactory', + $$type: '@backstage/BackendFeature', version: 'v1', service: c.service, ...('createRootContext' in c diff --git a/packages/backend-plugin-api/src/types.ts b/packages/backend-plugin-api/src/types.ts new file mode 100644 index 0000000000..59f1106fab --- /dev/null +++ b/packages/backend-plugin-api/src/types.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** @internal */ +export interface BackendFeatureFactory< + TOptions extends [options?: object] = [], +> { + (...options: TOptions): BackendFeature; + $$type: '@backstage/BackendFeatureFactory'; +} + +/** @public */ +export interface BackendFeature { + // NOTE: This type is opaque in order to simplify future API evolution. + $$type: '@backstage/BackendFeature'; +} diff --git a/packages/backend-plugin-api/src/wiring/factories.test.ts b/packages/backend-plugin-api/src/wiring/factories.test.ts index ae199d84e4..1fd05a1605 100644 --- a/packages/backend-plugin-api/src/wiring/factories.test.ts +++ b/packages/backend-plugin-api/src/wiring/factories.test.ts @@ -33,22 +33,19 @@ describe('createExtensionPoint', () => { describe('createBackendPlugin', () => { it('should create a BackendPlugin', () => { - const plugin = createBackendPlugin((_options: { a: string }) => ({ + const plugin = createBackendPlugin({ pluginId: 'x', register(r) { r.registerInit({ deps: {}, async init() {} }); }, - })); + }); expect(plugin).toBeDefined(); - expect(plugin({ a: 'a' })).toBeDefined(); - expect(plugin({ a: 'a' })).toEqual({ + expect(plugin()).toEqual({ $$type: '@backstage/BackendFeature', version: 'v1', getRegistrations: expect.any(Function), }); - expect( - (plugin({ a: 'a' }) as InternalBackendFeature).getRegistrations(), - ).toEqual([ + expect((plugin() as InternalBackendFeature).getRegistrations()).toEqual([ { type: 'plugin', pluginId: 'x', @@ -60,95 +57,32 @@ describe('createBackendPlugin', () => { }, ]); // @ts-expect-error - expect(plugin()).toBeDefined(); - // @ts-expect-error - expect(plugin({ b: 'b' })).toBeDefined(); - }); - - it('should create plugins with optional options', () => { - const plugin = createBackendPlugin((_options?: { a: string }) => ({ - pluginId: 'x', - register() {}, - })); - expect(plugin).toBeDefined(); expect(plugin({ a: 'a' })).toBeDefined(); - expect(plugin()).toBeDefined(); - // @ts-expect-error - expect(plugin({ b: 'b' })).toBeDefined(); - }); - - it('should create plugins without options', () => { - const plugin = createBackendPlugin({ - pluginId: 'x', - register() {}, - }); - expect(plugin).toBeDefined(); - // @ts-expect-error - expect(plugin({ a: 'a' })).toBeDefined(); - // @ts-expect-error - expect(plugin({})).toBeDefined(); - }); - - it('should create a BackendPlugin with options as interface', () => { - interface TestOptions { - a: string; - } - const plugin = createBackendPlugin((_options: TestOptions) => ({ - pluginId: 'x', - register() {}, - })); - expect(plugin).toBeDefined(); - expect(plugin({ a: 'a' })).toBeDefined(); - expect(plugin({ a: 'a' })).toEqual({ - $$type: '@backstage/BackendFeature', - version: 'v1', - getRegistrations: expect.any(Function), - }); - // @ts-expect-error - expect(plugin()).toBeDefined(); - // @ts-expect-error - expect(plugin({ b: 'b' })).toBeDefined(); - }); - - it('should create plugins with optional options as interface', () => { - interface TestOptions { - a: string; - } - const plugin = createBackendPlugin((_options?: TestOptions) => ({ - pluginId: 'x', - register() {}, - })); - expect(plugin).toBeDefined(); - expect(plugin({ a: 'a' })).toBeDefined(); - expect(plugin()).toBeDefined(); - // @ts-expect-error - expect(plugin({ b: 'b' })).toBeDefined(); }); }); describe('createBackendModule', () => { it('should create a BackendModule', () => { - const mod = createBackendModule((_options: { a: string }) => ({ + const mod = createBackendModule({ pluginId: 'x', moduleId: 'y', register(r) { r.registerInit({ deps: {}, async init() {} }); }, - })); + }); expect(mod).toBeDefined(); - expect(mod({ a: 'a' })).toBeDefined(); - expect(mod({ a: 'a' })).toEqual({ + expect(mod()).toBeDefined(); + expect(mod()).toEqual({ $$type: '@backstage/BackendFeature', version: 'v1', getRegistrations: expect.any(Function), }); - expect( - (mod({ a: 'a' }) as InternalBackendFeature).getRegistrations(), - ).toEqual([ + expect((mod() as InternalBackendFeature).getRegistrations()).toEqual([ { type: 'module', pluginId: 'x', moduleId: 'y', + extensionPoints: [], init: { deps: expect.any(Object), func: expect.any(Function), @@ -156,72 +90,6 @@ describe('createBackendModule', () => { }, ]); // @ts-expect-error - expect(mod()).toBeDefined(); - // @ts-expect-error - expect(mod({ b: 'b' })).toBeDefined(); - }); - - it('should create modules with optional options', () => { - const mod = createBackendModule((_options?: { a: string }) => ({ - pluginId: 'x', - moduleId: 'y', - register() {}, - })); - expect(mod).toBeDefined(); expect(mod({ a: 'a' })).toBeDefined(); - expect(mod()).toBeDefined(); - // @ts-expect-error - expect(mod({ b: 'b' })).toBeDefined(); - }); - - it('should create modules without options', () => { - const mod = createBackendModule({ - pluginId: 'x', - moduleId: 'y', - register() {}, - }); - expect(mod).toBeDefined(); - // @ts-expect-error - expect(mod({ a: 'a' })).toBeDefined(); - // @ts-expect-error - expect(mod({})).toBeDefined(); - }); - - it('should create a BackendModule as interface', () => { - interface TestOptions { - a: string; - } - const mod = createBackendModule((_options: TestOptions) => ({ - pluginId: 'x', - moduleId: 'y', - register() {}, - })); - expect(mod).toBeDefined(); - expect(mod({ a: 'a' })).toBeDefined(); - expect(mod({ a: 'a' })).toEqual({ - $$type: '@backstage/BackendFeature', - version: 'v1', - getRegistrations: expect.any(Function), - }); - // @ts-expect-error - expect(mod()).toBeDefined(); - // @ts-expect-error - expect(mod({ b: 'b' })).toBeDefined(); - }); - - it('should create modules with optional options as interface', () => { - interface TestOptions { - a: string; - } - const mod = createBackendModule((_options?: TestOptions) => ({ - pluginId: 'x', - moduleId: 'y', - register() {}, - })); - expect(mod).toBeDefined(); - expect(mod({ a: 'a' })).toBeDefined(); - expect(mod()).toBeDefined(); - // @ts-expect-error - expect(mod({ b: 'b' })).toBeDefined(); }); }); diff --git a/packages/backend-plugin-api/src/wiring/factories.ts b/packages/backend-plugin-api/src/wiring/factories.ts index 928c933af0..9fa0f87bab 100644 --- a/packages/backend-plugin-api/src/wiring/factories.ts +++ b/packages/backend-plugin-api/src/wiring/factories.ts @@ -14,12 +14,11 @@ * limitations under the License. */ +import { BackendFeature, BackendFeatureFactory } from '../types'; import { BackendModuleRegistrationPoints, BackendPluginRegistrationPoints, - BackendFeature, ExtensionPoint, - InternalBackendFeature, InternalBackendModuleRegistration, InternalBackendPluginRegistration, } from './types'; @@ -85,13 +84,10 @@ export interface BackendPluginConfig { * @see {@link https://backstage.io/docs/backend-system/architecture/plugins | The architecture of plugins} * @see {@link https://backstage.io/docs/backend-system/architecture/naming-patterns | Recommended naming patterns} */ -export function createBackendPlugin( - config: BackendPluginConfig | ((...params: TOptions) => BackendPluginConfig), -): (...params: TOptions) => BackendFeature { - const configCallback = typeof config === 'function' ? config : () => config; - return (...options: TOptions): InternalBackendFeature => { - const c = configCallback(...options); - +export function createBackendPlugin( + config: BackendPluginConfig, +): () => BackendFeature { + const factory: BackendFeatureFactory = () => { let registrations: InternalBackendPluginRegistration[]; return { @@ -106,7 +102,7 @@ export function createBackendPlugin( let init: InternalBackendPluginRegistration['init'] | undefined = undefined; - c.register({ + config.register({ registerExtensionPoint(ext, impl) { if (init) { throw new Error( @@ -128,14 +124,14 @@ export function createBackendPlugin( if (!init) { throw new Error( - `registerInit was not called by register in ${c.pluginId}`, + `registerInit was not called by register in ${config.pluginId}`, ); } registrations = [ { type: 'plugin', - pluginId: c.pluginId, + pluginId: config.pluginId, extensionPoints, init, }, @@ -144,6 +140,9 @@ export function createBackendPlugin( }, }; }; + factory.$$type = '@backstage/BackendFeatureFactory'; + + return factory; } /** @@ -155,13 +154,14 @@ export function createBackendPlugin( */ export interface BackendModuleConfig { /** - * The ID of this plugin. + * Should exactly match the `id` of the plugin that the module extends. * * @see {@link https://backstage.io/docs/backend-system/architecture/naming-patterns | Recommended naming patterns} */ pluginId: string; + /** - * Should exactly match the `id` of the plugin that the module extends. + * The ID of this module, used to identify the module and ensure that it is not installed twice. */ moduleId: string; register(reg: BackendModuleRegistrationPoints): void; @@ -174,13 +174,10 @@ export interface BackendModuleConfig { * @see {@link https://backstage.io/docs/backend-system/architecture/modules | The architecture of modules} * @see {@link https://backstage.io/docs/backend-system/architecture/naming-patterns | Recommended naming patterns} */ -export function createBackendModule( - config: BackendModuleConfig | ((...params: TOptions) => BackendModuleConfig), -): (...params: TOptions) => BackendFeature { - const configCallback = typeof config === 'function' ? config : () => config; - return (...options: TOptions): InternalBackendFeature => { - const c = configCallback(...options); - +export function createBackendModule( + config: BackendModuleConfig, +): () => BackendFeature { + const factory: BackendFeatureFactory = () => { let registrations: InternalBackendModuleRegistration[]; return { @@ -190,10 +187,20 @@ export function createBackendModule( if (registrations) { return registrations; } + const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = + []; let init: InternalBackendModuleRegistration['init'] | undefined = undefined; - c.register({ + config.register({ + registerExtensionPoint(ext, impl) { + if (init) { + throw new Error( + 'registerExtensionPoint called after registerInit', + ); + } + extensionPoints.push([ext, impl]); + }, registerInit(regInit) { if (init) { throw new Error('registerInit must only be called once'); @@ -207,15 +214,16 @@ export function createBackendModule( if (!init) { throw new Error( - `registerInit was not called by register in ${c.moduleId} module for ${c.pluginId}`, + `registerInit was not called by register in ${config.moduleId} module for ${config.pluginId}`, ); } registrations = [ { type: 'module', - pluginId: c.pluginId, - moduleId: c.moduleId, + pluginId: config.pluginId, + moduleId: config.moduleId, + extensionPoints, init, }, ]; @@ -223,4 +231,7 @@ export function createBackendModule( }, }; }; + factory.$$type = '@backstage/BackendFeatureFactory'; + + return factory; } diff --git a/packages/backend-plugin-api/src/wiring/index.ts b/packages/backend-plugin-api/src/wiring/index.ts index 9cb767b8ac..49f15c0b55 100644 --- a/packages/backend-plugin-api/src/wiring/index.ts +++ b/packages/backend-plugin-api/src/wiring/index.ts @@ -27,6 +27,5 @@ export { export type { BackendModuleRegistrationPoints, BackendPluginRegistrationPoints, - BackendFeature, ExtensionPoint, } from './types'; diff --git a/packages/backend-plugin-api/src/wiring/types.ts b/packages/backend-plugin-api/src/wiring/types.ts index c81ee3ceb7..55d43ccb7c 100644 --- a/packages/backend-plugin-api/src/wiring/types.ts +++ b/packages/backend-plugin-api/src/wiring/types.ts @@ -15,6 +15,7 @@ */ import { ServiceRef } from '../services/system/types'; +import { BackendFeature } from '../types'; /** * TODO @@ -59,6 +60,10 @@ export interface BackendPluginRegistrationPoints { * @public */ export interface BackendModuleRegistrationPoints { + registerExtensionPoint( + ref: ExtensionPoint, + impl: TExtensionPoint, + ): void; registerInit(options: { deps: { [name in keyof Deps]: ServiceRef | ExtensionPoint; @@ -67,12 +72,6 @@ export interface BackendModuleRegistrationPoints { }): void; } -/** @public */ -export interface BackendFeature { - // NOTE: This type is opaque in order to simplify future API evolution. - $$type: '@backstage/BackendFeature'; -} - /** @internal */ export interface InternalBackendFeature extends BackendFeature { version: 'v1'; @@ -97,6 +96,7 @@ export interface InternalBackendModuleRegistration { pluginId: string; moduleId: string; type: 'module'; + extensionPoints: Array, unknown]>; init: { deps: Record | ExtensionPoint>; func(deps: Record): Promise; diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index ec1dd319ea..11a039e053 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/backend-tasks +## 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 diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 4c0e4fd27e..ca9129078e 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.5.5-next.0", + "version": "0.5.5", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts b/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts index fc2a045614..21f51954b5 100644 --- a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts +++ b/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.test.ts @@ -76,7 +76,7 @@ describe('readTaskScheduleDefinitionFromConfig', () => { ); }); - it('invalid frequency value', () => { + it('invalid frequency key', () => { const config = new ConfigReader({ frequency: { invalid: 'value', @@ -89,6 +89,19 @@ describe('readTaskScheduleDefinitionFromConfig', () => { ); }); + it('invalid frequency value', () => { + const config = new ConfigReader({ + frequency: { + minutes: 'value', + }, + timeout: 'PT3M', + }); + + expect(() => readTaskScheduleDefinitionFromConfig(config)).toThrow( + "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: { diff --git a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts b/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts index 1448a328f8..b31cf68f74 100644 --- a/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts +++ b/packages/backend-tasks/src/tasks/readTaskScheduleDefinitionFromConfig.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { HumanDuration, JsonObject } from '@backstage/types'; +import { HumanDuration } from '@backstage/types'; import { TaskScheduleDefinition } from './types'; import { Duration } from 'luxon'; @@ -31,29 +31,48 @@ const propsOfHumanDuration = [ ]; function convertToHumanDuration(config: Config, key: string): HumanDuration { - const props = config.getConfig(key).keys(); - if (!props.find(prop => propsOfHumanDuration.includes(prop))) { + // Ensures that the root is an object + const root = config.getConfig(key); + + const result: Record = {}; + let found = false; + for (const prop of propsOfHumanDuration) { + const value = root.getOptionalNumber(prop); + if (value !== undefined) { + result[prop] = value; + found = true; + } + } + + if (!found) { throw new Error( `HumanDuration needs at least one of: ${propsOfHumanDuration}`, ); } - const invalidProps = props.filter( - prop => !propsOfHumanDuration.includes(prop), - ); + const invalidProps = root + .keys() + .filter(prop => !propsOfHumanDuration.includes(prop)); if (invalidProps.length > 0) { throw new Error( `HumanDuration does not contain properties: ${invalidProps}`, ); } - return config.get(key) as HumanDuration; + return result as HumanDuration; } function readDuration(config: Config, key: string): Duration | HumanDuration { - return typeof config.get(key) === 'string' - ? Duration.fromISO(config.getString(key)) - : convertToHumanDuration(config, key); + 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; + } + + return convertToHumanDuration(config, key); } function readCronOrDuration( diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 2d40873bb9..d6eec9f7ed 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,61 @@ # @backstage/backend-test-utils +## 0.2.0 + +### Minor Changes + +- b9c57a4f857e: **BREAKING**: Renamed `mockServices.config` to `mockServices.rootConfig`. +- a6d7983f349c: **BREAKING**: Removed the `services` option from `createBackend`. Service factories are now `BackendFeature`s and should be installed with `backend.add(...)` instead. The following should be migrated: + + ```ts + const backend = createBackend({ services: [myCustomServiceFactory] }); + ``` + + To instead pass the service factory via `backend.add(...)`: + + ```ts + const backend = createBackend(); + backend.add(customRootLoggerServiceFactory); + ``` + +### Patch Changes + +- ae9304818136: Add needed constants and constructs to support PostgreSQL version 14 as test database +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-app-api@0.5.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.2.0-next.1 + +### Minor Changes + +- b9c57a4f857e: **BREAKING**: Renamed `mockServices.config` to `mockServices.rootConfig`. + +### Patch Changes + +- ae9304818136: Add needed constants and constructs to support PostgreSQL version 14 as test database +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-app-api@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + ## 0.1.40-next.0 ### Patch Changes diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index 358b51071f..d086b1d2ae 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -6,7 +6,6 @@ import { Backend } from '@backstage/backend-app-api'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { CacheService } from '@backstage/backend-plugin-api'; -import { ConfigService } from '@backstage/backend-plugin-api'; import { DatabaseService } from '@backstage/backend-plugin-api'; import { ExtendedHttpServer } from '@backstage/backend-app-api'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; @@ -18,10 +17,10 @@ import { Knex } from 'knex'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { PermissionsService } from '@backstage/backend-plugin-api'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { RootLifecycleService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { ServiceFactory } 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'; @@ -36,19 +35,6 @@ export namespace mockServices { factory: () => ServiceFactory; } // (undocumented) - export function config(options?: config.Options): ConfigService; - // (undocumented) - export namespace config { - // (undocumented) - export type Options = { - data?: JsonObject; - }; - const // (undocumented) - factory: ( - options?: Options | undefined, - ) => ServiceFactory; - } - // (undocumented) export namespace database { const // (undocumented) factory: () => ServiceFactory; @@ -83,6 +69,19 @@ export namespace mockServices { factory: () => ServiceFactory; } // (undocumented) + export function rootConfig(options?: rootConfig.Options): RootConfigService; + // (undocumented) + export namespace rootConfig { + // (undocumented) + export type Options = { + data?: JsonObject; + }; + const // (undocumented) + factory: ( + options?: Options | undefined, + ) => ServiceFactory; + } + // (undocumented) export namespace rootLifecycle { const // (undocumented) factory: () => ServiceFactory; @@ -127,11 +126,8 @@ export function setupRequestMockHandlers(worker: { }): void; // @public (undocumented) -export function startTestBackend< - TServices extends any[], - TExtensionPoints extends any[], ->( - options: TestBackendOptions, +export function startTestBackend( + options: TestBackendOptions, ): Promise; // @public (undocumented) @@ -140,10 +136,7 @@ export interface TestBackend extends Backend { } // @public (undocumented) -export interface TestBackendOptions< - TServices extends any[], - TExtensionPoints extends any[], -> { +export interface TestBackendOptions { // (undocumented) extensionPoints?: readonly [ ...{ @@ -154,20 +147,12 @@ export interface TestBackendOptions< }, ]; // (undocumented) - features?: BackendFeature[]; - // (undocumented) - services?: readonly [ - ...{ - [index in keyof TServices]: - | ServiceFactory - | (() => ServiceFactory) - | [ServiceRef, Partial]; - }, - ]; + features?: Array BackendFeature)>; } // @public export type TestDatabaseId = + | 'POSTGRES_14' | 'POSTGRES_13' | 'POSTGRES_12' | 'POSTGRES_11' diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index d9d904357b..02cb1d5cf9 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.40-next.0", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/src/database/TestDatabases.test.ts b/packages/backend-test-utils/src/database/TestDatabases.test.ts index be29a42806..d766d38815 100644 --- a/packages/backend-test-utils/src/database/TestDatabases.test.ts +++ b/packages/backend-test-utils/src/database/TestDatabases.test.ts @@ -59,6 +59,37 @@ describe('TestDatabases', () => { describe('each connect', () => { const dbs = TestDatabases.create(); + itIfDocker( + 'obeys a provided connection string for postgres 14', + async () => { + const { host, port, user, password, stop } = + await startPostgresContainer('postgres:14'); + + try { + // Leave a mark + process.env.BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING = `postgresql://${user}:${password}@${host}:${port}`; + const input = await dbs.init('POSTGRES_14'); + await input.schema.createTable('a', table => + table.string('x').primary(), + ); + await input.insert({ x: 'y' }).into('a'); + + // Look for the mark + const database = input.client.config.connection.database; + const output = knexFactory({ + client: 'pg', + connection: { host, port, user, password, database }, + }); + // eslint-disable-next-line jest/no-standalone-expect + await expect(output.select('x').from('a')).resolves.toEqual([ + { x: 'y' }, + ]); + } finally { + await stop(); + } + }, + ); + itIfDocker( 'obeys a provided connection string for postgres 13', async () => { diff --git a/packages/backend-test-utils/src/database/types.ts b/packages/backend-test-utils/src/database/types.ts index 8c1d6f5193..a7bfdeb12d 100644 --- a/packages/backend-test-utils/src/database/types.ts +++ b/packages/backend-test-utils/src/database/types.ts @@ -24,6 +24,7 @@ import { getDockerImageForName } from '../util/getDockerImageForName'; * @public */ export type TestDatabaseId = + | 'POSTGRES_14' | 'POSTGRES_13' | 'POSTGRES_12' | 'POSTGRES_11' @@ -46,6 +47,13 @@ export type Instance = { export const allDatabases: Record = Object.freeze({ + POSTGRES_14: { + name: 'Postgres 14.x', + driver: 'pg', + dockerImageName: getDockerImageForName('postgres:14'), + connectionStringEnvironmentVariableName: + 'BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING', + }, POSTGRES_13: { name: 'Postgres 13.x', driver: 'pg', diff --git a/packages/backend-test-utils/src/next/services/mockServices.ts b/packages/backend-test-utils/src/next/services/mockServices.ts index 8a3125214c..0455b9f1ae 100644 --- a/packages/backend-test-utils/src/next/services/mockServices.ts +++ b/packages/backend-test-utils/src/next/services/mockServices.ts @@ -15,7 +15,7 @@ */ import { - ConfigService, + RootConfigService, coreServices, createServiceFactory, IdentityService, @@ -61,13 +61,13 @@ function simpleFactory< * @public */ export namespace mockServices { - export function config(options?: config.Options): ConfigService { + export function rootConfig(options?: rootConfig.Options): RootConfigService { return new ConfigReader(options?.data, 'mock-config'); } - export namespace config { + export namespace rootConfig { export type Options = { data?: JsonObject }; - export const factory = simpleFactory(coreServices.config, config); + export const factory = simpleFactory(coreServices.rootConfig, rootConfig); } export function rootLogger(options?: rootLogger.Options): LoggerService { 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 aa93a7dae1..b127712927 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -31,7 +31,6 @@ import { startTestBackend } from './TestBackend'; let globalTestBackendHasBeenStopped = false; beforeAll(async () => { await startTestBackend({ - services: [], features: [ createBackendModule({ moduleId: 'test.module', @@ -46,7 +45,7 @@ beforeAll(async () => { }, }); }, - })(), + }), ], }); }); @@ -68,17 +67,37 @@ describe('TestBackend', () => { const extensionPoint5 = createExtensionPoint({ id: 'b5' }); await expect( startTestBackend({ - services: [ + features: [ // @ts-expect-error [extensionPoint1, { a: 'a' }], - [serviceRef, { a: 'a' }], - [serviceRef, { a: 'a', b: 'b' }], - // @ts-expect-error - [serviceRef, { c: 'c' }], - // @ts-expect-error - [serviceRef, { a: 'a', c: 'c' }], - // @ts-expect-error - [serviceRef, { a: 'a', b: 'b', c: 'c' }], + createServiceFactory(() => ({ + service: serviceRef, + deps: {}, + // @ts-expect-error + factory: async () => ({ a: 'a' }), + })), + createServiceFactory(() => ({ + service: serviceRef, + deps: {}, + factory: async () => ({ a: 'a', b: 'b' }), + })), + createServiceFactory(() => ({ + service: serviceRef, + deps: {}, + // @ts-expect-error + factory: async () => ({ c: 'c' }), + })), + createServiceFactory(() => ({ + service: serviceRef, + deps: {}, + // @ts-expect-error + factory: async () => ({ a: 'a', c: 'c' }), + })), + createServiceFactory(() => ({ + service: serviceRef, + deps: {}, + factory: async () => ({ a: 'a', b: 'b', c: 'c' }), + })), ], extensionPoints: [ // @ts-expect-error @@ -124,8 +143,7 @@ describe('TestBackend', () => { }); await startTestBackend({ - services: [sf], - features: [testModule()], + features: [testModule(), sf()], }); expect(testFn).toHaveBeenCalledWith('winning'); @@ -150,7 +168,6 @@ describe('TestBackend', () => { }); const backend = await startTestBackend({ - services: [], features: [testModule()], }); @@ -168,7 +185,7 @@ describe('TestBackend', () => { env.registerInit({ deps: { cache: coreServices.cache, - config: coreServices.config, + config: coreServices.rootConfig, database: coreServices.database, discovery: coreServices.discovery, httpRouter: coreServices.httpRouter, @@ -192,7 +209,6 @@ describe('TestBackend', () => { }); await startTestBackend({ - services: [], features: [testPlugin()], }); }); @@ -220,4 +236,89 @@ describe('TestBackend', () => { expect(res.status).toEqual(200); expect(res.body).toEqual({ message: 'pong' }); }); + + it('should provide extension point implementations', async () => { + expect.assertions(3); + + const extensionPointA = createExtensionPoint({ id: 'a' }); + const extensionPointB = createExtensionPoint({ id: 'b' }); + await expect( + startTestBackend({ + extensionPoints: [ + [extensionPointA, 'a'], + [extensionPointB, 'b'], + ], + features: [ + createBackendModule({ + pluginId: 'testA', + moduleId: 'test', + register(reg) { + reg.registerInit({ + deps: { ext: extensionPointA }, + async init({ ext }) { + expect(ext).toBe('a'); + }, + }); + }, + }), + createBackendModule({ + pluginId: 'testB', + moduleId: 'test', + register(reg) { + reg.registerInit({ + deps: { ext: extensionPointB }, + async init({ ext }) { + expect(ext).toBe('b'); + }, + }); + }, + }), + ], + }), + ).resolves.not.toBeUndefined(); + }); + + it('should reject extension point used by multiple plugins', async () => { + const extensionPointA = createExtensionPoint({ id: 'a' }); + await expect( + startTestBackend({ + extensionPoints: [[extensionPointA, 'a']], + features: [ + createBackendModule({ + pluginId: 'testA', + moduleId: 'test', + register(reg) { + reg.registerInit({ + deps: { ext: extensionPointA }, + async init() {}, + }); + }, + }), + createBackendModule({ + pluginId: 'testB', + moduleId: 'test', + register(reg) { + reg.registerInit({ + deps: { ext: extensionPointA }, + async init() {}, + }); + }, + }), + ], + }), + ).rejects.toThrow( + "Extension point registered for plugin 'testA' may not be used by module for plugin 'testB'", + ); + }); + + it('should reject extension point not used by any plugin', async () => { + const extensionPointA = createExtensionPoint({ id: 'a' }); + await expect( + startTestBackend({ + extensionPoints: [[extensionPointA, 'a']], + }), + ).rejects.toThrow( + "Unable to determine the plugin ID of extension point(s) 'a'. Tested extension points must be depended on by one or more tested modules.", + ); + }); }); diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.ts index e8df15862f..97da3543ae 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.ts @@ -24,31 +24,21 @@ import { } from '@backstage/backend-app-api'; import { HostDiscovery } from '@backstage/backend-common'; import { - ServiceFactory, - ServiceRef, createServiceFactory, BackendFeature, ExtensionPoint, coreServices, - createBackendPlugin, + createBackendModule, } from '@backstage/backend-plugin-api'; import { mockServices } from '../services'; import { ConfigReader } from '@backstage/config'; import express from 'express'; +// Direct internal import to avoid duplication +// eslint-disable-next-line @backstage/no-forbidden-package-imports +import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring/types'; /** @public */ -export interface TestBackendOptions< - TServices extends any[], - TExtensionPoints extends any[], -> { - services?: readonly [ - ...{ - [index in keyof TServices]: - | ServiceFactory - | (() => ServiceFactory) - | [ServiceRef, Partial]; - }, - ]; +export interface TestBackendOptions { extensionPoints?: readonly [ ...{ [index in keyof TExtensionPoints]: [ @@ -57,7 +47,7 @@ export interface TestBackendOptions< ]; }, ]; - features?: BackendFeature[]; + features?: Array BackendFeature)>; } /** @public */ @@ -73,7 +63,7 @@ export interface TestBackend extends Backend { const defaultServiceFactories = [ mockServices.cache.factory(), - mockServices.config.factory(), + mockServices.rootConfig.factory(), mockServices.database.factory(), mockServices.httpRouter.factory(), mockServices.identity.factory(), @@ -87,28 +77,115 @@ const defaultServiceFactories = [ mockServices.urlReader.factory(), ]; +/** + * Given a set of extension points and plugins, find + * @returns + */ +function createExtensionPointTestModules( + features: Array BackendFeature)>, + extensionPointTuples?: readonly [ + ref: ExtensionPoint, + impl: unknown, + ][], +): Array<() => BackendFeature> { + if (!extensionPointTuples) { + return []; + } + + const registrations = features.flatMap(featureOrFunction => { + const feature = + typeof featureOrFunction === 'function' + ? featureOrFunction() + : featureOrFunction; + + if (feature.$$type !== '@backstage/BackendFeature') { + throw new Error( + `Failed to add feature, invalid type '${feature.$$type}'`, + ); + } + + if (isInternalBackendFeature(feature)) { + if (feature.version !== 'v1') { + throw new Error( + `Failed to add feature, invalid version '${feature.version}'`, + ); + } + return feature.getRegistrations(); + } + return []; + }); + + const extensionPointMap = new Map( + extensionPointTuples.map(ep => [ep[0].id, ep]), + ); + const extensionPointsToSort = new Set(extensionPointMap.keys()); + const extensionPointsByPlugin = new Map(); + + for (const registration of registrations) { + if (registration.type === 'module') { + const testDep = Object.values(registration.init.deps).filter(dep => + extensionPointsToSort.has(dep.id), + ); + if (testDep.length > 0) { + let points = extensionPointsByPlugin.get(registration.pluginId); + if (!points) { + points = []; + extensionPointsByPlugin.set(registration.pluginId, points); + } + for (const { id } of testDep) { + points.push(id); + extensionPointsToSort.delete(id); + } + } + } + } + + if (extensionPointsToSort.size > 0) { + const list = Array.from(extensionPointsToSort) + .map(id => `'${id}'`) + .join(', '); + throw new Error( + `Unable to determine the plugin ID of extension point(s) ${list}. ` + + 'Tested extension points must be depended on by one or more tested modules.', + ); + } + + const modules = []; + + for (const [pluginId, pluginExtensionPointIds] of extensionPointsByPlugin) { + modules.push( + createBackendModule({ + pluginId, + moduleId: 'testExtensionPointRegistration', + register(reg) { + for (const id of pluginExtensionPointIds) { + const tuple = extensionPointMap.get(id)!; + reg.registerExtensionPoint(...tuple); + } + + reg.registerInit({ deps: {}, async init() {} }); + }, + }), + ); + } + + return modules; +} + const backendInstancesToCleanUp = new Array(); /** @public */ -export async function startTestBackend< - TServices extends any[], - TExtensionPoints extends any[], ->( - options: TestBackendOptions, +export async function startTestBackend( + options: TestBackendOptions, ): Promise { - const { - services = [], - extensionPoints = [], - features = [], - ...otherOptions - } = options; + const { extensionPoints, features = [], ...otherOptions } = options; let server: ExtendedHttpServer; const rootHttpRouterFactory = createServiceFactory({ service: coreServices.rootHttpRouter, deps: { - config: coreServices.config, + config: coreServices.rootConfig, lifecycle: coreServices.rootLifecycle, rootLogger: coreServices.rootLogger, }, @@ -157,55 +234,20 @@ export async function startTestBackend< }, }); - const factories = services.map(serviceDef => { - if (Array.isArray(serviceDef)) { - // if type is ExtensionPoint? - // do something differently? - const [ref, impl] = serviceDef; - if (ref.scope === 'plugin') { - return createServiceFactory({ - service: ref as ServiceRef, - deps: {}, - factory: async () => impl, - })(); - } - return createServiceFactory({ - service: ref as ServiceRef, - deps: {}, - factory: async () => impl, - })(); - } - if (typeof serviceDef === 'function') { - return serviceDef(); - } - return serviceDef as ServiceFactory; - }); - - for (const factory of defaultServiceFactories) { - if (!factories.some(f => f.service.id === factory.service.id)) { - factories.push(factory); - } - } - const backend = createSpecializedBackend({ ...otherOptions, - services: [...factories, rootHttpRouterFactory, discoveryFactory], + defaultServiceFactories: [ + ...defaultServiceFactories, + rootHttpRouterFactory, + discoveryFactory, + ], }); backendInstancesToCleanUp.push(backend); - backend.add( - createBackendPlugin({ - pluginId: `---test-extension-point-registrar`, - register(reg) { - for (const [ref, impl] of extensionPoints) { - reg.registerExtensionPoint(ref, impl); - } - - reg.registerInit({ deps: {}, async init() {} }); - }, - })(), - ); + for (const m of createExtensionPointTestModules(features, extensionPoints)) { + backend.add(m); + } for (const feature of features) { backend.add(feature); @@ -248,3 +290,11 @@ function registerTestHooks() { } registerTestHooks(); + +function isInternalBackendFeature( + feature: BackendFeature, +): feature is InternalBackendFeature { + return ( + typeof (feature as InternalBackendFeature).getRegistrations === 'function' + ); +} diff --git a/packages/backend/.snyk b/packages/backend/.snyk new file mode 100644 index 0000000000..ea5abd653d --- /dev/null +++ b/packages/backend/.snyk @@ -0,0 +1,10 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.22.1 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-ISOLATEDVM-3037320: + - '*': + reason: We do not pass any V8 cache data, and are therefore unaffected by this vulnerability + expires: 2033-07-02T16:55:57.077Z + created: 2023-07-02T16:55:57.077Z +patch: {} diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index d4359d6ece..76ac221f69 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,164 @@ # example-backend +## 0.2.86 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-elasticsearch@1.3.3 + - @backstage/plugin-search-backend-module-pg@0.5.9 + - @backstage/plugin-azure-devops-backend@0.3.27 + - @backstage/plugin-kubernetes-backend@0.11.3 + - @backstage/plugin-lighthouse-backend@0.2.4 + - @backstage/plugin-permission-backend@0.5.23 + - @backstage/plugin-scaffolder-backend@1.16.0 + - @backstage/plugin-devtools-backend@0.1.3 + - @backstage/plugin-techdocs-backend@1.6.5 + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/plugin-badges-backend@0.2.3 + - @backstage/plugin-events-backend@0.2.9 + - @backstage/plugin-search-backend@1.4.0 + - @backstage/plugin-kafka-backend@0.2.41 + - @backstage/plugin-proxy-backend@0.3.0 + - @backstage/plugin-todo-backend@0.2.0 + - @backstage/plugin-app-backend@0.3.48 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1 + - @backstage/plugin-auth-backend@0.18.6 + - @backstage/plugin-explore-backend@0.0.10 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.17 + - @backstage/plugin-entity-feedback-backend@0.1.6 + - @backstage/plugin-code-coverage-backend@0.2.14 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-linguist-backend@0.4.0 + - @backstage/plugin-playlist-backend@0.3.4 + - @backstage/plugin-jenkins-backend@0.2.3 + - @backstage/plugin-nomad-backend@0.1.2 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - example-app@0.2.86 + - @backstage/plugin-adr-backend@0.3.6 + - @backstage/plugin-azure-sites-backend@0.1.10 + - @backstage/plugin-graphql-backend@0.1.38 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/plugin-rollbar-backend@0.1.45 + - @backstage/plugin-tech-insights-backend@0.5.14 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32 + - @backstage/plugin-tech-insights-node@0.4.6 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 0.2.86-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.18.6-next.2 + - @backstage/plugin-scaffolder-backend@1.15.2-next.2 + - @backstage/plugin-explore-backend@0.0.10-next.2 + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/plugin-proxy-backend@0.3.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-app-backend@0.3.48-next.2 + - @backstage/plugin-linguist-backend@0.4.0-next.2 + - @backstage/plugin-techdocs-backend@1.6.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - example-app@0.2.86-next.2 + - @backstage/plugin-adr-backend@0.3.6-next.2 + - @backstage/plugin-azure-devops-backend@0.3.27-next.2 + - @backstage/plugin-badges-backend@0.2.3-next.2 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-devtools-backend@0.1.3-next.2 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.2 + - @backstage/plugin-events-backend@0.2.9-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + - @backstage/plugin-kafka-backend@0.2.41-next.2 + - @backstage/plugin-kubernetes-backend@0.11.3-next.2 + - @backstage/plugin-lighthouse-backend@0.2.4-next.2 + - @backstage/plugin-permission-backend@0.5.23-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-search-backend@1.4.0-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.3-next.2 + - @backstage/plugin-search-backend-module-pg@0.5.9-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-todo-backend@0.2.0-next.2 + - @backstage/plugin-tech-insights-backend@0.5.14-next.2 + - @backstage/plugin-tech-insights-node@0.4.6-next.2 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.17-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + - @backstage/plugin-azure-sites-backend@0.1.10-next.2 + - @backstage/plugin-code-coverage-backend@0.2.14-next.2 + - @backstage/plugin-graphql-backend@0.1.38-next.2 + - @backstage/plugin-jenkins-backend@0.2.3-next.2 + - @backstage/plugin-nomad-backend@0.1.2-next.2 + - @backstage/plugin-playlist-backend@0.3.4-next.2 + - @backstage/plugin-rollbar-backend@0.1.45-next.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32-next.2 + +## 0.2.86-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-elasticsearch@1.3.3-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.9-next.1 + - @backstage/plugin-azure-devops-backend@0.3.27-next.1 + - @backstage/plugin-kubernetes-backend@0.11.3-next.1 + - @backstage/plugin-lighthouse-backend@0.2.4-next.1 + - @backstage/plugin-permission-backend@0.5.23-next.1 + - @backstage/plugin-scaffolder-backend@1.15.2-next.1 + - @backstage/plugin-devtools-backend@0.1.3-next.1 + - @backstage/plugin-techdocs-backend@1.6.5-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-badges-backend@0.2.3-next.1 + - @backstage/plugin-events-backend@0.2.9-next.1 + - @backstage/plugin-search-backend@1.4.0-next.1 + - @backstage/plugin-kafka-backend@0.2.41-next.1 + - @backstage/plugin-proxy-backend@0.2.42-next.1 + - @backstage/plugin-todo-backend@0.2.0-next.1 + - @backstage/plugin-app-backend@0.3.48-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.1-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.17-next.1 + - @backstage/plugin-entity-feedback-backend@0.1.6-next.1 + - @backstage/plugin-code-coverage-backend@0.2.14-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/plugin-linguist-backend@0.3.2-next.1 + - @backstage/plugin-playlist-backend@0.3.4-next.1 + - @backstage/plugin-explore-backend@0.0.10-next.1 + - @backstage/plugin-jenkins-backend@0.2.3-next.1 + - @backstage/plugin-nomad-backend@0.1.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/plugin-auth-backend@0.18.6-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-adr-backend@0.3.6-next.1 + - @backstage/plugin-azure-sites-backend@0.1.10-next.1 + - @backstage/plugin-graphql-backend@0.1.38-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/plugin-rollbar-backend@0.1.45-next.1 + - @backstage/plugin-tech-insights-backend@0.5.14-next.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.32-next.1 + - @backstage/plugin-tech-insights-node@0.4.6-next.1 + - example-app@0.2.86-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + ## 0.2.86-next.0 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 1c99a1bdf3..9e0896233d 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.86-next.0", + "version": "0.2.86", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli-node/CHANGELOG.md b/packages/cli-node/CHANGELOG.md index fc2876fa76..8a7af72d8d 100644 --- a/packages/cli-node/CHANGELOG.md +++ b/packages/cli-node/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/cli-node +## 0.1.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.1.3-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.1.2 ### Patch Changes diff --git a/packages/cli-node/package.json b/packages/cli-node/package.json index b5ba1855a8..589139a141 100644 --- a/packages/cli-node/package.json +++ b/packages/cli-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-node", "description": "Node.js library for Backstage CLIs", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 2652a40bde..9294e55efd 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/cli +## 0.22.10 + +### Patch Changes + +- 3f67cefb4780: Reload the frontend when app config changes +- cebbf8a27f3c: Enable to print the config schema not merged with the `--no-merge` flag +- 5c28ebc79fd6: Updated dependency `esbuild` to `^0.19.0`. +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/config-loader@1.4.0 + - @backstage/cli-node@0.1.3 + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/eslint-plugin@0.1.3 + - @backstage/release-manifests@0.0.9 + - @backstage/types@1.1.0 + +## 0.22.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.4.0-next.1 + - @backstage/cli-node@0.1.3-next.0 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/eslint-plugin@0.1.3 + - @backstage/release-manifests@0.0.9 + - @backstage/types@1.1.0 + ## 0.22.10-next.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 8b78b16900..95095fe78c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.22.10-next.0", + "version": "0.22.10", "publishConfig": { "access": "public" }, @@ -78,7 +78,7 @@ "cross-spawn": "^7.0.3", "css-loader": "^6.5.1", "diff": "^5.0.0", - "esbuild": "^0.18.0", + "esbuild": "^0.19.0", "esbuild-loader": "^2.18.0", "eslint": "^8.6.0", "eslint-config-prettier": "^8.3.0", @@ -167,7 +167,7 @@ "del": "^7.0.0", "mock-fs": "^5.1.0", "msw": "^1.0.0", - "nodemon": "^2.0.2", + "nodemon": "^3.0.1", "ts-node": "^10.0.0", "type-fest": "^2.19.0" }, diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 0782876942..909bb71b37 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,89 @@ # @backstage/config-loader +## 1.4.0 + +### Minor Changes + +- 2f1859585998: Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. + + In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. + +- cd514545d1d0: Adds a new `deepVisibility` schema keyword that sets child visibility recursively to the defined value, respecting preexisting values or child `deepVisibility`. + + Example usage: + + ```ts + export interface Config { + /** + * Enforces a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + secretValue: { + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + + Example of a schema that would not be allowed: + + ```ts + export interface Config { + /** + * Set the top level property to secret, enforcing a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + frontendUrl: { + /** + * We can NOT override the visibility to reveal a property to the front end. + * @visibility frontend + */ + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.4.0-next.1 + +### Minor Changes + +- 2f1859585998: Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. + + In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 1.4.0-next.0 ### Minor Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 51a4a5a6cd..7fba2d8acb 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "1.4.0-next.0", + "version": "1.4.0", "publishConfig": { "access": "public", "main": "dist/index.cjs.js", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 1de645fb9e..b09076f773 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-app-api +## 1.9.1 + +### Patch Changes + +- 9ae4e7e63836: Fixed a bug that could cause `navigate` analytics events to be misattributed to the plugin mounted on the root route (e.g. the `home` plugin at `/`) when the route that was navigated to wasn't associated with a routable extension. +- Updated dependencies + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + ## 1.9.1-next.0 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 15dca326e0..ff43d81329 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "1.9.1-next.0", + "version": "1.9.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 4ebd92ceb6..af5e2b8d6a 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/core-components +## 0.13.4 + +### Patch Changes + +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- Updated dependencies + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/version-bridge@1.0.4 + ## 0.13.4-next.0 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index c9b1584b85..641a4e7270 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.13.4-next.0", + "version": "0.13.4", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 9a65a15f98..9ddd48393b 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/create-app +## 0.5.4 + +### Patch Changes + +- b441642fbe0d: Bumped create-app version. +- 572abc7edf55: Bumped create-app version. +- 74f77f151a96: Bumped create-app version. +- 5cc0ac5ef3d1: Bump to a newer version of the `concurrently` library +- 46c9a798e41d: Updated the `app-config.yaml` template to use `proxy.endpoints`. +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/cli-common@0.1.12 + +## 0.5.4-next.2 + +### Patch Changes + +- Bumped create-app version. + +## 0.5.4-next.1 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.12 + ## 0.5.4-next.0 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index fb203edf26..4f629d951e 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.4-next.0", + "version": "0.5.4", "publishConfig": { "access": "public" }, @@ -49,7 +49,7 @@ "@types/node": "^16.11.26", "@types/recursive-readdir": "^2.2.0", "mock-fs": "^5.1.1", - "nodemon": "^2.0.2", + "nodemon": "^3.0.1", "ts-node": "^10.0.0" }, "nodemonConfig": { diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index 194549f079..08d2abff59 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -46,9 +46,10 @@ integrations: proxy: ### Example for how to add a proxy endpoint for the frontend. ### A typical reason to do this is to handle HTTPS and CORS for internal services. - # '/test': - # target: 'https://example.com' - # changeOrigin: true + # endpoints: + # '/test': + # target: 'https://example.com' + # changeOrigin: true # Reference documentation http://backstage.io/docs/features/techdocs/configuration # Note: After experimenting with basic setup, use CI/CD to generate docs diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index 0682114a35..c53ed6970f 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -31,7 +31,7 @@ "devDependencies": { "@backstage/cli": "^{{version '@backstage/cli'}}", "@spotify/prettier-config": "^12.0.0", - "concurrently": "^6.0.0", + "concurrently": "^8.0.0", "lerna": "^4.0.0", "node-gyp": "^9.0.0", "prettier": "^2.3.2", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 45df686876..99fb4f1372 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/dev-utils +## 1.0.18 + +### Patch Changes + +- 254ad469f053: Removed deprecated calls to `app.getProvider()` and `app.getRouter()` in `DevAppBuilder` +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/app-defaults@1.4.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 1.0.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 1.0.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 1.0.18-next.0 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d7e12209c3..44b16b4a62 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "1.0.18-next.0", + "version": "1.0.18", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 36e87855ed..5f2b21b8d2 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -15,7 +15,7 @@ */ import { createApp } from '@backstage/app-defaults'; -import { FlatRoutes } from '@backstage/core-app-api'; +import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; import { AlertDisplay, OAuthRequestDialog, @@ -195,35 +195,30 @@ export class DevAppBuilder { }, }); - const AppProvider = app.getProvider(); - const AppRouter = app.getRouter(); + const DevApp = ( + <> + + + {this.rootChildren} + + + + + {this.sidebarItems} + + + + + + {this.routes} + } /> + + + + + ); - const DevApp = () => { - return ( - - - - {this.rootChildren} - - - - - {this.sidebarItems} - - - - - - {this.routes} - } /> - - - - - ); - }; - - return DevApp; + return app.createRoot(DevApp); } /** diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index f6f29ff3f2..6bf5527c4b 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,30 @@ # e2e-test +## 0.2.6 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4-next.2 + +## 0.2.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.4-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + ## 0.2.6-next.0 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 48ef90db6f..48d4643394 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.6-next.0", + "version": "0.2.6", "private": true, "backstage": { "role": "cli" @@ -35,7 +35,7 @@ "cross-fetch": "^3.1.5", "fs-extra": "10.1.0", "handlebars": "^4.7.3", - "pgtools": "^0.3.0", + "pgtools": "^1.0.0", "puppeteer": "^17.0.0", "tree-kill": "^1.2.2" }, @@ -44,7 +44,7 @@ "@types/fs-extra": "^9.0.1", "@types/node": "^16.11.26", "@types/puppeteer": "^5.4.4", - "nodemon": "^2.0.2", + "nodemon": "^3.0.1", "ts-node": "^10.0.0" }, "nodemonConfig": { diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index 4f275a8750..9d08c09bd9 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/integration-react +## 1.1.16 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## 1.1.16-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + ## 1.1.16-next.0 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index b39d4fcb0c..ec68a20f36 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.16-next.0", + "version": "1.1.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index d1f3f46306..b4b97f579e 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/integration +## 1.6.0 + +### Minor Changes + +- 443afcf7f567: Added `buildGerritGitilesArchiveUrl()` to construct a Gitiles URL to download an archive. + Gitiles URL that uses an authenticated prefix (`/a/`) can now be parsed by the integration. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 1.5.1 ### Patch Changes diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index c2501b4ac2..7266950ac4 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -172,6 +172,14 @@ export type BitbucketServerIntegrationConfig = { password?: string; }; +// @public +export function buildGerritGitilesArchiveUrl( + config: GerritIntegrationConfig, + project: string, + branch: string, + filePath: string, +): string; + // @public export class DefaultGithubCredentialsProvider implements GithubCredentialsProvider diff --git a/packages/integration/package.json b/packages/integration/package.json index e02e070266..b0da382abd 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration", "description": "Helpers for managing integrations towards external systems", - "version": "1.5.1", + "version": "1.6.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration/src/gerrit/core.test.ts b/packages/integration/src/gerrit/core.test.ts index 5a829e88f9..c00bae384f 100644 --- a/packages/integration/src/gerrit/core.test.ts +++ b/packages/integration/src/gerrit/core.test.ts @@ -20,6 +20,7 @@ import fetch from 'cross-fetch'; import { setupRequestMockHandlers } from '../helpers'; import { GerritIntegrationConfig } from './config'; import { + buildGerritGitilesArchiveUrl, buildGerritGitilesUrl, getGerritBranchApiUrl, getGerritCloneRepoUrl, @@ -33,6 +34,41 @@ describe('gerrit core', () => { const worker = setupServer(); setupRequestMockHandlers(worker); + describe('buildGerritGitilesArchiveUrl', () => { + const config: GerritIntegrationConfig = { + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.com/gitiles', + }; + it('can create an archive url for a branch', () => { + expect(buildGerritGitilesArchiveUrl(config, 'repo', 'dev', '')).toEqual( + 'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev.tar.gz', + ); + + expect(buildGerritGitilesArchiveUrl(config, 'repo', 'dev', '/')).toEqual( + 'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev.tar.gz', + ); + }); + it('can create an archive url for a specific directory', () => { + expect( + buildGerritGitilesArchiveUrl(config, 'repo', 'dev', 'docs'), + ).toEqual( + 'https://gerrit.com/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz', + ); + }); + it('can create an authenticated url when auth is enabled', () => { + const authConfig = { + ...config, + username: 'username', + password: 'password', + }; + expect( + buildGerritGitilesArchiveUrl(authConfig, 'repo', 'dev', 'docs'), + ).toEqual( + 'https://gerrit.com/a/gitiles/repo/+archive/refs/heads/dev/docs.tar.gz', + ); + }); + }); + describe('buildGerritGitilesUrl', () => { it('can create an url from arguments', () => { const config: GerritIntegrationConfig = { @@ -86,6 +122,25 @@ describe('gerrit core', () => { ); expect(rootPath).toEqual('/'); }); + it('can parse a valid authenticated gitiles url.', () => { + const config: GerritIntegrationConfig = { + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.com/gitiles', + }; + const { branch, filePath, project } = parseGerritGitilesUrl( + config, + 'https://gerrit.com/a/gitiles/web/project/+/refs/heads/master/README.md', + ); + expect(project).toEqual('web/project'); + expect(branch).toEqual('master'); + expect(filePath).toEqual('README.md'); + + const { filePath: rootPath } = parseGerritGitilesUrl( + config, + 'https://gerrit.com/gitiles/web/project/+/refs/heads/master', + ); + expect(rootPath).toEqual('/'); + }); it('throws on incorrect gitiles urls.', () => { const config: GerritIntegrationConfig = { host: 'gerrit.com', diff --git a/packages/integration/src/gerrit/core.ts b/packages/integration/src/gerrit/core.ts index 0403d8fc75..107d5a4469 100644 --- a/packages/integration/src/gerrit/core.ts +++ b/packages/integration/src/gerrit/core.ts @@ -37,6 +37,7 @@ const GERRIT_BODY_PREFIX = ")]}'"; * * Gitiles url: * https://g.com/optional_path/\{project\}/+/refs/heads/\{branch\}/\{filePath\} + * https://g.com/a/optional_path/\{project\}/+/refs/heads/\{branch\}/\{filePath\} * * * @param url - An URL pointing to a file stored in git. @@ -47,7 +48,17 @@ export function parseGerritGitilesUrl( config: GerritIntegrationConfig, url: string, ): { branch: string; filePath: string; project: string } { - const urlPath = url.replace(config.gitilesBaseUrl!, ''); + const baseUrlParse = new URL(config.gitilesBaseUrl!); + const urlParse = new URL(url); + + // Remove the gerrit authentication prefix '/a/' from the url + // In case of the gitilesBaseUrl is https://review.gerrit.com/plugins/gitiles + // and the url provided is https://review.gerrit.com/a/plugins/gitiles/... + // remove the prefix only if the pathname start with '/a/' + const urlPath = urlParse.pathname + .substring(urlParse.pathname.startsWith('/a/') ? 2 : 0) + .replace(baseUrlParse.pathname, ''); + const parts = urlPath.split('/').filter(p => !!p); const projectEndIndex = parts.indexOf('+'); @@ -91,6 +102,28 @@ export function buildGerritGitilesUrl( }/${project}/+/refs/heads/${branch}/${trimStart(filePath, '/')}`; } +/** + * Build a Gerrit Gitiles archive url that targets a specific branch and path + * + * @param config - A Gerrit provider config. + * @param project - The name of the git project + * @param branch - The branch we will target. + * @param filePath - The absolute file path. + * @public + */ +export function buildGerritGitilesArchiveUrl( + config: GerritIntegrationConfig, + project: string, + branch: string, + filePath: string, +): string { + const archiveName = + filePath === '/' || filePath === '' ? '.tar.gz' : `/${filePath}.tar.gz`; + return `${getGitilesAuthenticationUrl( + config, + )}/${project}/+archive/refs/heads/${branch}${archiveName}`; +} + /** * Return the authentication prefix. * @@ -109,6 +142,26 @@ export function getAuthenticationPrefix( return config.password ? '/a/' : '/'; } +/** + * Return the authentication gitiles url. + * + * @remarks + * + * To authenticate with a password the API url must be prefixed with "/a/". + * If no password is set anonymous access (without the prefix) will + * be used. + * + * @param config - A Gerrit provider config. + */ +export function getGitilesAuthenticationUrl( + config: GerritIntegrationConfig, +): string { + const parsedUrl = new URL(config.gitilesBaseUrl!); + return `${parsedUrl.protocol}//${parsedUrl.host}${getAuthenticationPrefix( + config, + )}${parsedUrl.pathname.substring(1)}`; +} + /** * Return the url to get branch info from the Gerrit API. * diff --git a/packages/integration/src/gerrit/index.ts b/packages/integration/src/gerrit/index.ts index 127c949c4e..6c6295f7a3 100644 --- a/packages/integration/src/gerrit/index.ts +++ b/packages/integration/src/gerrit/index.ts @@ -19,6 +19,7 @@ export { readGerritIntegrationConfigs, } from './config'; export { + buildGerritGitilesArchiveUrl, getGerritBranchApiUrl, getGerritCloneRepoUrl, getGerritFileContentsApiUrl, diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index b8f37f4977..ddebca0746 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/repo-tools +## 0.3.3 + +### Patch Changes + +- 75702e85862a: Bumped `@microsoft/api-extractor` dependency to `^7.36.4`, and `@microsoft/api-documenter` to `^7.22.33`. +- 1f3337ebc707: Introducing a new, experimental command `backstage-repo-tools generate-catalog-info`, which can be used to create standardized `catalog-info.yaml` files for each Backstage package in a Backstage monorepo. It can also be used to automatically fix existing `catalog-info.yaml` files with the correct metadata (including `metadata.name`, `metadata.title`, and `metadata.description` introspected from the package's `package.json`, as well as `spec.owner` introspected from `CODEOWNERS`), e.g. in a post-commit hook. +- ebeb77586975: Update `schema openapi generate` command to now create a default router that can be imported and used directly. +- Updated dependencies + - @backstage/cli-node@0.1.3 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + +## 0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.1.3-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + ## 0.3.3-next.0 ### Patch Changes diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index a0df5e721c..9c87bd8dff 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -14,6 +14,7 @@ Options: Commands: api-reports [options] [paths...] type-deps + generate-catalog-info [options] schema [command] help [command] ``` @@ -36,6 +37,16 @@ Options: -h, --help ``` +### `backstage-repo-tools generate-catalog-info` + +``` +Usage: backstage-repo-tools generate-catalog-info [options] + +Options: + --dry-run + -h, --help +``` + ### `backstage-repo-tools schema` ``` diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index c05cd30c75..82099ea621 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.3.3-next.0", + "version": "0.3.3", "publishConfig": { "access": "public" }, @@ -32,12 +32,13 @@ "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", "@apisyouwonthate/style-guide": "^1.4.0", + "@backstage/catalog-model": "workspace:^", "@backstage/cli-common": "workspace:^", "@backstage/cli-node": "workspace:^", "@backstage/errors": "workspace:^", "@manypkg/get-packages": "^1.1.3", - "@microsoft/api-documenter": "^7.19.27", - "@microsoft/api-extractor": "^7.33.7", + "@microsoft/api-documenter": "^7.22.33", + "@microsoft/api-extractor": "^7.36.4", "@stoplight/spectral-core": "^1.18.0", "@stoplight/spectral-formatters": "^1.1.0", "@stoplight/spectral-functions": "^1.7.2", @@ -46,6 +47,7 @@ "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^13.14.0", "chalk": "^4.0.0", + "codeowners-utils": "^1.0.2", "commander": "^9.1.0", "fs-extra": "10.1.0", "glob": "^8.0.3", @@ -54,7 +56,8 @@ "lodash": "^4.17.21", "minimatch": "^5.1.1", "p-limit": "^3.0.2", - "ts-node": "^10.0.0" + "ts-node": "^10.0.0", + "yaml-diff-patch": "^2.0.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/packages/repo-tools/src/commands/generate-catalog-info/codeowners.ts b/packages/repo-tools/src/commands/generate-catalog-info/codeowners.ts new file mode 100644 index 0000000000..f485e872e9 --- /dev/null +++ b/packages/repo-tools/src/commands/generate-catalog-info/codeowners.ts @@ -0,0 +1,69 @@ +/* + * 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 { + CodeOwnersEntry, + CODEOWNERS_PATHS, + matchFile as matchCodeowner, + parse as parseCodeowners, +} from 'codeowners-utils'; +import { relative as relativePath, resolve as resolvePath } from 'path'; +import { readFile } from './utils'; + +export async function loadCodeowners(): Promise { + const maybeFiles = await Promise.allSettled( + CODEOWNERS_PATHS.map(async path => + readFile(resolvePath('.', path), { encoding: 'utf-8' }), + ), + ); + const file = maybeFiles.find( + maybeFile => maybeFile.status === 'fulfilled', + ) as PromiseFulfilledResult | undefined; + + if (!file) { + throw new Error( + 'This utility expects a CODEOWNERS file, but no such file was found.', + ); + } + + return parseCodeowners(file.value); +} + +export function getPossibleCodeowners( + codeowners: CodeOwnersEntry[], + relPath: string, +): string[] { + const codeownerMaybe = matchCodeowner(relPath, codeowners); + return codeownerMaybe + ? codeownerMaybe.owners.map( + owner => (owner.match(/(?:\@[^\/]+\/)?([^\@\/]*)$/) || [])[1], + ) + : []; +} + +export function getOwnerFromCodeowners( + codeowners: CodeOwnersEntry[], + absPath: string, +): string { + const relPath = relativePath('.', absPath); + const possibleOwners = getPossibleCodeowners(codeowners, relPath); + const owner = possibleOwners.slice(-1)[0]; + + if (!owner) { + throw new Error(`${relPath} isn't owned by anyone in CODEOWNERS`); + } + + return owner; +} diff --git a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts new file mode 100644 index 0000000000..08f73898c8 --- /dev/null +++ b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts @@ -0,0 +1,200 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import YAML from 'js-yaml'; +import pLimit from 'p-limit'; +import { relative as relativePath, resolve as resolvePath } from 'path'; +import { yamlOverwrite } from 'yaml-diff-patch'; +import chalk from 'chalk'; +import { PackageGraph, PackageRole } from '@backstage/cli-node'; +import { Entity } from '@backstage/catalog-model'; +import { + getOwnerFromCodeowners, + getPossibleCodeowners, + loadCodeowners, +} from './codeowners'; +import { + BackstagePackageJson, + isBackstagePackage, + readFile, + writeFile, +} from './utils'; +import { CodeOwnersEntry } from 'codeowners-utils'; + +type CreateFixPackageInfoYamlsOptions = { + dryRun?: boolean; +}; + +export default async (opts: CreateFixPackageInfoYamlsOptions) => { + const { dryRun = false } = opts; + const packages = await PackageGraph.listTargetPackages(); + const codeowners = await loadCodeowners(); + const limit = pLimit(10); + + const results = await Promise.allSettled( + packages.map(({ packageJson, dir }) => + limit(async () => { + if (!isBackstagePackage(packageJson)) { + return; + } + + // Check if there is already a corresponding catalog-info.yaml + const infoYamlPath = resolvePath(dir, 'catalog-info.yaml'); + let yamlString = ''; + + try { + yamlString = await readFile(infoYamlPath, { encoding: 'utf-8' }); + } catch (e) { + if (e.code === 'ENOENT') { + await createCatalogInfoYaml({ + yamlPath: infoYamlPath, + packageJson, + codeowners, + dryRun, + }); + return; + } + + throw e; + } + + await fixCatalogInfoYaml({ + yamlPath: infoYamlPath, + packageJson, + codeowners, + yamlString, + dryRun, + }); + }), + ), + ); + + const rejects = results.filter( + r => r.status === 'rejected', + ) as PromiseRejectedResult[]; + if (rejects.length > 0) { + // Problems encountered. Print details here. + console.error( + chalk.red('Unable to create or fix catalog-info.yaml files\n'), + ); + rejects.forEach(reject => console.error(` ${reject.reason}`)); + console.error(); + process.exit(1); + } +}; + +type CreateOptions = { + yamlPath: string; + packageJson: BackstagePackageJson; + codeowners: CodeOwnersEntry[]; + dryRun: boolean; +}; + +type FixOptions = CreateOptions & { + yamlString: string; +}; + +type BackstagePackageEntity = Entity & { + spec: { + type: 'backstage-package'; + backstageRole: PackageRole; + lifecycle: string; + owner: string; + [key: string]: any; + }; +}; + +function createCatalogInfoYaml(options: CreateOptions) { + const { codeowners, dryRun, packageJson, yamlPath } = options; + const owner = getOwnerFromCodeowners(codeowners, yamlPath); + const entity = createOrMergeEntity(packageJson, owner); + + return dryRun + ? Promise.resolve(console.error(`Create ${relativePath('.', yamlPath)}`)) + : writeFile(yamlPath, YAML.dump(entity)); +} + +function fixCatalogInfoYaml(options: FixOptions) { + const { codeowners, dryRun, packageJson, yamlPath, yamlString } = options; + const possibleOwners = getPossibleCodeowners( + codeowners, + relativePath('.', yamlPath), + ); + const safeName = packageJson.name + .replace(/[^a-z0-9_\-\.]+/g, '-') + .replace(/^[^a-z0-9]|[^a-z0-9]$/g, ''); + let yamlJson: BackstagePackageEntity; + + try { + yamlJson = YAML.load(yamlString) as BackstagePackageEntity; + } catch (e) { + throw new Error(`Unable to parse ${relativePath('.', yamlPath)}: ${e}`); + } + + const badOwner = !possibleOwners.includes(yamlJson.spec?.owner); + const badTitle = yamlJson.metadata.title !== packageJson.name; + const badName = yamlJson.metadata.name !== safeName; + const badType = + yamlJson.spec?.type !== `backstage-${packageJson.backstage.role}`; + const badDesc = yamlJson.metadata.description !== packageJson.description; + + if (badOwner || badTitle || badName || badType || badDesc) { + const owner = badOwner + ? getOwnerFromCodeowners(codeowners, yamlPath) + : yamlJson.spec?.owner; + const newJson = createOrMergeEntity(packageJson, owner, yamlJson); + return dryRun + ? Promise.resolve(console.error(`Update ${relativePath('.', yamlPath)}`)) + : writeFile(yamlPath, yamlOverwrite(yamlString, newJson)); + } + + return Promise.resolve(); +} + +/** + * Canonical representation on how to create (or update) a backstage-package + * component. + */ +function createOrMergeEntity( + packageJson: BackstagePackageJson, + owner: string, + existingEntity: BackstagePackageEntity | Record = {}, +): BackstagePackageEntity { + const safeEntityName = packageJson.name + .replace(/[^a-z0-9_\-\.]+/g, '-') + .replace(/^[^a-z0-9]|[^a-z0-9]$/g, ''); + + return { + ...existingEntity, + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + ...existingEntity.metadata, + // Provide default name/title/description values. + name: safeEntityName, + title: packageJson.name, + ...(packageJson.description + ? { description: packageJson.description } + : undefined), + }, + spec: { + lifecycle: 'experimental', + ...existingEntity.spec, + type: `backstage-${packageJson.backstage.role}`, + owner, + }, + }; +} diff --git a/packages/repo-tools/src/commands/generate-catalog-info/utils.ts b/packages/repo-tools/src/commands/generate-catalog-info/utils.ts new file mode 100644 index 0000000000..f446f4e867 --- /dev/null +++ b/packages/repo-tools/src/commands/generate-catalog-info/utils.ts @@ -0,0 +1,42 @@ +/* + * 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 fs from 'fs'; +import { Package } from '@manypkg/get-packages'; +import { + BackstagePackageJson as BackstagePackageJsonActual, + PackageRole, +} from '@backstage/cli-node'; +import { promisify } from 'util'; + +export const readFile = promisify(fs.readFile); +export const writeFile = promisify(fs.writeFile); + +export type BackstagePackageJson = BackstagePackageJsonActual & { + description?: string; + backstage: { + role: PackageRole; + }; +}; + +export function isBackstagePackage( + packageJson: Package['packageJson'], +): packageJson is BackstagePackageJson { + return ( + packageJson && + packageJson.hasOwnProperty('backstage') && + (packageJson as any)?.backstage?.role !== 'undefined' + ); +} diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index e19ffde4b8..7aa574d291 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -96,6 +96,21 @@ export function registerCommands(program: Command) { .description('Find inconsistencies in types of all packages and plugins') .action(lazy(() => import('./type-deps/type-deps').then(m => m.default))); + program + .command('generate-catalog-info') + .option( + '--dry-run', + 'Shows what would happen without actually writing any yaml.', + ) + .description('Create or fix info yaml files for all backstage packages') + .action( + lazy(() => + import('./generate-catalog-info/generate-catalog-info').then( + m => m.default, + ), + ), + ); + registerSchemaCommand(program); } diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 26f2246d13..6728d9de41 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,53 @@ # techdocs-cli-embedded-app +## 0.2.85 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/integration-react@1.1.16 + - @backstage/cli@0.22.10 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/app-defaults@1.4.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/plugin-techdocs@1.6.6 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9 + +## 0.2.85-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.2 + - @backstage/plugin-techdocs@1.6.6-next.2 + - @backstage/cli@0.22.10-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 0.2.85-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/cli@0.22.10-next.1 + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/plugin-techdocs@1.6.6-next.1 + - @backstage/app-defaults@1.4.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + ## 0.2.85-next.0 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 1b6af7f9e9..c8097fca3b 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.85-next.0", + "version": "0.2.85", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index daefe75e38..8334aa54b6 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,36 @@ # @techdocs/cli +## 1.4.5 + +### Patch Changes + +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## 1.4.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## 1.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + ## 1.4.5-next.0 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index d59450992b..3dc8eda540 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.4.5-next.0", + "version": "1.4.5", "publishConfig": { "access": "public" }, @@ -46,7 +46,7 @@ "@types/webpack-env": "^1.15.3", "cypress": "^10.0.0", "find-process": "^1.4.5", - "nodemon": "^2.0.2", + "nodemon": "^3.0.1", "techdocs-cli-embedded-app": "link:../techdocs-cli-embedded-app", "ts-node": "^10.0.0" }, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index a3fb8ba382..d3e28b30d2 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/test-utils +## 1.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + ## 1.4.2-next.0 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index ef074d13b2..73ef2c5268 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "1.4.2-next.0", + "version": "1.4.2", "publishConfig": { "access": "public" }, diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index c63b8bd687..e038706836 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-adr-backend +## 0.3.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-adr-common@0.2.12 + - @backstage/plugin-search-common@1.2.5 + +## 0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/plugin-search-common@1.2.5 + ## 0.3.6-next.0 ### Patch Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index e7f390b790..c15eafd6cf 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.3.6-next.0", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index 9b8d5dbf74..dfe54806fe 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-adr-common +## 0.2.12 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/plugin-search-common@1.2.5 + ## 0.2.11 ### Patch Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index 2def65cf55..25e02845ef 100644 --- a/plugins/adr-common/package.json +++ b/plugins/adr-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-adr-common", "description": "Common functionalities for the adr plugin", - "version": "0.2.11", + "version": "0.2.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index fbbf4c13de..e25c80d4d2 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-adr +## 0.6.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-adr-common@0.2.12 + - @backstage/plugin-search-common@1.2.5 + +## 0.6.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 0.6.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-adr-common@0.2.11 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + ## 0.6.4-next.0 ### Patch Changes diff --git a/plugins/adr/package.json b/plugins/adr/package.json index b032ccad7b..37bf13619a 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.6.4-next.0", + "version": "0.6.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index c0f695f372..7eea9d8287 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-airbrake-backend +## 0.2.21 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + +## 0.2.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.21-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + ## 0.2.21-next.0 ### Patch Changes diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index 226126531b..530de9a77a 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.2.21-next.0", + "version": "0.2.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake-backend/src/plugin.ts b/plugins/airbrake-backend/src/plugin.ts index 79850c1272..90e790c05f 100644 --- a/plugins/airbrake-backend/src/plugin.ts +++ b/plugins/airbrake-backend/src/plugin.ts @@ -32,7 +32,7 @@ export const airbrakePlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, httpRouter: coreServices.httpRouter, }, diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index e5b52f1932..b4d5afccdc 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-airbrake +## 0.3.21 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/dev-utils@1.0.18 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.3.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/dev-utils@1.0.18-next.2 + +## 0.3.21-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/dev-utils@1.0.18-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.3.21-next.0 ### Patch Changes diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 8a156c775d..5ccd8bd076 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.21-next.0", + "version": "0.3.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index caa81f04cf..cd7c7973b5 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-allure +## 0.1.37 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.1.37-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.1.37-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.1.37-next.0 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 7a65d9f707..881d4d0c9d 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.37-next.0", + "version": "0.1.37", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index d7c09cddb1..b6ceb1c28d 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-analytics-module-ga +## 0.1.32 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## 0.1.32-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + ## 0.1.32-next.0 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 8fb8d42a9e..434f231545 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.32-next.0", + "version": "0.1.32", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga4/CHANGELOG.md b/plugins/analytics-module-ga4/CHANGELOG.md index 9715015579..bc08d64809 100644 --- a/plugins/analytics-module-ga4/CHANGELOG.md +++ b/plugins/analytics-module-ga4/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-analytics-module-ga4 +## 0.1.3 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + +## 0.1.3-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + ## 0.1.3-next.0 ### Patch Changes diff --git a/plugins/analytics-module-ga4/package.json b/plugins/analytics-module-ga4/package.json index 09e7240142..63f03e440b 100644 --- a/plugins/analytics-module-ga4/package.json +++ b/plugins/analytics-module-ga4/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga4", - "version": "0.1.3-next.0", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-newrelic-browser/CHANGELOG.md b/plugins/analytics-module-newrelic-browser/CHANGELOG.md index beea288700..3090cd3fc2 100644 --- a/plugins/analytics-module-newrelic-browser/CHANGELOG.md +++ b/plugins/analytics-module-newrelic-browser/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-analytics-module-newrelic-browser +## 0.0.1 + +### Patch Changes + +- ec7357258853: Introduced the New Relic Browser analytics module. Check out the plugins [README.md](https://github.com/backstage/backstage/tree/master/plugins/analytics-module-newrelic-browser) for more details! +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/config@1.0.8 + ## 0.0.1-next.0 ### Patch Changes diff --git a/plugins/analytics-module-newrelic-browser/package.json b/plugins/analytics-module-newrelic-browser/package.json index 677d3b8e16..ba72a1628b 100644 --- a/plugins/analytics-module-newrelic-browser/package.json +++ b/plugins/analytics-module-newrelic-browser/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-newrelic-browser", - "version": "0.0.1-next.0", + "version": "0.0.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index ef1d34dc23..e23c4a7c0c 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-apache-airflow +## 0.2.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + +## 0.2.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index 60afaa9a24..281d49c781 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.14-next.0", + "version": "0.2.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 68923046f5..f84d57542b 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-api-docs +## 0.9.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.9.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-catalog@1.12.1-next.2 + +## 0.9.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.9.7-next.0 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 625fbd3138..1fe4133515 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.9.7-next.0", + "version": "0.9.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index 0051c8a9e7..0833267e83 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-apollo-explorer +## 0.1.14 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + +## 0.1.14-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + ## 0.1.14-next.0 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index b9eba3278d..aae09fe19a 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.14-next.0", + "version": "0.1.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 7e048ba4d5..2ac0068ccc 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-app-backend +## 0.3.48 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- d564ad142b17: Migrated the alpha `appBackend` export to use static configuration and extension points rather than accepting options. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config-loader@1.4.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-app-node@0.1.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 0.3.48-next.2 + +### Patch Changes + +- d564ad142b17: Migrated the alpha `appBackend` export to use static configuration and extension points rather than accepting options. +- Updated dependencies + - @backstage/plugin-app-node@0.1.0-next.0 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/config-loader@1.4.0-next.1 + +## 0.3.48-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config-loader@1.4.0-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + ## 0.3.48-next.0 ### Patch Changes diff --git a/plugins/app-backend/alpha-api-report.md b/plugins/app-backend/alpha-api-report.md index 20a2902557..5fb0546a79 100644 --- a/plugins/app-backend/alpha-api-report.md +++ b/plugins/app-backend/alpha-api-report.md @@ -4,18 +4,9 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import express from 'express'; // @alpha -export const appPlugin: (options: AppPluginOptions) => BackendFeature; - -// @alpha (undocumented) -export type AppPluginOptions = { - appPackageName?: string; - staticFallbackHandler?: express.Handler; - disableConfigInjection?: boolean; - disableStaticFallbackCache?: boolean; -}; +export const appPlugin: () => BackendFeature; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/app-backend/config.d.ts b/plugins/app-backend/config.d.ts new file mode 100644 index 0000000000..dc11bc4e0a --- /dev/null +++ b/plugins/app-backend/config.d.ts @@ -0,0 +1,46 @@ +/* + * 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. + */ + +export interface Config { + app?: { + /** + * The name of the app package (in most Backstage repositories, this is the + * "name" field in `packages/app/package.json`) that content should be served + * from. The same app package should be added as a dependency to the backend + * package in order for it to be accessible at runtime. + * + * In a typical setup with a single app package, this will default to 'app'. + */ + packageName?: string; + + /** + * Disables the configuration injection. This can be useful if you're running in an environment + * with a read-only filesystem, or for some other reason don't want configuration to be injected. + * + * Note that this will cause the configuration used when building the app bundle to be used, unless + * a separate configuration loading strategy is set up. + * + * This also disables configuration injection though `APP_CONFIG_` environment variables. + */ + disableConfigInjection?: boolean; + + /** + * By default the app backend plugin will cache previously deployed static assets in the database. + * If you disable this, it is recommended to set a `staticFallbackHandler` instead. + */ + disableStaticFallbackCache?: boolean; + }; +} diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 5574c8aba3..05db6bb37d 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.48-next.0", + "version": "0.3.48", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -49,6 +49,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/config-loader": "workspace:^", + "@backstage/plugin-app-node": "workspace:^", "@backstage/types": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -73,8 +74,10 @@ "node-fetch": "^2.6.7", "supertest": "^6.1.3" }, + "configSchema": "config.d.ts", "files": [ "dist", + "config.d.ts", "migrations/**/*.{js,d.ts}", "static" ] diff --git a/plugins/app-backend/src/alpha.ts b/plugins/app-backend/src/alpha.ts index cc8276b853..edb5c8caf5 100644 --- a/plugins/app-backend/src/alpha.ts +++ b/plugins/app-backend/src/alpha.ts @@ -15,4 +15,3 @@ */ export { appPlugin } from './service/appPlugin'; -export type { AppPluginOptions } from './service/appPlugin'; diff --git a/plugins/app-backend/src/service/appPlugin.test.ts b/plugins/app-backend/src/service/appPlugin.test.ts index 518f56b7f9..b6a972b4c7 100644 --- a/plugins/app-backend/src/service/appPlugin.test.ts +++ b/plugins/app-backend/src/service/appPlugin.test.ts @@ -17,7 +17,7 @@ import mockFs from 'mock-fs'; import { resolve as resolvePath } from 'path'; import fetch from 'node-fetch'; -import { startTestBackend } from '@backstage/backend-test-utils'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { appPlugin } from './appPlugin'; describe('appPlugin', () => { @@ -40,9 +40,13 @@ describe('appPlugin', () => { it('boots', async () => { const { server } = await startTestBackend({ features: [ - appPlugin({ - appPackageName: 'app', - disableStaticFallbackCache: true, + appPlugin(), + mockServices.rootConfig.factory({ + data: { + app: { + disableStaticFallbackCache: true, + }, + }, }), ], }); diff --git a/plugins/app-backend/src/service/appPlugin.ts b/plugins/app-backend/src/service/appPlugin.ts index a29f8fd5a8..9ba2ea6d6c 100644 --- a/plugins/app-backend/src/service/appPlugin.ts +++ b/plugins/app-backend/src/service/appPlugin.ts @@ -21,84 +21,49 @@ import { } from '@backstage/backend-plugin-api'; import { createRouter } from './router'; import { loggerToWinstonLogger } from '@backstage/backend-common'; - -/** @alpha */ -export type AppPluginOptions = { - /** - * The name of the app package (in most Backstage repositories, this is the - * "name" field in `packages/app/package.json`) that content should be served - * from. The same app package should be added as a dependency to the backend - * package in order for it to be accessible at runtime. - * - * In a typical setup with a single app package, this will default to 'app'. - */ - appPackageName?: string; - - /** - * A request handler to handle requests for static content that are not present in the app bundle. - * - * This can be used to avoid issues with clients on older deployment versions trying to access lazy - * loaded content that is no longer present. Typically the requests would fall back to a long-term - * object store where all recently deployed versions of the app are present. - * - * Another option is to provide a `database` that will take care of storing the static assets instead. - * - * If both `database` and `staticFallbackHandler` are provided, the `database` will attempt to serve - * static assets first, and if they are not found, the `staticFallbackHandler` will be called. - */ - staticFallbackHandler?: express.Handler; - - /** - * Disables the configuration injection. This can be useful if you're running in an environment - * with a read-only filesystem, or for some other reason don't want configuration to be injected. - * - * Note that this will cause the configuration used when building the app bundle to be used, unless - * a separate configuration loading strategy is set up. - * - * This also disables configuration injection though `APP_CONFIG_` environment variables. - */ - disableConfigInjection?: boolean; - - /** - * By default the app backend plugin will cache previously deployed static assets in the database. - * If you disable this, it is recommended to set a `staticFallbackHandler` instead. - */ - disableStaticFallbackCache?: boolean; -}; +import { staticFallbackHandlerExtensionPoint } from '@backstage/plugin-app-node'; /** * The App plugin is responsible for serving the frontend app bundle and static assets. * @alpha */ -export const appPlugin = createBackendPlugin((options: AppPluginOptions) => ({ +export const appPlugin = createBackendPlugin({ pluginId: 'app', register(env) { + let staticFallbackHandler: express.Handler | undefined; + + env.registerExtensionPoint(staticFallbackHandlerExtensionPoint, { + setStaticFallbackHandler(handler) { + if (staticFallbackHandler) { + throw new Error( + 'Attempted to install a static fallback handler for the app-backend twice', + ); + } + staticFallbackHandler = handler; + }, + }); + env.registerInit({ deps: { logger: coreServices.logger, - config: coreServices.config, + config: coreServices.rootConfig, database: coreServices.database, httpRouter: coreServices.httpRouter, }, async init({ logger, config, database, httpRouter }) { - const { - appPackageName, - staticFallbackHandler, - disableConfigInjection, - disableStaticFallbackCache, - } = options; + const appPackageName = + config.getOptionalString('app.packageName') ?? 'app'; const winstonLogger = loggerToWinstonLogger(logger); const router = await createRouter({ logger: winstonLogger, config, - database: disableStaticFallbackCache ? undefined : database, - appPackageName: appPackageName ?? 'app', + database, + appPackageName, staticFallbackHandler, - disableConfigInjection, }); httpRouter.use(router); }, }); }, -})); +}); diff --git a/plugins/app-backend/src/service/router.ts b/plugins/app-backend/src/service/router.ts index 4e94138439..40e291c19d 100644 --- a/plugins/app-backend/src/service/router.ts +++ b/plugins/app-backend/src/service/router.ts @@ -92,6 +92,13 @@ export async function createRouter( ): Promise { const { config, logger, appPackageName, staticFallbackHandler } = options; + const disableConfigInjection = + options.disableConfigInjection ?? + config.getOptionalBoolean('app.disableConfigInjection'); + const disableStaticFallbackCache = config.getOptionalBoolean( + 'app.disableStaticFallbackCache', + ); + const appDistDir = resolvePackagePath(appPackageName, 'dist'); const staticDir = resolvePath(appDistDir, 'static'); @@ -107,7 +114,7 @@ export async function createRouter( logger.info(`Serving static app content from ${appDistDir}`); - if (!options.disableConfigInjection) { + if (!disableConfigInjection) { const appConfigs = await readConfigs({ config, appDistDir, @@ -131,7 +138,7 @@ export async function createRouter( }), ); - if (options.database) { + if (options.database && !disableStaticFallbackCache) { const store = await StaticAssetsStore.create({ logger, database: options.database, diff --git a/plugins/app-node/.eslintrc.js b/plugins/app-node/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/app-node/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md new file mode 100644 index 0000000000..1a2fd84689 --- /dev/null +++ b/plugins/app-node/CHANGELOG.md @@ -0,0 +1,23 @@ +# @backstage/plugin-app-node + +## 0.1.0 + +### Minor Changes + +- 9fbe95ef6503: Added the `app` plugin node library, initially providing an extension point that can be used to configure a static fallback handler. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + +## 0.1.0-next.0 + +### Minor Changes + +- 9fbe95ef6503: Added the `app` plugin node library, initially providing an extension point that can be used to configure a static fallback handler. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 diff --git a/plugins/app-node/README.md b/plugins/app-node/README.md new file mode 100644 index 0000000000..5c606540c0 --- /dev/null +++ b/plugins/app-node/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-app-node + +Welcome to the Node.js library package for the app plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/app-node/api-report.md b/plugins/app-node/api-report.md new file mode 100644 index 0000000000..504c9d48c1 --- /dev/null +++ b/plugins/app-node/api-report.md @@ -0,0 +1,16 @@ +## API Report File for "@backstage/plugin-app-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { Handler } from 'express'; + +// @public +export interface StaticFallbackHandlerExtensionPoint { + setStaticFallbackHandler(handler: Handler): void; +} + +// @public +export const staticFallbackHandlerExtensionPoint: ExtensionPoint; +``` diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json new file mode 100644 index 0000000000..842086ecee --- /dev/null +++ b/plugins/app-node/package.json @@ -0,0 +1,35 @@ +{ + "name": "@backstage/plugin-app-node", + "description": "Node.js library for the app plugin", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "node-library" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ], + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@types/express": "^4.17.6", + "express": "^4.17.1" + } +} diff --git a/plugins/app-node/src/extensions.ts b/plugins/app-node/src/extensions.ts new file mode 100644 index 0000000000..6a75a5b791 --- /dev/null +++ b/plugins/app-node/src/extensions.ts @@ -0,0 +1,52 @@ +/* + * 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 { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { Handler } from 'express'; + +/** + * The interface for {@link staticFallbackHandlerExtensionPoint}. + * + * @public + */ +export interface StaticFallbackHandlerExtensionPoint { + /** + * Sets the static fallback handler. This can only be done once. + */ + setStaticFallbackHandler(handler: Handler): void; +} + +/** + * An extension point the exposes the ability to configure a static fallback handler for the app backend. + * + * The static fallback handler is a request handler to handle requests for static content that is not + * present in the app bundle. + * + * This can be used to avoid issues with clients on older deployment versions trying to access lazy + * loaded content that is no longer present. Typically the requests would fall back to a long-term + * object store where all recently deployed versions of the app are present. + * + * Another option is to provide a `database` that will take care of storing the static assets instead. + * + * If both `database` and `staticFallbackHandler` are provided, the `database` will attempt to serve + * static assets first, and if they are not found, the `staticFallbackHandler` will be called. + * + * @public + */ +export const staticFallbackHandlerExtensionPoint = + createExtensionPoint({ + id: 'app.staticFallbackHandler', + }); diff --git a/plugins/app-node/src/index.ts b/plugins/app-node/src/index.ts new file mode 100644 index 0000000000..c5189fa8b9 --- /dev/null +++ b/plugins/app-node/src/index.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Node.js library for the app plugin. + * + * @packageDocumentation + */ + +export { + staticFallbackHandlerExtensionPoint, + type StaticFallbackHandlerExtensionPoint, +} from './extensions'; diff --git a/plugins/app-node/src/setupTests.ts b/plugins/app-node/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/app-node/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export {}; diff --git a/plugins/auth-backend-module-gcp-iap-provider/.eslintrc.js b/plugins/auth-backend-module-gcp-iap-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-gcp-iap-provider/README.md b/plugins/auth-backend-module-gcp-iap-provider/README.md new file mode 100644 index 0000000000..a5f3ce1d4c --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/README.md @@ -0,0 +1,5 @@ +# Auth Backend Module - GCP IAP Provider + +## Links + +- [The Backstage homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-gcp-iap-provider/api-report.md b/plugins/auth-backend-module-gcp-iap-provider/api-report.md new file mode 100644 index 0000000000..02b77cea36 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/api-report.md @@ -0,0 +1,46 @@ +## API Report File for "@backstage/plugin-auth-backend-module-gcp-iap-provider" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +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) +export const authModuleGcpIapProvider: () => BackendFeature; + +// @public (undocumented) +export const gcpIapAuthenticator: ProxyAuthenticator< + { + jwtHeader: string; + tokenValidator: (token: string) => Promise; + }, + { + iapToken: GcpIapTokenInfo; + } +>; + +// @public +export type GcpIapResult = { + iapToken: GcpIapTokenInfo; +}; + +// @public +export namespace gcpIapSignInResolvers { + const emailMatchingUserEntityAnnotation: SignInResolverFactory< + GcpIapResult, + unknown + >; +} + +// @public +export type GcpIapTokenInfo = { + sub: string; + email: string; + [key: string]: JsonPrimitive; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/auth-backend-module-gcp-iap-provider/config.d.ts b/plugins/auth-backend-module-gcp-iap-provider/config.d.ts new file mode 100644 index 0000000000..945378ada6 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/config.d.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + auth?: { + providers?: { + /** + * Configuration for the Google Cloud Platform Identity-Aware Proxy (IAP) auth provider. + */ + gcpIap?: { + [authEnv: string]: { + /** + * The audience to use when validating incoming JWT tokens. + * See https://backstage.io/docs/auth/google/gcp-iap-auth + */ + audience: string; + + /** + * The name of the header to read the JWT token from, defaults to `'x-goog-iap-jwt-assertion'`. + */ + jwtHeader?: string; + }; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json new file mode 100644 index 0000000000..9c073a6cb8 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -0,0 +1,53 @@ +{ + "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", + "description": "A GCP IAP auth provider module for the Backstage auth backend", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth-backend-module-gcp-iap-provider" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/types": "workspace:^", + "google-auth-library": "^8.0.0" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "express": "^4.18.2", + "msw": "^1.0.0", + "supertest": "^6.1.3" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/authenticator.test.ts b/plugins/auth-backend-module-gcp-iap-provider/src/authenticator.test.ts new file mode 100644 index 0000000000..51efbe1a04 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/authenticator.test.ts @@ -0,0 +1,91 @@ +/* + * 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 { mockServices } from '@backstage/backend-test-utils'; +import { Request } from 'express'; +import { gcpIapAuthenticator } from './authenticator'; + +beforeEach(() => { + jest.clearAllMocks(); +}); +jest.mock('./helpers', () => ({ + createTokenValidator() { + return async () => ({ sub: 's', email: 'e' }); + }, +})); + +describe('GcpIapProvider', () => { + it('should find default JWT header', async () => { + const ctx = await gcpIapAuthenticator.initialize({ + config: mockServices.rootConfig({ data: { audience: 'my-audience' } }), + }); + await expect( + gcpIapAuthenticator.authenticate( + { + req: { + header(name: string) { + return name === 'x-goog-iap-jwt-assertion' + ? 'my-token' + : undefined; + }, + } as Request, + }, + ctx, + ), + ).resolves.toEqual({ result: { iapToken: { sub: 's', email: 'e' } } }); + }); + + it('should find custom JWT header', async () => { + const jwtHeader = 'x-custom-header'; + const ctx = await gcpIapAuthenticator.initialize({ + config: mockServices.rootConfig({ + data: { audience: 'my-audience', jwtHeader }, + }), + }); + await expect( + gcpIapAuthenticator.authenticate( + { + req: { + header(name: string) { + return name === jwtHeader ? 'my-token' : undefined; + }, + } as Request, + }, + ctx, + ), + ).resolves.toEqual({ result: { iapToken: { sub: 's', email: 'e' } } }); + }); + + it('should throw if header is missing', async () => { + const ctx = await gcpIapAuthenticator.initialize({ + config: mockServices.rootConfig({ + data: { audience: 'my-audience' }, + }), + }); + await expect( + gcpIapAuthenticator.authenticate( + { + req: { + header(_name: string) { + return undefined; + }, + } as Request, + }, + ctx, + ), + ).rejects.toThrow('Missing Google IAP header'); + }); +}); diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/authenticator.ts b/plugins/auth-backend-module-gcp-iap-provider/src/authenticator.ts new file mode 100644 index 0000000000..ca104ded86 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/authenticator.ts @@ -0,0 +1,52 @@ +/* + * 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 { AuthenticationError } from '@backstage/errors'; +import { createProxyAuthenticator } from '@backstage/plugin-auth-node'; +import { createTokenValidator } from './helpers'; +import { GcpIapResult } from './types'; + +/** + * The header name used by the IAP. + */ +const DEFAULT_IAP_JWT_HEADER = 'x-goog-iap-jwt-assertion'; + +/** @public */ +export const gcpIapAuthenticator = createProxyAuthenticator({ + defaultProfileTransform: async (result: GcpIapResult) => { + return { profile: { email: result.iapToken.email } }; + }, + async initialize({ config }) { + const audience = config.getString('audience'); + const jwtHeader = + config.getOptionalString('jwtHeader') ?? DEFAULT_IAP_JWT_HEADER; + + const tokenValidator = createTokenValidator(audience); + + return { jwtHeader, tokenValidator }; + }, + async authenticate({ req }, { jwtHeader, tokenValidator }) { + const token = req.header(jwtHeader); + + if (!token || typeof token !== 'string') { + throw new AuthenticationError('Missing Google IAP header'); + } + + const iapToken = await tokenValidator(token); + + return { result: { iapToken } }; + }, +}); diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/helpers.test.ts b/plugins/auth-backend-module-gcp-iap-provider/src/helpers.test.ts new file mode 100644 index 0000000000..4079f8f7d3 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/helpers.test.ts @@ -0,0 +1,124 @@ +/* + * 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 { OAuth2Client } from 'google-auth-library'; +import { createTokenValidator } from './helpers'; + +const mockJwt = 'a.b.c'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +describe('helpers', () => { + describe('createTokenValidator', () => { + it('runs the happy path', async () => { + const mockClient = { + getIapPublicKeys: async () => ({ pubkeys: '' }), + verifySignedJwtWithCertsAsync: async () => ({ + getPayload: () => ({ sub: 's', email: 'e@mail.com' }), + }), + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(mockJwt)).resolves.toMatchObject({ + sub: 's', + email: 'e@mail.com', + }); + }); + + it('throws if listing keys fail', async () => { + const mockClient = { + getIapPublicKeys: async () => { + throw new Error('NOPE'); + }, + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(mockJwt)).rejects.toThrow( + 'Unable to list Google IAP token verification keys, Error: NOPE', + ); + }); + + it('throws if the verifying signature fails', async () => { + const mockClient = { + getIapPublicKeys: async () => ({ pubkeys: '' }), + verifySignedJwtWithCertsAsync: async () => { + throw new Error('NOPE'); + }, + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(mockJwt)).rejects.toThrow( + 'Google IAP token verification failed, Error: NOPE', + ); + }); + + it('rejects empty payload', async () => { + const mockClient = { + getIapPublicKeys: async () => ({ pubkeys: '' }), + verifySignedJwtWithCertsAsync: async () => ({ + getPayload: () => undefined, + }), + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(mockJwt)).rejects.toThrow( + 'Google IAP token verification failed, token had no payload', + ); + }); + + it('rejects payload without subject', async () => { + const mockClient = { + getIapPublicKeys: async () => ({ pubkeys: '' }), + verifySignedJwtWithCertsAsync: async () => ({ + getPayload: () => ({ email: 'e@mail.com' }), + }), + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(mockJwt)).rejects.toThrow( + 'Google IAP token payload is missing subject claim', + ); + }); + + it('rejects payload without email', async () => { + const mockClient = { + getIapPublicKeys: async () => ({ pubkeys: '' }), + verifySignedJwtWithCertsAsync: async () => ({ + getPayload: () => ({ sub: 's' }), + }), + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(mockJwt)).rejects.toThrow( + 'Google IAP token payload is missing email claim', + ); + }); + }); +}); diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/helpers.ts b/plugins/auth-backend-module-gcp-iap-provider/src/helpers.ts new file mode 100644 index 0000000000..54db7117f9 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/helpers.ts @@ -0,0 +1,67 @@ +/* + * 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 { AuthenticationError } from '@backstage/errors'; +import { OAuth2Client } from 'google-auth-library'; +import { GcpIapTokenInfo } from './types'; + +export function createTokenValidator( + audience: string, + providedClient?: OAuth2Client, +): (token: string) => Promise { + const client = providedClient ?? new OAuth2Client(); + + return async function tokenValidator(token) { + // TODO(freben): Rate limit the public key reads. It may be sensible to + // cache these for some reasonable time rather than asking for the public + // keys on every single sign-in. But since the rate of events here is so + // slow, I decided to keep it simple for now. + const response = await client.getIapPublicKeys().catch(error => { + throw new AuthenticationError( + `Unable to list Google IAP token verification keys, ${error}`, + ); + }); + const ticket = await client + .verifySignedJwtWithCertsAsync(token, response.pubkeys, audience, [ + 'https://cloud.google.com/iap', + ]) + .catch(error => { + throw new AuthenticationError( + `Google IAP token verification failed, ${error}`, + ); + }); + + const payload = ticket.getPayload(); + if (!payload) { + throw new AuthenticationError( + 'Google IAP token verification failed, token had no payload', + ); + } + + if (!payload.sub) { + throw new AuthenticationError( + 'Google IAP token payload is missing subject claim', + ); + } + if (!payload.email) { + throw new AuthenticationError( + 'Google IAP token payload is missing email claim', + ); + } + + return payload as unknown as GcpIapTokenInfo; + }; +} diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/index.ts b/plugins/auth-backend-module-gcp-iap-provider/src/index.ts new file mode 100644 index 0000000000..be6626a815 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export { gcpIapAuthenticator } from './authenticator'; +export { authModuleGcpIapProvider } from './module'; +export { gcpIapSignInResolvers } from './resolvers'; +export { type GcpIapResult, type GcpIapTokenInfo } from './types'; diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/module.ts b/plugins/auth-backend-module-gcp-iap-provider/src/module.ts new file mode 100644 index 0000000000..90771eecb9 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/module.ts @@ -0,0 +1,49 @@ +/* + * 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 { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + commonSignInResolvers, + createProxyAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { gcpIapAuthenticator } from './authenticator'; +import { gcpIapSignInResolvers } from './resolvers'; + +/** @public */ +export const authModuleGcpIapProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'gcpIapProvider', + register(reg) { + reg.registerInit({ + deps: { + providers: authProvidersExtensionPoint, + }, + async init({ providers }) { + providers.registerProvider({ + providerId: 'gcpIap', + factory: createProxyAuthProviderFactory({ + authenticator: gcpIapAuthenticator, + signInResolverFactories: { + ...gcpIapSignInResolvers, + ...commonSignInResolvers, + }, + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts b/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts new file mode 100644 index 0000000000..32ab2b6cc7 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts @@ -0,0 +1,49 @@ +/* + * 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 { + createSignInResolverFactory, + SignInInfo, +} from '@backstage/plugin-auth-node'; +import { GcpIapResult } from './types'; + +/** + * Available sign-in resolvers for the Google auth provider. + * + * @public + */ +export namespace gcpIapSignInResolvers { + /** + * Looks up the user by matching their email to the `google.com/email` annotation. + */ + export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ + create() { + return async (info: SignInInfo, ctx) => { + const email = info.result.iapToken.email; + + if (!email) { + throw new Error('Google IAP sign-in result is missing email'); + } + + return ctx.signInWithCatalogUser({ + annotations: { + 'google.com/email': email, + }, + }); + }; + }, + }); +} diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/types.ts b/plugins/auth-backend-module-gcp-iap-provider/src/types.ts new file mode 100644 index 0000000000..a967a4d579 --- /dev/null +++ b/plugins/auth-backend-module-gcp-iap-provider/src/types.ts @@ -0,0 +1,50 @@ +/* + * 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 { JsonPrimitive } from '@backstage/types'; + +/** + * The data extracted from an IAP token. + * + * @public + */ +export type GcpIapTokenInfo = { + /** + * The unique, stable identifier for the user. + */ + sub: string; + /** + * User email address. + */ + email: string; + /** + * Other fields. + */ + [key: string]: JsonPrimitive; +}; + +/** + * The result of the initial auth challenge. This is the input to the auth + * callbacks. + * + * @public + */ +export type GcpIapResult = { + /** + * The data extracted from the IAP token header. + */ + iapToken: GcpIapTokenInfo; +}; diff --git a/plugins/auth-backend-module-google-provider/.eslintrc.js b/plugins/auth-backend-module-google-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-google-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-google-provider/README.md b/plugins/auth-backend-module-google-provider/README.md new file mode 100644 index 0000000000..e031482f64 --- /dev/null +++ b/plugins/auth-backend-module-google-provider/README.md @@ -0,0 +1,5 @@ +# Auth Backend Module - Google Provider + +## Links + +- [The Backstage homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-google-provider/api-report.md b/plugins/auth-backend-module-google-provider/api-report.md new file mode 100644 index 0000000000..8040c44832 --- /dev/null +++ b/plugins/auth-backend-module-google-provider/api-report.md @@ -0,0 +1,31 @@ +## API Report File for "@backstage/plugin-auth-backend-module-google-provider" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +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'; +import { PassportProfile } from '@backstage/plugin-auth-node'; +import { SignInResolverFactory } from '@backstage/plugin-auth-node'; + +// @public (undocumented) +export const authModuleGoogleProvider: () => BackendFeature; + +// @public (undocumented) +export const googleAuthenticator: OAuthAuthenticator< + PassportOAuthAuthenticatorHelper, + PassportProfile +>; + +// @public +export namespace googleSignInResolvers { + const emailMatchingUserEntityAnnotation: SignInResolverFactory< + OAuthAuthenticatorResult, + unknown + >; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/auth-backend-module-google-provider/config.d.ts b/plugins/auth-backend-module-google-provider/config.d.ts new file mode 100644 index 0000000000..6a1716c5bf --- /dev/null +++ b/plugins/auth-backend-module-google-provider/config.d.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + /** Configuration options for the auth plugin */ + auth?: { + providers?: { + google?: { + [authEnv: string]: { + clientId: string; + /** + * @visibility secret + */ + clientSecret: string; + callbackUrl?: string; + }; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json new file mode 100644 index 0000000000..2104af5124 --- /dev/null +++ b/plugins/auth-backend-module-google-provider/package.json @@ -0,0 +1,52 @@ +{ + "name": "@backstage/plugin-auth-backend-module-google-provider", + "description": "A Google auth provider module for the Backstage auth backend", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth-backend-module-google-provider" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "google-auth-library": "^8.0.0", + "passport-google-oauth20": "^2.0.0" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@types/passport-google-oauth20": "^2.0.3", + "msw": "^1.0.0", + "supertest": "^6.1.3" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/auth-backend-module-google-provider/src/authenticator.ts b/plugins/auth-backend-module-google-provider/src/authenticator.ts new file mode 100644 index 0000000000..a428519fee --- /dev/null +++ b/plugins/auth-backend-module-google-provider/src/authenticator.ts @@ -0,0 +1,86 @@ +/* + * 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 { + PassportOAuthAuthenticatorHelper, + PassportOAuthDoneCallback, + PassportProfile, + createOAuthAuthenticator, +} from '@backstage/plugin-auth-node'; +import { OAuth2Client } from 'google-auth-library'; +import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; + +/** @public */ +export const googleAuthenticator = createOAuthAuthenticator({ + defaultProfileTransform: + PassportOAuthAuthenticatorHelper.defaultProfileTransform, + initialize({ callbackUrl, config }) { + const clientId = config.getString('clientId'); + const clientSecret = config.getString('clientSecret'); + + return PassportOAuthAuthenticatorHelper.from( + new GoogleStrategy( + { + clientID: clientId, + clientSecret: clientSecret, + callbackURL: callbackUrl, + passReqToCallback: false, + }, + ( + accessToken: string, + refreshToken: string, + params: any, + fullProfile: PassportProfile, + done: PassportOAuthDoneCallback, + ) => { + done( + undefined, + { + fullProfile, + params, + accessToken, + }, + { + refreshToken, + }, + ); + }, + ), + ); + }, + + async start(input, helper) { + return helper.start(input, { + accessType: 'offline', + prompt: 'consent', + }); + }, + + async authenticate(input, helper) { + return helper.authenticate(input); + }, + + async refresh(input, helper) { + return helper.refresh(input); + }, + + async logout(input) { + if (input.refreshToken) { + const oauthClient = new OAuth2Client(); + await oauthClient.revokeToken(input.refreshToken); + } + }, +}); diff --git a/plugins/auth-backend-module-google-provider/src/index.ts b/plugins/auth-backend-module-google-provider/src/index.ts new file mode 100644 index 0000000000..9999dd2bc5 --- /dev/null +++ b/plugins/auth-backend-module-google-provider/src/index.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export { googleAuthenticator } from './authenticator'; +export { authModuleGoogleProvider } from './module'; +export { googleSignInResolvers } from './resolvers'; diff --git a/plugins/auth-backend-module-google-provider/src/module.ts b/plugins/auth-backend-module-google-provider/src/module.ts new file mode 100644 index 0000000000..0354f99880 --- /dev/null +++ b/plugins/auth-backend-module-google-provider/src/module.ts @@ -0,0 +1,49 @@ +/* + * 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 { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + commonSignInResolvers, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { googleAuthenticator } from './authenticator'; +import { googleSignInResolvers } from './resolvers'; + +/** @public */ +export const authModuleGoogleProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'googleProvider', + register(reg) { + reg.registerInit({ + deps: { + providers: authProvidersExtensionPoint, + }, + async init({ providers }) { + providers.registerProvider({ + providerId: 'google', + factory: createOAuthProviderFactory({ + authenticator: googleAuthenticator, + signInResolverFactories: { + ...googleSignInResolvers, + ...commonSignInResolvers, + }, + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-google-provider/src/resolvers.ts b/plugins/auth-backend-module-google-provider/src/resolvers.ts new file mode 100644 index 0000000000..b19fc4d49f --- /dev/null +++ b/plugins/auth-backend-module-google-provider/src/resolvers.ts @@ -0,0 +1,53 @@ +/* + * 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 { + createSignInResolverFactory, + OAuthAuthenticatorResult, + PassportProfile, + SignInInfo, +} from '@backstage/plugin-auth-node'; + +/** + * Available sign-in resolvers for the Google auth provider. + * + * @public + */ +export namespace googleSignInResolvers { + /** + * Looks up the user by matching their email to the `google.com/email` annotation. + */ + export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ + create() { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { profile } = info; + + if (!profile.email) { + throw new Error('Google profile contained no email'); + } + + return ctx.signInWithCatalogUser({ + annotations: { + 'google.com/email': profile.email, + }, + }); + }; + }, + }); +} diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 60555ef74f..5504f162e6 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-auth-backend +## 0.18.6 + +### Patch Changes + +- 16452cd007ae: Updated `frameHandler` to return `undefined` when using the redirect flow instead of returning `postMessageReponse` which was causing errors +- 9dad4b0e61bd: Updated config schema to match what was being used in code +- bb70a9c3886a: Add frontend visibility to provider objects in `auth` config. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.18.6-next.2 + +### Patch Changes + +- 16452cd007ae: Updated `frameHandler` to return `undefined` when using the redirect flow instead of returning `postMessageReponse` which was causing errors +- bb70a9c3886a: Add frontend visibility to provider objects in `auth` config. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.18.6-next.1 + +### Patch Changes + +- 9dad4b0e61bd: Updated config schema to match what was being used in code +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.18.6-next.0 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 80db2e0b71..5eb038c436 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -5,98 +5,69 @@ ```ts /// -import { BackstageIdentityResponse } from '@backstage/plugin-auth-node'; +import { AuthProviderConfig as AuthProviderConfig_2 } from '@backstage/plugin-auth-node'; +import { AuthProviderFactory as AuthProviderFactory_2 } from '@backstage/plugin-auth-node'; +import { AuthProviderRouteHandlers as AuthProviderRouteHandlers_2 } from '@backstage/plugin-auth-node'; +import { AuthResolverCatalogUserQuery as AuthResolverCatalogUserQuery_2 } from '@backstage/plugin-auth-node'; +import { AuthResolverContext as AuthResolverContext_2 } from '@backstage/plugin-auth-node'; import { BackstageSignInResult } from '@backstage/plugin-auth-node'; import { CacheService } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; +import { ClientAuthResponse } from '@backstage/plugin-auth-node'; import { Config } from '@backstage/config'; +import { CookieConfigurer as CookieConfigurer_2 } from '@backstage/plugin-auth-node'; +import { decodeOAuthState } from '@backstage/plugin-auth-node'; +import { encodeOAuthState } from '@backstage/plugin-auth-node'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; -import { GetEntitiesRequest } from '@backstage/catalog-client'; +import { GcpIapResult as GcpIapResult_2 } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; +import { GcpIapTokenInfo as GcpIapTokenInfo_2 } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; import { IncomingHttpHeaders } from 'http'; -import { JsonValue } from '@backstage/types'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { OAuthEnvironmentHandler as OAuthEnvironmentHandler_2 } from '@backstage/plugin-auth-node'; +import { OAuthState as OAuthState_2 } from '@backstage/plugin-auth-node'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { prepareBackstageIdentityResponse as prepareBackstageIdentityResponse_2 } from '@backstage/plugin-auth-node'; import { Profile } from 'passport'; +import { ProfileInfo as ProfileInfo_2 } from '@backstage/plugin-auth-node'; +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 { TokenParams as TokenParams_2 } from '@backstage/plugin-auth-node'; import { TokenSet } from 'openid-client'; import { UserEntity } from '@backstage/catalog-model'; import { UserinfoResponse } from 'openid-client'; +import { WebMessageResponse as WebMessageResponse_2 } from '@backstage/plugin-auth-node'; -// @public +// @public @deprecated export type AuthHandler = ( input: TAuthResult, context: AuthResolverContext, ) => Promise; -// @public +// @public @deprecated export type AuthHandlerResult = { profile: ProfileInfo; }; -// @public (undocumented) -export type AuthProviderConfig = { - baseUrl: string; - appUrl: string; - isOriginAllowed: (origin: string) => boolean; - cookieConfigurer?: CookieConfigurer; -}; +// @public @deprecated (undocumented) +export type AuthProviderConfig = AuthProviderConfig_2; -// @public (undocumented) -export type AuthProviderFactory = (options: { - providerId: string; - globalConfig: AuthProviderConfig; - config: Config; - logger: Logger; - resolverContext: AuthResolverContext; -}) => AuthProviderRouteHandlers; +// @public @deprecated (undocumented) +export type AuthProviderFactory = AuthProviderFactory_2; -// @public -export interface AuthProviderRouteHandlers { - frameHandler(req: express.Request, res: express.Response): Promise; - logout?(req: express.Request, res: express.Response): Promise; - refresh?(req: express.Request, res: express.Response): Promise; - start(req: express.Request, res: express.Response): Promise; -} +// @public @deprecated (undocumented) +export type AuthProviderRouteHandlers = AuthProviderRouteHandlers_2; -// @public -export type AuthResolverCatalogUserQuery = - | { - entityRef: - | string - | { - kind?: string; - namespace?: string; - name: string; - }; - } - | { - annotations: Record; - } - | { - filter: Exclude; - }; +// @public @deprecated (undocumented) +export type AuthResolverCatalogUserQuery = AuthResolverCatalogUserQuery_2; -// @public -export type AuthResolverContext = { - issueToken(params: TokenParams): Promise<{ - token: string; - }>; - findCatalogUser(query: AuthResolverCatalogUserQuery): Promise<{ - entity: Entity; - }>; - signInWithCatalogUser( - query: AuthResolverCatalogUserQuery, - ): Promise; -}; +// @public @deprecated (undocumented) +export type AuthResolverContext = AuthResolverContext_2; -// @public (undocumented) -export type AuthResponse = { - providerInfo: ProviderInfo; - profile: ProfileInfo; - backstageIdentity?: BackstageIdentityResponse; -}; +// @public @deprecated (undocumented) +export type AuthResponse = ClientAuthResponse; // @public (undocumented) export type AwsAlbResult = { @@ -151,7 +122,7 @@ export class CatalogIdentityClient { findUser(query: { annotations: Record }): Promise; resolveCatalogMembership(query: { entityRefs: string[]; - logger?: Logger; + logger?: LoggerService; }): Promise; } @@ -191,18 +162,8 @@ export type CloudflareAccessResult = { token: string; }; -// @public -export type CookieConfigurer = (ctx: { - providerId: string; - baseUrl: string; - callbackUrl: string; - appOrigin: string; -}) => { - domain: string; - path: string; - secure: boolean; - sameSite?: 'none' | 'lax' | 'strict'; -}; +// @public @deprecated (undocumented) +export type CookieConfigurer = CookieConfigurer_2; // @public export function createAuthProviderIntegration< @@ -235,23 +196,17 @@ export type EasyAuthResult = { accessToken?: string; }; -// @public (undocumented) -export const encodeState: (state: OAuthState) => string; +// @public @deprecated (undocumented) +export const encodeState: typeof encodeOAuthState; -// @public (undocumented) +// @public @deprecated (undocumented) export const ensuresXRequestedWith: (req: express.Request) => boolean; -// @public -export type GcpIapResult = { - iapToken: GcpIapTokenInfo; -}; +// @public @deprecated +export type GcpIapResult = GcpIapResult_2; -// @public -export type GcpIapTokenInfo = { - sub: string; - email: string; - [key: string]: JsonValue; -}; +// @public @deprecated +export type GcpIapTokenInfo = GcpIapTokenInfo_2; // @public export function getDefaultOwnershipEntityRefs(entity: Entity): string[]; @@ -276,7 +231,7 @@ export type OAuth2ProxyResult = { getHeader(name: string): string | undefined; }; -// @public (undocumented) +// @public @deprecated (undocumented) export class OAuthAdapter implements AuthProviderRouteHandlers { constructor(handlers: OAuthHandlers, options: OAuthAdapterOptions); // (undocumented) @@ -298,7 +253,7 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { start(req: express.Request, res: express.Response): Promise; } -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthAdapterOptions = { providerId: string; persistScopes?: boolean; @@ -309,25 +264,10 @@ export type OAuthAdapterOptions = { callbackUrl: string; }; -// @public (undocumented) -export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { - constructor(handlers: Map); - // (undocumented) - frameHandler(req: express.Request, res: express.Response): Promise; - // (undocumented) - logout(req: express.Request, res: express.Response): Promise; - // (undocumented) - static mapConfig( - config: Config, - factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers, - ): OAuthEnvironmentHandler; - // (undocumented) - refresh(req: express.Request, res: express.Response): Promise; - // (undocumented) - start(req: express.Request, res: express.Response): Promise; -} +// @public @deprecated (undocumented) +export const OAuthEnvironmentHandler: typeof OAuthEnvironmentHandler_2; -// @public +// @public @deprecated (undocumented) export interface OAuthHandlers { handler(req: express.Request): Promise<{ response: OAuthResponse; @@ -341,12 +281,12 @@ export interface OAuthHandlers { start(req: OAuthStartRequest): Promise; } -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthLogoutRequest = express.Request<{}> & { refreshToken: string; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthProviderInfo = { accessToken: string; idToken?: string; @@ -354,59 +294,53 @@ export type OAuthProviderInfo = { scope: string; }; -// @public +// @public @deprecated export type OAuthProviderOptions = { clientId: string; clientSecret: string; callbackUrl: string; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthRefreshRequest = express.Request<{}> & { scope: string; refreshToken: string; }; -// @public +// @public @deprecated (undocumented) export type OAuthResponse = { profile: ProfileInfo; providerInfo: OAuthProviderInfo; backstageIdentity?: BackstageSignInResult; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthResult = { fullProfile: Profile; params: { id_token?: string; scope: string; + token_type?: string; expires_in: number; }; accessToken: string; refreshToken?: string; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthStartRequest = express.Request<{}> & { scope: string; state: OAuthState; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type OAuthStartResponse = { url: string; status?: number; }; -// @public (undocumented) -export type OAuthState = { - nonce: string; - env: string; - origin?: string; - scope?: string; - redirectUrl?: string; - flow?: string; -}; +// @public @deprecated (undocumented) +export type OAuthState = OAuthState_2; // @public export type OidcAuthResult = { @@ -414,24 +348,18 @@ export type OidcAuthResult = { userinfo: UserinfoResponse; }; -// @public (undocumented) +// @public @deprecated (undocumented) export const postMessageResponse: ( res: express.Response, appOrigin: string, response: WebMessageResponse, ) => void; -// @public -export function prepareBackstageIdentityResponse( - result: BackstageSignInResult, -): BackstageIdentityResponse; +// @public @deprecated (undocumented) +export const prepareBackstageIdentityResponse: typeof prepareBackstageIdentityResponse_2; -// @public -export type ProfileInfo = { - email?: string; - displayName?: string; - picture?: string; -}; +// @public @deprecated (undocumented) +export type ProfileInfo = ProfileInfo_2; // @public (undocumented) export type ProviderFactories = { @@ -452,7 +380,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; auth0: Readonly<{ @@ -467,7 +395,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; awsAlb: Readonly<{ @@ -480,7 +408,7 @@ export const providers: Readonly<{ }; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; bitbucket: Readonly<{ @@ -495,7 +423,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ usernameMatchingUserEntityAnnotation(): SignInResolver; userIdMatchingUserEntityAnnotation(): SignInResolver; @@ -513,7 +441,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ emailMatchingUserEntityProfileEmail: () => SignInResolver; }>; @@ -525,18 +453,18 @@ export const providers: Readonly<{ resolver: SignInResolver; }; cache?: CacheService | undefined; - }) => AuthProviderFactory; + }) => AuthProviderFactory_2; resolvers: Readonly<{ emailMatchingUserEntityProfileEmail: () => SignInResolver; }>; }>; gcpIap: Readonly<{ create: (options: { - authHandler?: AuthHandler | undefined; + authHandler?: AuthHandler | undefined; signIn: { - resolver: SignInResolver; + resolver: SignInResolver; }; - }) => AuthProviderFactory; + }) => AuthProviderFactory_2; resolvers: never; }>; github: Readonly<{ @@ -552,7 +480,7 @@ export const providers: Readonly<{ stateEncoder?: StateEncoder | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ usernameMatchingUserEntityName: () => SignInResolver; }>; @@ -569,7 +497,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; google: Readonly<{ @@ -584,11 +512,11 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ - emailLocalPartMatchingUserEntityName: () => SignInResolver; - emailMatchingUserEntityProfileEmail: () => SignInResolver; - emailMatchingUserEntityAnnotation(): SignInResolver; + emailMatchingUserEntityProfileEmail: () => SignInResolver_2; + emailLocalPartMatchingUserEntityName: () => SignInResolver_2; + emailMatchingUserEntityAnnotation: () => SignInResolver_2; }>; }>; microsoft: Readonly<{ @@ -603,7 +531,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ emailLocalPartMatchingUserEntityName: () => SignInResolver; emailMatchingUserEntityProfileEmail: () => SignInResolver; @@ -622,7 +550,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; oauth2Proxy: Readonly<{ @@ -631,7 +559,7 @@ export const providers: Readonly<{ signIn: { resolver: SignInResolver>; }; - }) => AuthProviderFactory; + }) => AuthProviderFactory_2; resolvers: never; }>; oidc: Readonly<{ @@ -646,7 +574,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ emailLocalPartMatchingUserEntityName: () => SignInResolver; emailMatchingUserEntityProfileEmail: () => SignInResolver; @@ -664,7 +592,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ emailLocalPartMatchingUserEntityName: () => SignInResolver; emailMatchingUserEntityProfileEmail: () => SignInResolver; @@ -683,7 +611,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; saml: Readonly<{ @@ -698,7 +626,7 @@ export const providers: Readonly<{ | undefined; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: Readonly<{ nameIdMatchingUserEntityName(): SignInResolver; }>; @@ -713,13 +641,13 @@ export const providers: Readonly<{ }; } | undefined, - ) => AuthProviderFactory; + ) => AuthProviderFactory_2; resolvers: never; }>; }>; -// @public (undocumented) -export const readState: (stateString: string) => OAuthState; +// @public @deprecated (undocumented) +export const readState: typeof decodeOAuthState; // @public (undocumented) export interface RouterOptions { @@ -732,7 +660,7 @@ export interface RouterOptions { // (undocumented) discovery: PluginEndpointDiscovery; // (undocumented) - logger: Logger; + logger: LoggerService; // (undocumented) providerFactories?: ProviderFactories; // (undocumented) @@ -746,42 +674,23 @@ export type SamlAuthResult = { fullProfile: any; }; -// @public -export type SignInInfo = { - profile: ProfileInfo; - result: TAuthResult; -}; +// @public @deprecated (undocumented) +export type SignInInfo = SignInInfo_2; -// @public -export type SignInResolver = ( - info: SignInInfo, - context: AuthResolverContext, -) => Promise; +// @public @deprecated (undocumented) +export type SignInResolver = SignInResolver_2; -// @public (undocumented) +// @public @deprecated (undocumented) export type StateEncoder = (req: OAuthStartRequest) => Promise<{ encodedState: string; }>; -// @public -export type TokenParams = { - claims: { - sub: string; - ent?: string[]; - } & Record; -}; +// @public @deprecated (undocumented) +export type TokenParams = TokenParams_2; -// @public (undocumented) +// @public @deprecated (undocumented) export const verifyNonce: (req: express.Request, providerId: string) => void; -// @public -export type WebMessageResponse = - | { - type: 'authorization_response'; - response: AuthResponse; - } - | { - type: 'authorization_response'; - error: Error; - }; +// @public @deprecated (undocumented) +export type WebMessageResponse = WebMessageResponse_2; ``` diff --git a/plugins/auth-backend/architecture.drawio.svg b/plugins/auth-backend/architecture.drawio.svg new file mode 100644 index 0000000000..d709935e3e --- /dev/null +++ b/plugins/auth-backend/architecture.drawio.svg @@ -0,0 +1,262 @@ + + + + + + + +
+
+
+ PassportStrategy +
+
+
+
+ + PassportStrategy + +
+
+ + + + + + + + +
+
+
+ OAuthAuthenticator +
+
+
+
+ + OAuthAuthenticator + +
+
+ + + + + + + + + + + + +
+
+
+ ProxyProviderFactory +
+
+
+
+ + ProxyProviderFactory + +
+
+ + + + + + + + + + + + + + + + +
+
+
+ OAuthProviderFactory +
+
+
+
+ + OAuthProviderFactory + +
+
+ + + + +
+
+
+ ProfileTransform +
+
+
+
+ + ProfileTransform + +
+
+ + + + +
+
+
+ ProxyAuthenticator +
+
+
+
+ + ProxyAuthenticator + +
+
+ + + + +
+
+
+ AccessCheck +
+
+
+
+ + AccessCheck + +
+
+ + + + +
+
+
+ SignInResolver +
+
+
+
+ + SignInResolver + +
+
+ + + + + + +
+
+
+ authModule*Provider +
+
+
+
+ + authModule*Provider + +
+
+ + + + +
+
+
+ OAuthEnvironmentHandler +
+
+
+
+ + OAuthEnvironmentHandler + +
+
+ + + + +
+
+
+ OAuthAdapter +
+
+
+
+ + OAuthAdapter + +
+
+ + + + +
+
+
+ PassportOAuthAuthenticatorHelper +
+
+
+
+ + PassportOAuthAuthenticatorHelper + +
+
+ + + + + + +
+
+
+ authModule*Provider +
+
+
+
+ + authModule*Provider + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index d059f2ec04..4ebf7c9de1 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -62,6 +62,7 @@ export interface Config { * @additionalProperties true */ providers?: { + /** @visibility frontend */ google?: { [authEnv: string]: { clientId: string; @@ -72,6 +73,7 @@ export interface Config { callbackUrl?: string; }; }; + /** @visibility frontend */ github?: { [authEnv: string]: { clientId: string; @@ -83,6 +85,7 @@ export interface Config { enterpriseInstanceUrl?: string; }; }; + /** @visibility frontend */ gitlab?: { [authEnv: string]: { clientId: string; @@ -94,6 +97,7 @@ export interface Config { callbackUrl?: string; }; }; + /** @visibility frontend */ saml?: { entryPoint: string; logoutUrl?: string; @@ -117,6 +121,7 @@ export interface Config { digestAlgorithm?: string; acceptedClockSkewMs?: number; }; + /** @visibility frontend */ okta?: { [authEnv: string]: { clientId: string; @@ -130,6 +135,7 @@ export interface Config { callbackUrl?: string; }; }; + /** @visibility frontend */ oauth2?: { [authEnv: string]: { clientId: string; @@ -143,6 +149,7 @@ export interface Config { disableRefresh?: boolean; }; }; + /** @visibility frontend */ oidc?: { [authEnv: string]: { clientId: string; @@ -158,6 +165,7 @@ export interface Config { prompt?: string; }; }; + /** @visibility frontend */ auth0?: { [authEnv: string]: { clientId: string; @@ -172,6 +180,7 @@ export interface Config { connectionScope?: string; }; }; + /** @visibility frontend */ microsoft?: { [authEnv: string]: { clientId: string; @@ -183,6 +192,7 @@ export interface Config { callbackUrl?: string; }; }; + /** @visibility frontend */ onelogin?: { [authEnv: string]: { clientId: string; @@ -194,10 +204,12 @@ export interface Config { callbackUrl?: string; }; }; + /** @visibility frontend */ awsalb?: { iss?: string; region: string; }; + /** @visibility frontend */ cfaccess?: { teamName: string; }; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 4e7f80afaf..c8d1633a47 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.18.6-next.0", + "version": "0.18.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,13 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-backend-module-gcp-iap-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-google-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/types": "workspace:^", "@davidzemon/passport-okta-oauth": "^0.0.5", diff --git a/plugins/auth-backend/src/identity/FirestoreKeyStore.ts b/plugins/auth-backend/src/identity/FirestoreKeyStore.ts index a5479a6470..b5f4f5dcfb 100644 --- a/plugins/auth-backend/src/identity/FirestoreKeyStore.ts +++ b/plugins/auth-backend/src/identity/FirestoreKeyStore.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { DocumentData, Firestore, @@ -57,7 +57,7 @@ export class FirestoreKeyStore implements KeyStore { static async verifyConnection( keyStore: FirestoreKeyStore, - logger?: Logger, + logger?: LoggerService, ): Promise { try { await keyStore.verify(); diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts index 4cd8f91842..cc35c0d84e 100644 --- a/plugins/auth-backend/src/identity/KeyStores.ts +++ b/plugins/auth-backend/src/identity/KeyStores.ts @@ -15,7 +15,7 @@ */ import { pickBy } from 'lodash'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; @@ -26,7 +26,7 @@ import { MemoryKeyStore } from './MemoryKeyStore'; import { KeyStore } from './types'; type Options = { - logger: Logger; + logger: LoggerService; database: AuthDatabase; }; diff --git a/plugins/auth-backend/src/identity/TokenFactory.ts b/plugins/auth-backend/src/identity/TokenFactory.ts index f80b79b6bc..ca16f8821f 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.ts @@ -18,14 +18,14 @@ import { AuthenticationError } from '@backstage/errors'; import { exportJWK, generateKeyPair, importJWK, JWK, SignJWT } from 'jose'; import { DateTime } from 'luxon'; import { v4 as uuid } from 'uuid'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { AnyJWK, KeyStore, TokenIssuer, TokenParams } from './types'; const MS_IN_S = 1000; type Options = { - logger: Logger; + logger: LoggerService; /** Value of the issuer claim in issued tokens */ issuer: string; /** Key store used for storing signing keys */ @@ -57,7 +57,7 @@ type Options = { */ export class TokenFactory implements TokenIssuer { private readonly issuer: string; - private readonly logger: Logger; + private readonly logger: LoggerService; private readonly keyStore: KeyStore; private readonly keyDurationSeconds: number; private readonly algorithm: string; diff --git a/plugins/auth-backend/src/identity/types.ts b/plugins/auth-backend/src/identity/types.ts index e325f74c81..fcfc0345cc 100644 --- a/plugins/auth-backend/src/identity/types.ts +++ b/plugins/auth-backend/src/identity/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { JsonValue } from '@backstage/types'; +import { TokenParams as _TokenParams } from '@backstage/plugin-auth-node'; /** Represents any form of serializable JWK */ export interface AnyJWK extends Record { @@ -25,26 +25,10 @@ export interface AnyJWK extends Record { } /** - * Parameters used to issue new ID Tokens - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type TokenParams = { - /** - * The claims that will be embedded within the token. At a minimum, this should include - * the subject claim, `sub`. It is common to also list entity ownership relations in the - * `ent` list. Additional claims may also be added at the developer's discretion except - * for the following list, which will be overwritten by the TokenIssuer: `iss`, `aud`, - * `iat`, and `exp`. The Backstage team also maintains the right add new claims in the future - * without listing the change as a "breaking change". - */ - claims: { - /** The token subject, i.e. User ID */ - sub: string; - /** A list of entity references that the user claims ownership through */ - ent?: string[]; - } & Record; -}; +export type TokenParams = _TokenParams; /** * A TokenIssuer is able to issue verifiable ID Tokens on demand. diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts index 494e8b4d38..ff983e27a5 100644 --- a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { ConflictError, NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; import { @@ -78,7 +78,7 @@ export class CatalogIdentityClient { */ async resolveCatalogMembership(query: { entityRefs: string[]; - logger?: Logger; + logger?: LoggerService; }): Promise { const { entityRefs, logger } = query; const resolvedEntityRefs = entityRefs diff --git a/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts b/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts index a0c437815d..a61d51b27b 100644 --- a/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts +++ b/plugins/auth-backend/src/lib/flow/authFlowHelpers.test.ts @@ -125,7 +125,7 @@ describe('oauth helpers', () => { postMessageResponse(mockResponse, appOrigin, data); expect(responseBody.match(/.postMessage\(/g)).toHaveLength(2); expect( - responseBody.match(/.postMessage\([a-zA-z.()]*, \'\*\'\)/g), + responseBody.match(/.postMessage\([a-zA-Z.()]*, \'\*\'\)/g), ).toHaveLength(1); const errData: WebMessageResponse = { @@ -135,7 +135,7 @@ describe('oauth helpers', () => { postMessageResponse(mockResponse, appOrigin, errData); expect(responseBody.match(/.postMessage\(/g)).toHaveLength(2); expect( - responseBody.match(/.postMessage\([a-zA-z.()]*, \'\*\'\)/g), + responseBody.match(/.postMessage\([a-zA-Z.()]*, \'\*\'\)/g), ).toHaveLength(1); }); diff --git a/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts b/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts index 94ac4fba15..2f1770a3d4 100644 --- a/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts +++ b/plugins/auth-backend/src/lib/flow/authFlowHelpers.ts @@ -24,7 +24,10 @@ export const safelyEncodeURIComponent = (value: string) => { return encodeURIComponent(value).replace(/'/g, '%27'); }; -/** @public */ +/** + * @public + * @deprecated Use `sendWebMessageResponse` from `@backstage/plugin-auth-node` instead + */ export const postMessageResponse = ( res: express.Response, appOrigin: string, @@ -69,7 +72,10 @@ export const postMessageResponse = ( res.end(``); }; -/** @public */ +/** + * @public + * @deprecated Use inline logic to check that the `X-Requested-With` header is set to `'XMLHttpRequest'` instead. + */ export const ensuresXRequestedWith = (req: express.Request) => { const requiredHeader = req.header('X-Requested-With'); if (!requiredHeader || requiredHeader !== 'XMLHttpRequest') { diff --git a/plugins/auth-backend/src/lib/flow/types.ts b/plugins/auth-backend/src/lib/flow/types.ts index f67198a1b0..36f3033b21 100644 --- a/plugins/auth-backend/src/lib/flow/types.ts +++ b/plugins/auth-backend/src/lib/flow/types.ts @@ -14,20 +14,10 @@ * limitations under the License. */ -import { AuthResponse } from '../../providers/types'; +import { WebMessageResponse as _WebMessageResponse } from '@backstage/plugin-auth-node'; /** - * Payload sent as a post message after the auth request is complete. - * If successful then has a valid payload with Auth information else contains an error. - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type WebMessageResponse = - | { - type: 'authorization_response'; - response: AuthResponse; - } - | { - type: 'authorization_response'; - error: Error; - }; +export type WebMessageResponse = _WebMessageResponse; diff --git a/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthHandler.test.ts b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthHandler.test.ts new file mode 100644 index 0000000000..5c5de6a890 --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthHandler.test.ts @@ -0,0 +1,61 @@ +/* + * 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 { AuthResolverContext } from '@backstage/plugin-auth-node'; +import { AuthHandler } from '../../providers'; +import { OAuthResult } from '../oauth'; +import { PassportProfile } from '../passport/types'; +import { adaptLegacyOAuthHandler } from './adaptLegacyOAuthHandler'; + +describe('adaptLegacyOAuthHandler', () => { + it('should pass through undefined', () => { + expect(adaptLegacyOAuthHandler(undefined)).toBeUndefined(); + }); + + it('should convert an old auth handler to a new profile transform', () => { + const authHandler: AuthHandler = jest.fn(); + const profileTransform = adaptLegacyOAuthHandler(authHandler); + + profileTransform?.( + { + fullProfile: { id: 'id' } as PassportProfile, + session: { + accessToken: 'token', + expiresInSeconds: 3, + scope: 'sco pe', + tokenType: 'bear', + idToken: 'id-token', + refreshToken: 'refresh-token', + }, + }, + { ctx: 'ctx' } as unknown as AuthResolverContext, + ); + + expect(authHandler).toHaveBeenCalledWith( + { + fullProfile: { id: 'id' }, + accessToken: 'token', + params: { + scope: 'sco pe', + id_token: 'id-token', + expires_in: 3, + token_type: 'bear', + }, + }, + { ctx: 'ctx' }, + ); + }); +}); diff --git a/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthHandler.ts b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthHandler.ts new file mode 100644 index 0000000000..37f10ff184 --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthHandler.ts @@ -0,0 +1,46 @@ +/* + * 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 { + OAuthAuthenticatorResult, + ProfileTransform, +} from '@backstage/plugin-auth-node'; +import { AuthHandler } from '../../providers'; +import { OAuthResult } from '../oauth'; +import { PassportProfile } from '../passport/types'; + +/** @internal */ +export function adaptLegacyOAuthHandler( + authHandler?: AuthHandler, +): ProfileTransform> | undefined { + return ( + authHandler && + (async (result, ctx) => + authHandler( + { + fullProfile: result.fullProfile, + accessToken: result.session.accessToken, + params: { + scope: result.session.scope, + id_token: result.session.idToken, + token_type: result.session.tokenType, + expires_in: result.session.expiresInSeconds, + }, + }, + ctx, + )) + ); +} diff --git a/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthSignInResolver.test.ts b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthSignInResolver.test.ts new file mode 100644 index 0000000000..749cf96cbb --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthSignInResolver.test.ts @@ -0,0 +1,69 @@ +/* + * 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 { + AuthResolverContext, + PassportProfile, +} from '@backstage/plugin-auth-node'; +import { adaptLegacyOAuthSignInResolver } from './adaptLegacyOAuthSignInResolver'; + +describe('adaptLegacyOAuthSignInResolver', () => { + it('should pass through undefined', () => { + expect(adaptLegacyOAuthSignInResolver(undefined)).toBeUndefined(); + }); + + it('should convert a legacy resolver to a new one', () => { + const legacyResolver = jest.fn(); + + const newResolver = adaptLegacyOAuthSignInResolver(legacyResolver); + + newResolver?.( + { + profile: { email: 'em@i.l' }, + result: { + fullProfile: { id: 'id' } as PassportProfile, + session: { + accessToken: 'token', + expiresInSeconds: 3, + scope: 'sco pe', + tokenType: 'bear', + idToken: 'id-token', + refreshToken: 'refresh-token', + }, + }, + }, + { ctx: 'ctx' } as unknown as AuthResolverContext, + ); + + expect(legacyResolver).toHaveBeenCalledWith( + { + profile: { email: 'em@i.l' }, + result: { + fullProfile: { id: 'id' }, + accessToken: 'token', + refreshToken: 'refresh-token', + params: { + scope: 'sco pe', + id_token: 'id-token', + expires_in: 3, + token_type: 'bear', + }, + }, + }, + { ctx: 'ctx' }, + ); + }); +}); diff --git a/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthSignInResolver.ts b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthSignInResolver.ts new file mode 100644 index 0000000000..e0318464ed --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/adaptLegacyOAuthSignInResolver.ts @@ -0,0 +1,49 @@ +/* + * 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 { + OAuthAuthenticatorResult, + PassportProfile, + SignInResolver, +} from '@backstage/plugin-auth-node'; +import { OAuthResult } from '../oauth'; + +/** @internal */ +export function adaptLegacyOAuthSignInResolver( + signInResolver?: SignInResolver, +): SignInResolver> | undefined { + return ( + signInResolver && + (async (input, ctx) => + signInResolver( + { + profile: input.profile, + result: { + fullProfile: input.result.fullProfile, + accessToken: input.result.session.accessToken, + refreshToken: input.result.session.refreshToken, + params: { + scope: input.result.session.scope, + id_token: input.result.session.idToken, + token_type: input.result.session.tokenType, + expires_in: input.result.session.expiresInSeconds, + }, + }, + }, + ctx, + )) + ); +} diff --git a/plugins/auth-backend/src/lib/legacy/adaptOAuthSignInResolverToLegacy.test.ts b/plugins/auth-backend/src/lib/legacy/adaptOAuthSignInResolverToLegacy.test.ts new file mode 100644 index 0000000000..521dcf6395 --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/adaptOAuthSignInResolverToLegacy.test.ts @@ -0,0 +1,85 @@ +/* + * 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 { + AuthResolverContext, + PassportProfile, +} from '@backstage/plugin-auth-node'; +import { adaptOAuthSignInResolverToLegacy } from './adaptOAuthSignInResolverToLegacy'; + +describe('adaptOAuthSignInResolverToLegacy', () => { + it('should pass through an empty object', () => { + const legacyResolvers = adaptOAuthSignInResolverToLegacy({}); + expect(legacyResolvers).toEqual({}); + + // @ts-expect-error + legacyResolvers.missing?.(); + }); + + it('should adapt a collection of sign-in resolvers', () => { + const resolverA = jest.fn(); + const resolverB = jest.fn(); + + const legacyResolvers = adaptOAuthSignInResolverToLegacy({ + resolverA, + resolverB, + }); + + const legacyResolverA = legacyResolvers.resolverA(); + legacyResolverA( + { + profile: { email: 'em@i.l' }, + result: { + fullProfile: { id: 'id' } as PassportProfile, + accessToken: 'token', + refreshToken: 'refresh-token', + params: { + scope: 'sco pe', + id_token: 'id-token', + expires_in: 3, + token_type: 'bear', + }, + }, + }, + { ctx: 'ctx' } as unknown as AuthResolverContext, + ); + + expect(resolverA).toHaveBeenCalledWith( + { + profile: { email: 'em@i.l' }, + result: { + fullProfile: { id: 'id' } as PassportProfile, + session: { + accessToken: 'token', + expiresInSeconds: 3, + scope: 'sco pe', + tokenType: 'bear', + idToken: 'id-token', + refreshToken: 'refresh-token', + }, + }, + }, + { ctx: 'ctx' }, + ); + + expect(resolverB).not.toHaveBeenCalled(); + legacyResolvers.resolverB()( + { profile: {}, result: { params: {} } } as any, + {} as any, + ); + expect(resolverB).toHaveBeenCalled(); + }); +}); diff --git a/plugins/auth-backend/src/lib/legacy/adaptOAuthSignInResolverToLegacy.ts b/plugins/auth-backend/src/lib/legacy/adaptOAuthSignInResolverToLegacy.ts new file mode 100644 index 0000000000..8be6049348 --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/adaptOAuthSignInResolverToLegacy.ts @@ -0,0 +1,55 @@ +/* + * 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 { + OAuthAuthenticatorResult, + PassportProfile, + SignInResolver, +} from '@backstage/plugin-auth-node'; +import { OAuthResult } from '../oauth'; + +/** @internal */ +export function adaptOAuthSignInResolverToLegacy< + TKeys extends string, +>(resolvers: { + [key in TKeys]: SignInResolver>; +}): { [key in TKeys]: () => SignInResolver } { + const legacyResolvers = {} as { + [key in TKeys]: () => SignInResolver; + }; + for (const name of Object.keys(resolvers) as TKeys[]) { + const resolver = resolvers[name]; + legacyResolvers[name] = () => async (input, ctx) => + resolver( + { + profile: input.profile, + result: { + fullProfile: input.result.fullProfile, + session: { + accessToken: input.result.accessToken, + expiresInSeconds: input.result.params.expires_in, + scope: input.result.params.scope, + idToken: input.result.params.id_token, + tokenType: input.result.params.token_type ?? 'bearer', + refreshToken: input.result.refreshToken, + }, + }, + }, + ctx, + ); + } + return legacyResolvers; +} diff --git a/plugins/auth-backend/src/lib/legacy/index.ts b/plugins/auth-backend/src/lib/legacy/index.ts new file mode 100644 index 0000000000..8bd8b5c62e --- /dev/null +++ b/plugins/auth-backend/src/lib/legacy/index.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export { adaptLegacyOAuthHandler } from './adaptLegacyOAuthHandler'; +export { adaptLegacyOAuthSignInResolver } from './adaptLegacyOAuthSignInResolver'; +export { adaptOAuthSignInResolverToLegacy } from './adaptOAuthSignInResolverToLegacy'; diff --git a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts index 5f51b2d301..9b4afb4b2b 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthAdapter.ts @@ -50,7 +50,10 @@ import { prepareBackstageIdentityResponse } from '../../providers/prepareBacksta export const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000; export const TEN_MINUTES_MS = 600 * 1000; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export type OAuthAdapterOptions = { providerId: string; persistScopes?: boolean; @@ -61,7 +64,10 @@ export type OAuthAdapterOptions = { callbackUrl: string; }; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export class OAuthAdapter implements AuthProviderRouteHandlers { static fromConfig( config: AuthProviderConfig, @@ -187,6 +193,7 @@ export class OAuthAdapter implements AuthProviderRouteHandlers { ); } res.redirect(state.redirectUrl); + return undefined; } // post message back to popup if successful return postMessageResponse(res, appOrigin, responseObj); diff --git a/plugins/auth-backend/src/lib/oauth/OAuthEnvironmentHandler.ts b/plugins/auth-backend/src/lib/oauth/OAuthEnvironmentHandler.ts index 1c6d45d0a4..c9244eb29e 100644 --- a/plugins/auth-backend/src/lib/oauth/OAuthEnvironmentHandler.ts +++ b/plugins/auth-backend/src/lib/oauth/OAuthEnvironmentHandler.ts @@ -14,84 +14,10 @@ * limitations under the License. */ -import express from 'express'; -import { Config } from '@backstage/config'; -import { InputError, NotFoundError } from '@backstage/errors'; -import { readState } from './helpers'; -import { AuthProviderRouteHandlers } from '../../providers/types'; +import { OAuthEnvironmentHandler as _OAuthEnvironmentHandler } from '@backstage/plugin-auth-node'; -/** @public */ -export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { - static mapConfig( - config: Config, - factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers, - ) { - const envs = config.keys(); - const handlers = new Map(); - - for (const env of envs) { - const envConfig = config.getConfig(env); - const handler = factoryFunc(envConfig); - handlers.set(env, handler); - } - - return new OAuthEnvironmentHandler(handlers); - } - - constructor( - private readonly handlers: Map, - ) {} - - async start(req: express.Request, res: express.Response): Promise { - const provider = this.getProviderForEnv(req); - await provider.start(req, res); - } - - async frameHandler( - req: express.Request, - res: express.Response, - ): Promise { - const provider = this.getProviderForEnv(req); - await provider.frameHandler(req, res); - } - - async refresh(req: express.Request, res: express.Response): Promise { - const provider = this.getProviderForEnv(req); - await provider.refresh?.(req, res); - } - - async logout(req: express.Request, res: express.Response): Promise { - const provider = this.getProviderForEnv(req); - await provider.logout?.(req, res); - } - - private getRequestFromEnv(req: express.Request): string | undefined { - const reqEnv = req.query.env?.toString(); - if (reqEnv) { - return reqEnv; - } - const stateParams = req.query.state?.toString(); - if (!stateParams) { - return undefined; - } - const env = readState(stateParams).env; - return env; - } - - private getProviderForEnv(req: express.Request): AuthProviderRouteHandlers { - const env: string | undefined = this.getRequestFromEnv(req); - - if (!env) { - throw new InputError(`Must specify 'env' query to select environment`); - } - - const handler = this.handlers.get(env); - if (!handler) { - throw new NotFoundError( - `No configuration available for the '${env}' environment of this provider.`, - ); - } - - return handler; - } -} +/** + * @public + * @deprecated import from `@backstage/plugin-auth-node` instead + */ +export const OAuthEnvironmentHandler = _OAuthEnvironmentHandler; diff --git a/plugins/auth-backend/src/lib/oauth/helpers.test.ts b/plugins/auth-backend/src/lib/oauth/helpers.test.ts index 58a3dcab05..c8db34e2cc 100644 --- a/plugins/auth-backend/src/lib/oauth/helpers.test.ts +++ b/plugins/auth-backend/src/lib/oauth/helpers.test.ts @@ -76,7 +76,7 @@ describe('OAuthProvider Utils', () => { } as unknown as express.Request; expect(() => { verifyNonce(mockRequest, 'providera'); - }).toThrow('Invalid state passed via request'); + }).toThrow('OAuth state is invalid, missing env'); }); it('should throw error if nonce mismatch', () => { diff --git a/plugins/auth-backend/src/lib/oauth/helpers.ts b/plugins/auth-backend/src/lib/oauth/helpers.ts index 2b4e50b477..5e67b072e3 100644 --- a/plugins/auth-backend/src/lib/oauth/helpers.ts +++ b/plugins/auth-backend/src/lib/oauth/helpers.ts @@ -16,36 +16,28 @@ import express from 'express'; import { OAuthState } from './types'; -import pickBy from 'lodash/pickBy'; import { CookieConfigurer } from '../../providers/types'; +import { + decodeOAuthState, + encodeOAuthState, +} from '@backstage/plugin-auth-node'; -/** @public */ -export const readState = (stateString: string): OAuthState => { - const state = Object.fromEntries( - new URLSearchParams(Buffer.from(stateString, 'hex').toString('utf-8')), - ); - if ( - !state.nonce || - !state.env || - state.nonce?.length === 0 || - state.env?.length === 0 - ) { - throw Error(`Invalid state passed via request`); - } +/** + * @public + * @deprecated Use `decodeOAuthState` from `@backstage/plugin-auth-node` instead + */ +export const readState = decodeOAuthState; - return state as OAuthState; -}; +/** + * @public + * @deprecated Use `encodeOAuthState` from `@backstage/plugin-auth-node` instead + */ +export const encodeState = encodeOAuthState; -/** @public */ -export const encodeState = (state: OAuthState): string => { - const stateString = new URLSearchParams( - pickBy(state, value => value !== undefined), - ).toString(); - - return Buffer.from(stateString, 'utf-8').toString('hex'); -}; - -/** @public */ +/** + * @public + * @deprecated Use inline logic to make sure the session and state nonce matches instead. + */ export const verifyNonce = (req: express.Request, providerId: string) => { const cookieNonce = req.cookies[`${providerId}-nonce`]; const state: OAuthState = readState(req.query.state?.toString() ?? ''); diff --git a/plugins/auth-backend/src/lib/oauth/types.ts b/plugins/auth-backend/src/lib/oauth/types.ts index e960af2988..b7205c9b85 100644 --- a/plugins/auth-backend/src/lib/oauth/types.ts +++ b/plugins/auth-backend/src/lib/oauth/types.ts @@ -16,13 +16,17 @@ import express from 'express'; import { Profile as PassportProfile } from 'passport'; -import { BackstageSignInResult } from '@backstage/plugin-auth-node'; +import { + BackstageSignInResult, + OAuthState as _OAuthState, +} from '@backstage/plugin-auth-node'; import { OAuthStartResponse, ProfileInfo } from '../../providers/types'; /** * Common options for passport.js-based OAuth providers * * @public + * @deprecated No longer in use */ export type OAuthProviderOptions = { /** @@ -39,12 +43,16 @@ export type OAuthProviderOptions = { callbackUrl: string; }; -/** @public */ +/** + * @public + * @deprecated Use `OAuthAuthenticatorResult` from `@backstage/plugin-auth-node` instead + */ export type OAuthResult = { fullProfile: PassportProfile; params: { id_token?: string; scope: string; + token_type?: string; expires_in: number; }; accessToken: string; @@ -52,9 +60,8 @@ export type OAuthResult = { }; /** - * The expected response from an OAuth flow. - * * @public + * @deprecated Use `ClientAuthResponse` from `@backstage/plugin-auth-node` instead */ export type OAuthResponse = { profile: ProfileInfo; @@ -62,7 +69,10 @@ export type OAuthResponse = { backstageIdentity?: BackstageSignInResult; }; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export type OAuthProviderInfo = { /** * An access token issued for the signed in user. @@ -82,41 +92,41 @@ export type OAuthProviderInfo = { scope: string; }; -/** @public */ -export type OAuthState = { - /* A type for the serialized value in the `state` parameter of the OAuth authorization flow - */ - nonce: string; - env: string; - origin?: string; - scope?: string; - redirectUrl?: string; - flow?: string; -}; +/** + * @public + * @deprecated import from `@backstage/plugin-auth-node` instead + */ +export type OAuthState = _OAuthState; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export type OAuthStartRequest = express.Request<{}> & { scope: string; state: OAuthState; }; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export type OAuthRefreshRequest = express.Request<{}> & { scope: string; refreshToken: string; }; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export type OAuthLogoutRequest = express.Request<{}> & { refreshToken: string; }; /** - * Any OAuth provider needs to implement this interface which has provider specific - * handlers for different methods to perform authentication, get access tokens, - * refresh tokens and perform sign out. - * * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead */ export interface OAuthHandlers { /** diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index cbc2439563..7d22526193 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -24,7 +24,7 @@ import { stringifyEntityRef, } from '@backstage/catalog-model'; import { ConflictError, InputError, NotFoundError } from '@backstage/errors'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { TokenIssuer, TokenParams } from '../../identity/types'; import { AuthResolverContext } from '../../providers'; import { AuthResolverCatalogUserQuery } from '../../providers/types'; @@ -54,7 +54,7 @@ export function getDefaultOwnershipEntityRefs(entity: Entity) { */ export class CatalogAuthResolverContext implements AuthResolverContext { static create(options: { - logger: Logger; + logger: LoggerService; catalogApi: CatalogApi; tokenIssuer: TokenIssuer; tokenManager: TokenManager; @@ -73,7 +73,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { } private constructor( - public readonly logger: Logger, + public readonly logger: LoggerService, public readonly tokenIssuer: TokenIssuer, public readonly catalogIdentityClient: CatalogIdentityClient, private readonly catalogApi: CatalogApi, diff --git a/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts b/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts deleted file mode 100644 index 0162366f61..0000000000 --- a/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts +++ /dev/null @@ -1,134 +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 } from '@backstage/errors'; -import { OAuth2Client } from 'google-auth-library'; -import { createTokenValidator, parseRequestToken } from './helpers'; - -const validJwt = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyIsImlzcyI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.T2BNS4G-6RoiFnXc8Q8TiwdWzTpNitY8jcsGM3N3-Yo'; - -beforeEach(() => { - jest.clearAllMocks(); -}); - -describe('helpers', () => { - describe('createTokenValidator', () => { - it('runs the happy path', async () => { - const mockClient = { - getIapPublicKeys: async () => ({ pubkeys: '' }), - verifySignedJwtWithCertsAsync: async () => ({ - getPayload: () => ({ sub: 's', email: 'e@mail.com' }), - }), - }; - const validator = createTokenValidator( - 'a', - mockClient as unknown as OAuth2Client, - ); - await expect(validator(validJwt)).resolves.toMatchObject({ - sub: 's', - email: 'e@mail.com', - }); - }); - - it('throws if the client throws', async () => { - const mockClient = { - getIapPublicKeys: async () => { - throw new TypeError('bam'); - }, - }; - const validator = createTokenValidator( - 'a', - mockClient as unknown as OAuth2Client, - ); - await expect(validator(validJwt)).rejects.toThrow(TypeError); - }); - - it('rejects empty payload', async () => { - const mockClient = { - getIapPublicKeys: async () => ({ pubkeys: '' }), - verifySignedJwtWithCertsAsync: async () => ({ - getPayload: () => undefined, - }), - }; - const validator = createTokenValidator( - 'a', - mockClient as unknown as OAuth2Client, - ); - await expect(validator(validJwt)).rejects.toMatchObject({ - name: 'TypeError', - message: 'Token had no payload', - }); - }); - }); - - describe('parseRequestToken', () => { - it('runs the happy path', async () => { - await expect( - parseRequestToken( - validJwt, - async () => ({ sub: 's', email: 'e@mail.com' } as any), - ), - ).resolves.toMatchObject({ - iapToken: { - sub: 's', - email: 'e@mail.com', - }, - }); - }); - - it('rejects bad tokens', async () => { - await expect( - parseRequestToken(7, undefined as any), - ).rejects.toMatchObject({ - name: 'AuthenticationError', - message: 'Missing Google IAP header', - }); - await expect( - parseRequestToken(undefined, undefined as any), - ).rejects.toMatchObject({ - name: 'AuthenticationError', - message: 'Missing Google IAP header', - }); - await expect( - parseRequestToken('', undefined as any), - ).rejects.toMatchObject({ - name: 'AuthenticationError', - message: 'Missing Google IAP header', - }); - }); - - it('translates validator errors', async () => { - await expect( - parseRequestToken(validJwt, async () => { - throw new ConflictError('Ouch'); - }), - ).rejects.toMatchObject({ - name: 'AuthenticationError', - message: 'Google IAP token verification failed, ConflictError: Ouch', - }); - }); - - it('rejects bad token payloads', async () => { - await expect( - parseRequestToken(validJwt, async () => ({ sub: 'a' } as any)), - ).rejects.toMatchObject({ - name: 'AuthenticationError', - message: 'Google IAP token payload is missing sub and/or email claim', - }); - }); - }); -}); diff --git a/plugins/auth-backend/src/providers/gcp-iap/helpers.ts b/plugins/auth-backend/src/providers/gcp-iap/helpers.ts deleted file mode 100644 index 26d9a8c904..0000000000 --- a/plugins/auth-backend/src/providers/gcp-iap/helpers.ts +++ /dev/null @@ -1,82 +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 { AuthenticationError } from '@backstage/errors'; -import { OAuth2Client, TokenPayload } from 'google-auth-library'; -import { AuthHandler } from '../types'; -import { GcpIapResult } from './types'; - -export function createTokenValidator( - audience: string, - mockClient?: OAuth2Client, -): (token: string) => Promise { - const client = mockClient ?? new OAuth2Client(); - - return async function tokenValidator(token) { - // TODO(freben): Rate limit the public key reads. It may be sensible to - // cache these for some reasonable time rather than asking for the public - // keys on every single sign-in. But since the rate of events here is so - // slow, I decided to keep it simple for now. - const response = await client.getIapPublicKeys(); - const ticket = await client.verifySignedJwtWithCertsAsync( - token, - response.pubkeys, - audience, - ['https://cloud.google.com/iap'], - ); - - const payload = ticket.getPayload(); - if (!payload) { - throw new TypeError('Token had no payload'); - } - - return payload; - }; -} - -export async function parseRequestToken( - jwtToken: unknown, - tokenValidator: (token: string) => Promise, -): Promise { - if (typeof jwtToken !== 'string' || !jwtToken) { - throw new AuthenticationError('Missing Google IAP header'); - } - - let payload: TokenPayload; - try { - payload = await tokenValidator(jwtToken); - } catch (e) { - throw new AuthenticationError(`Google IAP token verification failed, ${e}`); - } - - if (!payload.sub || !payload.email) { - throw new AuthenticationError( - 'Google IAP token payload is missing sub and/or email claim', - ); - } - - return { - iapToken: { - ...payload, - sub: payload.sub, - email: payload.email, - }, - }; -} - -export const defaultAuthHandler: AuthHandler = async ({ - iapToken, -}) => ({ profile: { email: iapToken.email } }); diff --git a/plugins/auth-backend/src/providers/gcp-iap/provider.test.ts b/plugins/auth-backend/src/providers/gcp-iap/provider.test.ts deleted file mode 100644 index c01db3a853..0000000000 --- a/plugins/auth-backend/src/providers/gcp-iap/provider.test.ts +++ /dev/null @@ -1,75 +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 express from 'express'; -import request from 'supertest'; -import { AuthResolverContext } from '../types'; -import { GcpIapProvider } from './provider'; -import { DEFAULT_IAP_JWT_HEADER } from './types'; - -beforeEach(() => { - jest.clearAllMocks(); -}); - -describe('GcpIapProvider', () => { - const authHandler = jest.fn(); - const signInResolver = jest.fn(); - const tokenValidator = jest.fn(); - - it.each([undefined, 'x-custom-header'])( - 'runs the happy path', - async jwtHeader => { - const provider = new GcpIapProvider({ - authHandler, - signInResolver, - tokenValidator, - resolverContext: {} as AuthResolverContext, - jwtHeader: jwtHeader, - }); - - // { "sub": "user:default/me", "ent": ["group:default/home"] } - const backstageToken = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvbWUiLCJlbnQiOlsiZ3JvdXA6ZGVmYXVsdC9ob21lIl19.CbmAKzFErGmtsnpRxyPc7dHv7WEjb5lY6206YCzR_Rc'; - const iapToken = { sub: 's', email: 'e@mail.com' }; - - authHandler.mockResolvedValueOnce({ email: 'e@mail.com' }); - signInResolver.mockResolvedValueOnce({ token: backstageToken }); - tokenValidator.mockResolvedValueOnce(iapToken); - - const app = express(); - app.use('/refresh', provider.refresh.bind(provider)); - - const header = jwtHeader || DEFAULT_IAP_JWT_HEADER; - const response = await request(app).get('/refresh').set(header, 'token'); - - expect(response.status).toBe(200); - expect(response.get('content-type')).toBe( - 'application/json; charset=utf-8', - ); - expect(response.body).toEqual({ - backstageIdentity: { - token: backstageToken, - identity: { - type: 'user', - userEntityRef: 'user:default/me', - ownershipEntityRefs: ['group:default/home'], - }, - }, - providerInfo: { iapToken }, - }); - }, - ); -}); diff --git a/plugins/auth-backend/src/providers/gcp-iap/provider.ts b/plugins/auth-backend/src/providers/gcp-iap/provider.ts index 7e77853737..db4d9195b2 100644 --- a/plugins/auth-backend/src/providers/gcp-iap/provider.ts +++ b/plugins/auth-backend/src/providers/gcp-iap/provider.ts @@ -14,70 +14,11 @@ * limitations under the License. */ -import express from 'express'; -import { TokenPayload } from 'google-auth-library'; +import { gcpIapAuthenticator } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; +import { createProxyAuthProviderFactory } from '@backstage/plugin-auth-node'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; -import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse'; -import { - AuthHandler, - AuthProviderRouteHandlers, - AuthResolverContext, - SignInResolver, -} from '../types'; -import { - createTokenValidator, - defaultAuthHandler, - parseRequestToken, -} from './helpers'; -import { GcpIapResponse, GcpIapResult, DEFAULT_IAP_JWT_HEADER } from './types'; - -export class GcpIapProvider implements AuthProviderRouteHandlers { - private readonly authHandler: AuthHandler; - private readonly signInResolver: SignInResolver; - private readonly tokenValidator: (token: string) => Promise; - private readonly resolverContext: AuthResolverContext; - private readonly jwtHeader: string; - - constructor(options: { - authHandler: AuthHandler; - signInResolver: SignInResolver; - tokenValidator: (token: string) => Promise; - resolverContext: AuthResolverContext; - jwtHeader?: string; - }) { - this.authHandler = options.authHandler; - this.signInResolver = options.signInResolver; - this.tokenValidator = options.tokenValidator; - this.resolverContext = options.resolverContext; - this.jwtHeader = options?.jwtHeader || DEFAULT_IAP_JWT_HEADER; - } - - async start() {} - - async frameHandler() {} - - async refresh(req: express.Request, res: express.Response): Promise { - const result = await parseRequestToken( - req.header(this.jwtHeader), - this.tokenValidator, - ); - - const { profile } = await this.authHandler(result, this.resolverContext); - - const backstageIdentity = await this.signInResolver( - { profile, result }, - this.resolverContext, - ); - - const response: GcpIapResponse = { - providerInfo: { iapToken: result.iapToken }, - profile, - backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity), - }; - - res.json(response); - } -} +import { AuthHandler, SignInResolver } from '../types'; +import { GcpIapResult } from './types'; /** * Auth provider integration for Google Identity-Aware Proxy auth @@ -104,21 +45,10 @@ export const gcpIap = createAuthProviderIntegration({ resolver: SignInResolver; }; }) { - return ({ config, resolverContext }) => { - const audience = config.getString('audience'); - const jwtHeader = config.getOptionalString('jwtHeader'); - - const authHandler = options.authHandler ?? defaultAuthHandler; - const signInResolver = options.signIn.resolver; - const tokenValidator = createTokenValidator(audience); - - return new GcpIapProvider({ - authHandler, - signInResolver, - tokenValidator, - resolverContext, - jwtHeader, - }); - }; + return createProxyAuthProviderFactory({ + authenticator: gcpIapAuthenticator, + profileTransform: options?.authHandler, + signInResolver: options?.signIn?.resolver, + }); }, }); diff --git a/plugins/auth-backend/src/providers/gcp-iap/types.ts b/plugins/auth-backend/src/providers/gcp-iap/types.ts index 6519de64ac..b1f69318fc 100644 --- a/plugins/auth-backend/src/providers/gcp-iap/types.ts +++ b/plugins/auth-backend/src/providers/gcp-iap/types.ts @@ -14,58 +14,24 @@ * limitations under the License. */ -import { JsonValue } from '@backstage/types'; -import { AuthResponse } from '../types'; - -/** - * The header name used by the IAP. - */ -export const DEFAULT_IAP_JWT_HEADER = 'x-goog-iap-jwt-assertion'; +import { + GcpIapTokenInfo as _GcpIapTokenInfo, + GcpIapResult as _GcpIapResult, +} from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; /** * The data extracted from an IAP token. * * @public + * @deprecated import from `@backstage/plugin-auth-backend-module-gcp-iap-provider` instead */ -export type GcpIapTokenInfo = { - /** - * The unique, stable identifier for the user. - */ - sub: string; - /** - * User email address. - */ - email: string; - /** - * Other fields. - */ - [key: string]: JsonValue; -}; +export type GcpIapTokenInfo = _GcpIapTokenInfo; /** * The result of the initial auth challenge. This is the input to the auth * callbacks. * * @public + * @deprecated import from `@backstage/plugin-auth-backend-module-gcp-iap-provider` instead */ -export type GcpIapResult = { - /** - * The data extracted from the IAP token header. - */ - iapToken: GcpIapTokenInfo; -}; - -/** - * The provider info to return to the frontend. - */ -export type GcpIapProviderInfo = { - /** - * The data extracted from the IAP token header. - */ - iapToken: GcpIapTokenInfo; -}; - -/** - * The shape of the response to return to callers. - */ -export type GcpIapResponse = AuthResponse; +export type GcpIapResult = _GcpIapResult; diff --git a/plugins/auth-backend/src/providers/google/provider.test.ts b/plugins/auth-backend/src/providers/google/provider.test.ts index 3c0b367be2..abde2b66d1 100644 --- a/plugins/auth-backend/src/providers/google/provider.test.ts +++ b/plugins/auth-backend/src/providers/google/provider.test.ts @@ -14,74 +14,57 @@ * limitations under the License. */ -import { GoogleAuthProvider } from './provider'; -import * as helpers from '../../lib/passport/PassportStrategyHelper'; -import { OAuthResult } from '../../lib/oauth'; -import { AuthResolverContext } from '../types'; +import { googleAuthenticator } from '@backstage/plugin-auth-backend-module-google-provider'; +import { createOAuthProviderFactory } from '@backstage/plugin-auth-node'; +import { google } from './provider'; -jest.mock('../../lib/passport/PassportStrategyHelper', () => { - return { - executeFrameHandlerStrategy: jest.fn(), - executeRefreshTokenStrategy: jest.fn(), - executeFetchUserProfileStrategy: jest.fn(), - }; -}); - -const mockFrameHandler = jest.spyOn( - helpers, - 'executeFrameHandlerStrategy', -) as unknown as jest.MockedFunction< - () => Promise<{ result: OAuthResult; privateInfo: any }> ->; +jest.mock('@backstage/plugin-auth-node', () => ({ + ...jest.requireActual('@backstage/plugin-auth-node'), + createOAuthProviderFactory: jest.fn(() => 'provider-factory'), +})); describe('createGoogleProvider', () => { - it('should auth', async () => { - const provider = new GoogleAuthProvider({ - resolverContext: {} as AuthResolverContext, - authHandler: async ({ fullProfile }) => ({ - profile: { - email: fullProfile.emails![0]!.value, - displayName: fullProfile.displayName, - picture: 'http://google.com/lols', - }, - }), - clientId: 'mock', - clientSecret: 'mock', - callbackUrl: 'mock', - }); + afterEach(() => jest.clearAllMocks()); - mockFrameHandler.mockResolvedValueOnce({ - result: { - fullProfile: { - emails: [{ value: 'conrad@example.com' }], - displayName: 'Conrad', - id: 'conrad', - provider: 'google', - }, - params: { - id_token: 'idToken', - scope: 'scope', - expires_in: 123, - }, - accessToken: 'accessToken', - }, - privateInfo: { - refreshToken: 'wacka', - }, + it('should be created', async () => { + expect(google.create()).toBe('provider-factory'); + + expect(createOAuthProviderFactory).toHaveBeenCalledWith({ + authenticator: googleAuthenticator, }); - const { response } = await provider.handler({} as any); - expect(response).toEqual({ - providerInfo: { - accessToken: 'accessToken', - expiresInSeconds: 123, - idToken: 'idToken', - scope: 'scope', - }, - profile: { - email: 'conrad@example.com', - displayName: 'Conrad', - picture: 'http://google.com/lols', - }, + }); + + it('should be created with sign-in resolver', async () => { + expect(google.create({ signIn: { resolver: jest.fn() } })).toBe( + 'provider-factory', + ); + + expect(createOAuthProviderFactory).toHaveBeenCalledWith({ + authenticator: googleAuthenticator, + signInResolver: expect.any(Function), + }); + }); + + it('should be created with sign-in resolver and auth handler', async () => { + expect( + google.create({ + signIn: { resolver: jest.fn() }, + authHandler: jest.fn(), + }), + ).toBe('provider-factory'); + + expect(createOAuthProviderFactory).toHaveBeenCalledWith({ + authenticator: googleAuthenticator, + signInResolver: expect.any(Function), + profileTransform: expect.any(Function), + }); + }); + + it('should have resolvers', () => { + expect(google.resolvers).toEqual({ + emailLocalPartMatchingUserEntityName: expect.any(Function), + emailMatchingUserEntityAnnotation: expect.any(Function), + emailMatchingUserEntityProfileEmail: expect.any(Function), }); }); }); diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index afe5c27f6b..d467977094 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -14,166 +14,22 @@ * limitations under the License. */ -import express from 'express'; -import passport from 'passport'; -import { OAuth2Client } from 'google-auth-library'; -import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; import { - encodeState, - OAuthAdapter, - OAuthEnvironmentHandler, - OAuthHandlers, - OAuthProviderOptions, - OAuthRefreshRequest, - OAuthResponse, - OAuthResult, - OAuthStartRequest, - OAuthLogoutRequest, -} from '../../lib/oauth'; + googleAuthenticator, + googleSignInResolvers, +} from '@backstage/plugin-auth-backend-module-google-provider'; import { - executeFetchUserProfileStrategy, - executeFrameHandlerStrategy, - executeRedirectStrategy, - executeRefreshTokenStrategy, - makeProfileInfo, - PassportDoneCallback, -} from '../../lib/passport'; + commonSignInResolvers, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; import { - AuthHandler, - AuthResolverContext, - OAuthStartResponse, - SignInResolver, -} from '../types'; + adaptLegacyOAuthHandler, + adaptLegacyOAuthSignInResolver, + adaptOAuthSignInResolverToLegacy, +} from '../../lib/legacy'; +import { OAuthResult } from '../../lib/oauth'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; -import { - commonByEmailLocalPartResolver, - commonByEmailResolver, -} from '../resolvers'; - -type PrivateInfo = { - refreshToken: string; -}; - -type Options = OAuthProviderOptions & { - signInResolver?: SignInResolver; - authHandler: AuthHandler; - resolverContext: AuthResolverContext; -}; - -export class GoogleAuthProvider implements OAuthHandlers { - private readonly strategy: GoogleStrategy; - private readonly signInResolver?: SignInResolver; - private readonly authHandler: AuthHandler; - private readonly resolverContext: AuthResolverContext; - - constructor(options: Options) { - this.authHandler = options.authHandler; - this.signInResolver = options.signInResolver; - this.resolverContext = options.resolverContext; - this.strategy = new GoogleStrategy( - { - clientID: options.clientId, - clientSecret: options.clientSecret, - callbackURL: options.callbackUrl, - passReqToCallback: false, - }, - ( - accessToken: any, - refreshToken: any, - params: any, - fullProfile: passport.Profile, - done: PassportDoneCallback, - ) => { - done( - undefined, - { - fullProfile, - params, - accessToken, - refreshToken, - }, - { - refreshToken, - }, - ); - }, - ); - } - - async start(req: OAuthStartRequest): Promise { - return await executeRedirectStrategy(req, this.strategy, { - accessType: 'offline', - prompt: 'consent', - scope: req.scope, - state: encodeState(req.state), - }); - } - - async handler(req: express.Request) { - const { result, privateInfo } = await executeFrameHandlerStrategy< - OAuthResult, - PrivateInfo - >(req, this.strategy); - - return { - response: await this.handleResult(result), - refreshToken: privateInfo.refreshToken, - }; - } - - async logout(req: OAuthLogoutRequest) { - const oauthClient = new OAuth2Client(); - await oauthClient.revokeToken(req.refreshToken); - } - - async refresh(req: OAuthRefreshRequest) { - const { accessToken, refreshToken, params } = - await executeRefreshTokenStrategy( - this.strategy, - req.refreshToken, - req.scope, - ); - const fullProfile = await executeFetchUserProfileStrategy( - this.strategy, - accessToken, - ); - - return { - response: await this.handleResult({ - fullProfile, - params, - accessToken, - }), - refreshToken, - }; - } - - private async handleResult(result: OAuthResult) { - const { profile } = await this.authHandler(result, this.resolverContext); - - const response: OAuthResponse = { - providerInfo: { - idToken: result.params.id_token, - accessToken: result.accessToken, - scope: result.params.scope, - expiresInSeconds: result.params.expires_in, - }, - profile, - }; - - if (this.signInResolver) { - response.backstageIdentity = await this.signInResolver( - { - result, - profile, - }, - this.resolverContext, - ); - } - - return response; - } -} +import { AuthHandler, SignInResolver } from '../types'; /** * Auth provider integration for Google auth @@ -198,62 +54,18 @@ export const google = createAuthProviderIntegration({ resolver: SignInResolver; }; }) { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); - - const provider = new GoogleAuthProvider({ - clientId, - clientSecret, - callbackUrl, - signInResolver: options?.signIn?.resolver, - authHandler, - resolverContext, - }); - - return OAuthAdapter.fromConfig(globalConfig, provider, { - providerId, - callbackUrl, - }); - }); - }, - resolvers: { - /** - * Looks up the user by matching their email local part to the entity name. - */ - emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, - /** - * Looks up the user by matching their email to the entity email. - */ - emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, - /** - * Looks up the user by matching their email to the `google.com/email` annotation. - */ - emailMatchingUserEntityAnnotation(): SignInResolver { - return async (info, ctx) => { - const { profile } = info; - - if (!profile.email) { - throw new Error('Google profile contained no email'); - } - - return ctx.signInWithCatalogUser({ - annotations: { - 'google.com/email': profile.email, - }, - }); - }; - }, + return createOAuthProviderFactory({ + authenticator: googleAuthenticator, + profileTransform: adaptLegacyOAuthHandler(options?.authHandler), + signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), + }); }, + resolvers: adaptOAuthSignInResolverToLegacy({ + emailLocalPartMatchingUserEntityName: + commonSignInResolvers.emailLocalPartMatchingUserEntityName(), + emailMatchingUserEntityProfileEmail: + commonSignInResolvers.emailMatchingUserEntityProfileEmail(), + emailMatchingUserEntityAnnotation: + googleSignInResolvers.emailMatchingUserEntityAnnotation(), + }), }); diff --git a/plugins/auth-backend/src/providers/microsoft/provider.test.ts b/plugins/auth-backend/src/providers/microsoft/provider.test.ts index 6f8df2d4df..a1128ee6b9 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.test.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.test.ts @@ -45,6 +45,9 @@ describe('MicrosoftAuthProvider', () => { }, })({ providerId: 'microsoft', + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, globalConfig: { baseUrl: 'http://backstage.test/api/auth', appUrl: 'http://backstage.test', diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index bc581f6d10..8fc8459f10 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -47,7 +47,7 @@ import { commonByEmailLocalPartResolver, commonByEmailResolver, } from '../resolvers'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import fetch from 'node-fetch'; import { decodeJwt } from 'jose'; import { Profile as PassportProfile } from 'passport'; @@ -60,7 +60,7 @@ type PrivateInfo = { type Options = OAuthProviderOptions & { signInResolver?: SignInResolver; authHandler: AuthHandler; - logger: Logger; + logger: LoggerService; resolverContext: AuthResolverContext; authorizationUrl?: string; tokenUrl?: string; @@ -70,7 +70,7 @@ export class MicrosoftAuthProvider implements OAuthHandlers { private readonly _strategy: MicrosoftStrategy; private readonly signInResolver?: SignInResolver; private readonly authHandler: AuthHandler; - private readonly logger: Logger; + private readonly logger: LoggerService; private readonly resolverContext: AuthResolverContext; constructor(options: Options) { diff --git a/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts b/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts index 0279604f2d..f585d4831f 100644 --- a/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts +++ b/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts @@ -22,7 +22,7 @@ jest.mock('@backstage/catalog-client'); import { AuthenticationError } from '@backstage/errors'; import express from 'express'; import * as jose from 'jose'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { AuthHandler, AuthResolverContext, SignInResolver } from '../types'; import { oauth2Proxy, @@ -36,7 +36,7 @@ describe('Oauth2ProxyAuthProvider', () => { 'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob'; let provider: Oauth2ProxyAuthProvider; - let logger: jest.Mocked; + let logger: jest.Mocked; let signInResolver: jest.MockedFunction< SignInResolver> >; @@ -53,7 +53,7 @@ describe('Oauth2ProxyAuthProvider', () => { >; authHandler = jest.fn(); signInResolver = jest.fn(); - logger = { error: jest.fn() } as unknown as jest.Mocked; + logger = { error: jest.fn() } as unknown as jest.Mocked; mockResponse = { status: jest.fn(), diff --git a/plugins/auth-backend/src/providers/prepareBackstageIdentityResponse.ts b/plugins/auth-backend/src/providers/prepareBackstageIdentityResponse.ts index 761618c225..1fa8f4a2fa 100644 --- a/plugins/auth-backend/src/providers/prepareBackstageIdentityResponse.ts +++ b/plugins/auth-backend/src/providers/prepareBackstageIdentityResponse.ts @@ -14,34 +14,11 @@ * limitations under the License. */ -import { - BackstageIdentityResponse, - BackstageSignInResult, -} from '@backstage/plugin-auth-node'; - -function parseJwtPayload(token: string) { - const [_header, payload, _signature] = token.split('.'); - return JSON.parse(Buffer.from(payload, 'base64').toString()); -} +import { prepareBackstageIdentityResponse as _prepareBackstageIdentityResponse } from '@backstage/plugin-auth-node'; /** - * Parses a Backstage-issued token and decorates the - * {@link @backstage/plugin-auth-node#BackstageIdentityResponse} with identity information sourced from the - * token. - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export function prepareBackstageIdentityResponse( - result: BackstageSignInResult, -): BackstageIdentityResponse { - const { sub, ent } = parseJwtPayload(result.token); - - return { - ...result, - identity: { - type: 'user', - userEntityRef: sub, - ownershipEntityRefs: ent ?? [], - }, - }; -} +export const prepareBackstageIdentityResponse = + _prepareBackstageIdentityResponse; diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 1459cff484..354387153c 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -14,126 +14,42 @@ * limitations under the License. */ -import { GetEntitiesRequest } from '@backstage/catalog-client'; -import { Entity } from '@backstage/catalog-model'; -import { Config } from '@backstage/config'; import { - BackstageIdentityResponse, - BackstageSignInResult, + AuthProviderConfig as _AuthProviderConfig, + AuthProviderRouteHandlers as _AuthProviderRouteHandlers, + AuthProviderFactory as _AuthProviderFactory, + AuthResolverCatalogUserQuery as _AuthResolverCatalogUserQuery, + AuthResolverContext as _AuthResolverContext, + ClientAuthResponse as _ClientAuthResponse, + CookieConfigurer as _CookieConfigurer, + ProfileInfo as _ProfileInfo, + SignInInfo as _SignInInfo, + SignInResolver as _SignInResolver, } from '@backstage/plugin-auth-node'; -import express from 'express'; -import { Logger } from 'winston'; -import { TokenParams } from '../identity/types'; import { OAuthStartRequest } from '../lib/oauth/types'; /** - * A query for a single user in the catalog. - * - * If `entityRef` is used, the default kind is `'User'`. - * - * If `annotations` are used, all annotations must be present and - * match the provided value exactly. Only entities of kind `'User'` will be considered. - * - * If `filter` are used they are passed on as they are to the `CatalogApi`. - * - * Regardless of the query method, the query must match exactly one entity - * in the catalog, or an error will be thrown. - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type AuthResolverCatalogUserQuery = - | { - entityRef: - | string - | { - kind?: string; - namespace?: string; - name: string; - }; - } - | { - annotations: Record; - } - | { - filter: Exclude; - }; +export type AuthResolverCatalogUserQuery = _AuthResolverCatalogUserQuery; /** - * The context that is used for auth processing. - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type AuthResolverContext = { - /** - * Issues a Backstage token using the provided parameters. - */ - issueToken(params: TokenParams): Promise<{ token: string }>; - - /** - * Finds a single user in the catalog using the provided query. - * - * See {@link AuthResolverCatalogUserQuery} for details. - */ - findCatalogUser( - query: AuthResolverCatalogUserQuery, - ): Promise<{ entity: Entity }>; - - /** - * Finds a single user in the catalog using the provided query, and then - * issues an identity for that user using default ownership resolution. - * - * See {@link AuthResolverCatalogUserQuery} for details. - */ - signInWithCatalogUser( - query: AuthResolverCatalogUserQuery, - ): Promise; -}; +export type AuthResolverContext = _AuthResolverContext; /** - * The callback used to resolve the cookie configuration for auth providers that use cookies. * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type CookieConfigurer = (ctx: { - /** ID of the auth provider that this configuration applies to */ - providerId: string; - /** The externally reachable base URL of the auth-backend plugin */ - baseUrl: string; - /** The configured callback URL of the auth provider */ - callbackUrl: string; - /** The origin URL of the app */ - appOrigin: string; -}) => { - domain: string; - path: string; - secure: boolean; - sameSite?: 'none' | 'lax' | 'strict'; -}; +export type CookieConfigurer = _CookieConfigurer; -/** @public */ -export type AuthProviderConfig = { - /** - * The protocol://domain[:port] where the app is hosted. This is used to construct the - * callbackURL to redirect to once the user signs in to the auth provider. - */ - baseUrl: string; - - /** - * The base URL of the app as provided by app.baseUrl - */ - appUrl: string; - - /** - * A function that is called to check whether an origin is allowed to receive the authentication result. - */ - isOriginAllowed: (origin: string) => boolean; - - /** - * The function used to resolve cookie configuration based on the auth provider options. - */ - cookieConfigurer?: CookieConfigurer; -}; - -/** @public */ +/** + * @public + * @deprecated Use `createOAuthAuthenticator` from `@backstage/plugin-auth-node` instead + */ export type OAuthStartResponse = { /** * URL to redirect to @@ -146,138 +62,53 @@ export type OAuthStartResponse = { }; /** - * Any Auth provider needs to implement this interface which handles the routes in the - * auth backend. Any auth API requests from the frontend reaches these methods. - * - * The routes in the auth backend API are tied to these methods like below - * - * `/auth/[provider]/start -> start` - * `/auth/[provider]/handler/frame -> frameHandler` - * `/auth/[provider]/refresh -> refresh` - * `/auth/[provider]/logout -> logout` - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export interface AuthProviderRouteHandlers { - /** - * Handles the start route of the API. This initiates a sign in request with an auth provider. - * - * Request - * - scopes for the auth request (Optional) - * Response - * - redirect to the auth provider for the user to sign in or consent. - * - sets a nonce cookie and also pass the nonce as 'state' query parameter in the redirect request - */ - start(req: express.Request, res: express.Response): Promise; - - /** - * Once the user signs in or consents in the OAuth screen, the auth provider redirects to the - * callbackURL which is handled by this method. - * - * Request - * - to contain a nonce cookie and a 'state' query parameter - * Response - * - postMessage to the window with a payload that contains accessToken, expiryInSeconds?, idToken? and scope. - * - sets a refresh token cookie if the auth provider supports refresh tokens - */ - frameHandler(req: express.Request, res: express.Response): Promise; - - /** - * (Optional) If the auth provider supports refresh tokens then this method handles - * requests to get a new access token. - * - * Request - * - to contain a refresh token cookie and scope (Optional) query parameter. - * Response - * - payload with accessToken, expiryInSeconds?, idToken?, scope and user profile information. - */ - refresh?(req: express.Request, res: express.Response): Promise; - - /** - * (Optional) Handles sign out requests - * - * Response - * - removes the refresh token cookie - */ - logout?(req: express.Request, res: express.Response): Promise; -} - -/** @public */ -export type AuthProviderFactory = (options: { - providerId: string; - globalConfig: AuthProviderConfig; - config: Config; - logger: Logger; - resolverContext: AuthResolverContext; -}) => AuthProviderRouteHandlers; - -/** @public */ -export type AuthResponse = { - providerInfo: ProviderInfo; - profile: ProfileInfo; - backstageIdentity?: BackstageIdentityResponse; -}; +export type AuthProviderConfig = _AuthProviderConfig; /** - * Used to display login information to user, i.e. sidebar popup. - * - * It is also temporarily used as the profile of the signed-in user's Backstage - * identity, but we want to replace that with data from identity and/org catalog - * service - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type ProfileInfo = { - /** - * Email ID of the signed in user. - */ - email?: string; - /** - * Display name that can be presented to the signed in user. - */ - displayName?: string; - /** - * URL to an image that can be used as the display image or avatar of the - * signed in user. - */ - picture?: string; -}; +export type AuthProviderRouteHandlers = _AuthProviderRouteHandlers; /** - * Type of sign in information context. Includes the profile information and - * authentication result which contains auth related information. - * * @public + * @deprecated import from `@backstage/plugin-auth-node` instead */ -export type SignInInfo = { - /** - * The simple profile passed down for use in the frontend. - */ - profile: ProfileInfo; - - /** - * The authentication result that was received from the authentication - * provider. - */ - result: TAuthResult; -}; +export type AuthProviderFactory = _AuthProviderFactory; /** - * Describes the function which handles the result of a successful - * authentication. Must return a valid {@link @backstage/plugin-auth-node#BackstageSignInResult}. - * * @public + * @deprecated import `ClientAuthResponse` from `@backstage/plugin-auth-node` instead */ -export type SignInResolver = ( - info: SignInInfo, - context: AuthResolverContext, -) => Promise; +export type AuthResponse = _ClientAuthResponse; + +/** + * @public + * @deprecated import from `@backstage/plugin-auth-node` instead + */ +export type ProfileInfo = _ProfileInfo; + +/** + * @public + * @deprecated import from `@backstage/plugin-auth-node` instead + */ +export type SignInInfo = _SignInInfo; + +/** + * @public + * @deprecated import from `@backstage/plugin-auth-node` instead + */ +export type SignInResolver = _SignInResolver; /** * The return type of an authentication handler. Must contain valid profile * information. * * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead */ export type AuthHandlerResult = { profile: ProfileInfo }; @@ -293,13 +124,17 @@ export type AuthHandlerResult = { profile: ProfileInfo }; * group of users. * * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead */ export type AuthHandler = ( input: TAuthResult, context: AuthResolverContext, ) => Promise; -/** @public */ +/** + * @public + * @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead + */ export type StateEncoder = ( req: OAuthStartRequest, ) => Promise<{ encodedState: string }>; diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index fa8dd95727..026a6263b7 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -17,7 +17,7 @@ import express from 'express'; import Router from 'express-promise-router'; import cookieParser from 'cookie-parser'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { defaultAuthProviderFactories, AuthProviderFactory, @@ -44,7 +44,7 @@ export type ProviderFactories = { [s: string]: AuthProviderFactory }; /** @public */ export interface RouterOptions { - logger: Logger; + logger: LoggerService; database: PluginDatabaseManager; config: Config; discovery: PluginEndpointDiscovery; @@ -130,6 +130,9 @@ export async function createRouter( try { const provider = providerFactory({ providerId, + appUrl, + baseUrl: authUrl, + isOriginAllowed, globalConfig: { baseUrl: authUrl, appUrl, diff --git a/plugins/auth-backend/src/service/standaloneServer.ts b/plugins/auth-backend/src/service/standaloneServer.ts index 16ebbc9345..abda3a341c 100644 --- a/plugins/auth-backend/src/service/standaloneServer.ts +++ b/plugins/auth-backend/src/service/standaloneServer.ts @@ -23,11 +23,11 @@ import { } from '@backstage/backend-common'; import { Server } from 'http'; import Knex from 'knex'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { createRouter } from './router'; export interface ServerOptions { - logger: Logger; + logger: LoggerService; } export async function startStandaloneServer( diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index cb802b9757..30c133338b 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-auth-node +## 0.2.17 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.17-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.2.17-next.0 ### Patch Changes diff --git a/plugins/auth-node/api-report.md b/plugins/auth-node/api-report.md index bc9e392929..a7fc9b1612 100644 --- a/plugins/auth-node/api-report.md +++ b/plugins/auth-node/api-report.md @@ -3,8 +3,100 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackstageIdentityResponse as BackstageIdentityResponse_2 } from '@backstage/plugin-auth-node'; +import { BackstageSignInResult as BackstageSignInResult_2 } from '@backstage/plugin-auth-node'; +import { Config } from '@backstage/config'; +import { Entity } from '@backstage/catalog-model'; +import { EntityFilterQuery } from '@backstage/catalog-client'; +import express from 'express'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { JsonObject } from '@backstage/types'; +import { JsonValue } from '@backstage/types'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { Profile } from 'passport'; import { Request as Request_2 } from 'express'; +import { Response as Response_2 } from 'express'; +import { Strategy } from 'passport'; +import { ZodSchema } from 'zod'; +import { ZodTypeDef } from 'zod'; + +// @public @deprecated (undocumented) +export type AuthProviderConfig = { + baseUrl: string; + appUrl: string; + isOriginAllowed: (origin: string) => boolean; + cookieConfigurer?: CookieConfigurer; +}; + +// @public (undocumented) +export type AuthProviderFactory = (options: { + providerId: string; + globalConfig: AuthProviderConfig; + config: Config; + logger: LoggerService; + resolverContext: AuthResolverContext; + baseUrl: string; + appUrl: string; + isOriginAllowed: (origin: string) => boolean; + cookieConfigurer?: CookieConfigurer; +}) => AuthProviderRouteHandlers; + +// @public (undocumented) +export interface AuthProviderRegistrationOptions { + // (undocumented) + factory: AuthProviderFactory; + // (undocumented) + providerId: string; +} + +// @public +export interface AuthProviderRouteHandlers { + frameHandler(req: Request_2, res: Response_2): Promise; + logout?(req: Request_2, res: Response_2): Promise; + refresh?(req: Request_2, res: Response_2): Promise; + start(req: Request_2, res: Response_2): Promise; +} + +// @public (undocumented) +export interface AuthProvidersExtensionPoint { + // (undocumented) + registerProvider(options: AuthProviderRegistrationOptions): void; +} + +// @public (undocumented) +export const authProvidersExtensionPoint: ExtensionPoint; + +// @public +export type AuthResolverCatalogUserQuery = + | { + entityRef: + | string + | { + kind?: string; + namespace?: string; + name: string; + }; + } + | { + annotations: Record; + } + | { + filter: EntityFilterQuery; + }; + +// @public +export type AuthResolverContext = { + issueToken(params: TokenParams): Promise<{ + token: string; + }>; + findCatalogUser(query: AuthResolverCatalogUserQuery): Promise<{ + entity: Entity; + }>; + signInWithCatalogUser( + query: AuthResolverCatalogUserQuery, + ): Promise; +}; // @public export interface BackstageIdentityResponse extends BackstageSignInResult { @@ -23,6 +115,99 @@ export type BackstageUserIdentity = { ownershipEntityRefs: string[]; }; +// @public (undocumented) +export type ClientAuthResponse = { + providerInfo: TProviderInfo; + profile: ProfileInfo; + backstageIdentity?: BackstageIdentityResponse; +}; + +// @public +export namespace commonSignInResolvers { + const emailMatchingUserEntityProfileEmail: SignInResolverFactory< + unknown, + unknown + >; + const emailLocalPartMatchingUserEntityName: SignInResolverFactory< + unknown, + unknown + >; +} + +// @public +export type CookieConfigurer = (ctx: { + providerId: string; + baseUrl: string; + callbackUrl: string; + appOrigin: string; +}) => { + domain: string; + path: string; + secure: boolean; + sameSite?: 'none' | 'lax' | 'strict'; +}; + +// @public (undocumented) +export function createOAuthAuthenticator( + authenticator: OAuthAuthenticator, +): OAuthAuthenticator; + +// @public (undocumented) +export function createOAuthProviderFactory(options: { + authenticator: OAuthAuthenticator; + stateTransform?: OAuthStateTransform; + profileTransform?: ProfileTransform>; + signInResolver?: SignInResolver>; + signInResolverFactories?: { + [name in string]: SignInResolverFactory< + OAuthAuthenticatorResult, + unknown + >; + }; +}): AuthProviderFactory; + +// @public (undocumented) +export function createOAuthRouteHandlers( + options: OAuthRouteHandlersOptions, +): AuthProviderRouteHandlers; + +// @public (undocumented) +export function createProxyAuthenticator( + authenticator: ProxyAuthenticator, +): ProxyAuthenticator; + +// @public (undocumented) +export function createProxyAuthProviderFactory(options: { + authenticator: ProxyAuthenticator; + profileTransform?: ProfileTransform; + signInResolver?: SignInResolver; + signInResolverFactories?: Record< + string, + SignInResolverFactory + >; +}): AuthProviderFactory; + +// @public (undocumented) +export function createProxyAuthRouteHandlers( + options: ProxyAuthRouteHandlersOptions, +): AuthProviderRouteHandlers; + +// @public (undocumented) +export function createSignInResolverFactory< + TAuthResult, + TOptionsOutput, + TOptionsInput, +>( + options: SignInResolverFactoryOptions< + TAuthResult, + TOptionsOutput, + TOptionsInput + >, +): SignInResolverFactory; + +// @public (undocumented) +export function decodeOAuthState(encodedState: string): OAuthState; + // @public export class DefaultIdentityClient implements IdentityApi { // @deprecated @@ -34,6 +219,9 @@ export class DefaultIdentityClient implements IdentityApi { ): Promise; } +// @public (undocumented) +export function encodeOAuthState(state: OAuthState): string; + // @public export function getBearerTokenFromAuthorizationHeader( authorizationHeader: unknown, @@ -65,4 +253,391 @@ export type IdentityClientOptions = { issuer?: string; algorithms?: string[]; }; + +// @public (undocumented) +export interface OAuthAuthenticator { + // (undocumented) + authenticate( + input: OAuthAuthenticatorAuthenticateInput, + ctx: TContext, + ): Promise>; + // (undocumented) + defaultProfileTransform: ProfileTransform>; + // (undocumented) + initialize(ctx: { callbackUrl: string; config: Config }): TContext; + // (undocumented) + logout?(input: OAuthAuthenticatorLogoutInput, ctx: TContext): Promise; + // (undocumented) + refresh( + input: OAuthAuthenticatorRefreshInput, + ctx: TContext, + ): Promise>; + // (undocumented) + shouldPersistScopes?: boolean; + // (undocumented) + start( + input: OAuthAuthenticatorStartInput, + ctx: TContext, + ): Promise<{ + url: string; + status?: number; + }>; +} + +// @public (undocumented) +export interface OAuthAuthenticatorAuthenticateInput { + // (undocumented) + req: Request_2; +} + +// @public (undocumented) +export interface OAuthAuthenticatorLogoutInput { + // (undocumented) + accessToken?: string; + // (undocumented) + refreshToken?: string; + // (undocumented) + req: Request_2; +} + +// @public (undocumented) +export interface OAuthAuthenticatorRefreshInput { + // (undocumented) + refreshToken: string; + // (undocumented) + req: Request_2; + // (undocumented) + scope: string; +} + +// @public (undocumented) +export interface OAuthAuthenticatorResult { + // (undocumented) + fullProfile: TProfile; + // (undocumented) + session: OAuthSession; +} + +// @public (undocumented) +export interface OAuthAuthenticatorStartInput { + // (undocumented) + req: Request_2; + // (undocumented) + scope: string; + // (undocumented) + state: string; +} + +// @public (undocumented) +export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { + constructor(handlers: Map); + // (undocumented) + frameHandler(req: express.Request, res: express.Response): Promise; + // (undocumented) + logout(req: express.Request, res: express.Response): Promise; + // (undocumented) + static mapConfig( + config: Config, + factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers, + ): OAuthEnvironmentHandler; + // (undocumented) + refresh(req: express.Request, res: express.Response): Promise; + // (undocumented) + start(req: express.Request, res: express.Response): Promise; +} + +// @public (undocumented) +export interface OAuthRouteHandlersOptions { + // (undocumented) + appUrl: string; + // (undocumented) + authenticator: OAuthAuthenticator; + // (undocumented) + baseUrl: string; + // (undocumented) + config: Config; + // (undocumented) + cookieConfigurer?: CookieConfigurer; + // (undocumented) + isOriginAllowed: (origin: string) => boolean; + // (undocumented) + profileTransform?: ProfileTransform>; + // (undocumented) + providerId: string; + // (undocumented) + resolverContext: AuthResolverContext; + // (undocumented) + signInResolver?: SignInResolver>; + // (undocumented) + stateTransform?: OAuthStateTransform; +} + +// @public (undocumented) +export interface OAuthSession { + // (undocumented) + accessToken: string; + // (undocumented) + expiresInSeconds: number; + // (undocumented) + idToken?: string; + // (undocumented) + refreshToken?: string; + // (undocumented) + scope: string; + // (undocumented) + tokenType: string; +} + +// @public +export type OAuthState = { + nonce: string; + env: string; + origin?: string; + scope?: string; + redirectUrl?: string; + flow?: string; +}; + +// @public (undocumented) +export type OAuthStateTransform = ( + state: OAuthState, + context: { + req: Request_2; + }, +) => Promise<{ + state: OAuthState; +}>; + +// @public (undocumented) +export type PassportDoneCallback = ( + err?: Error, + result?: TResult, + privateInfo?: TPrivateInfo, +) => void; + +// @public (undocumented) +export class PassportHelpers { + // (undocumented) + static executeFetchUserProfileStrategy( + providerStrategy: Strategy, + accessToken: string, + ): Promise; + // (undocumented) + static executeFrameHandlerStrategy( + req: Request_2, + providerStrategy: Strategy, + options?: Record, + ): Promise<{ + result: TResult; + privateInfo: TPrivateInfo; + }>; + // (undocumented) + static executeRedirectStrategy( + req: Request_2, + providerStrategy: Strategy, + options: Record, + ): Promise<{ + url: string; + status?: number; + }>; + // (undocumented) + static executeRefreshTokenStrategy( + providerStrategy: Strategy, + refreshToken: string, + scope: string, + ): Promise<{ + accessToken: string; + refreshToken?: string; + params: any; + }>; + // (undocumented) + static transformProfile: ( + profile: PassportProfile, + idToken?: string, + ) => ProfileInfo; +} + +// @public (undocumented) +export class PassportOAuthAuthenticatorHelper { + // (undocumented) + authenticate( + input: OAuthAuthenticatorAuthenticateInput, + ): Promise>; + // (undocumented) + static defaultProfileTransform: ProfileTransform< + OAuthAuthenticatorResult + >; + // (undocumented) + fetchProfile(accessToken: string): Promise; + // (undocumented) + static from(strategy: Strategy): PassportOAuthAuthenticatorHelper; + // (undocumented) + refresh( + input: OAuthAuthenticatorRefreshInput, + ): Promise>; + // (undocumented) + start( + input: OAuthAuthenticatorStartInput, + options: Record, + ): Promise<{ + url: string; + status?: number; + }>; +} + +// @public (undocumented) +export type PassportOAuthDoneCallback = PassportDoneCallback< + PassportOAuthResult, + PassportOAuthPrivateInfo +>; + +// @public (undocumented) +export type PassportOAuthPrivateInfo = { + refreshToken?: string; +}; + +// @public (undocumented) +export type PassportOAuthResult = { + fullProfile: PassportProfile; + params: { + id_token?: string; + scope: string; + token_type?: string; + expires_in: number; + }; + accessToken: string; +}; + +// @public (undocumented) +export type PassportProfile = Profile & { + avatarUrl?: string; +}; + +// @public +export function prepareBackstageIdentityResponse( + result: BackstageSignInResult_2, +): BackstageIdentityResponse_2; + +// @public +export type ProfileInfo = { + email?: string; + displayName?: string; + picture?: string; +}; + +// @public +export type ProfileTransform = ( + result: TResult, + context: AuthResolverContext, +) => Promise<{ + profile: ProfileInfo; +}>; + +// @public (undocumented) +export interface ProxyAuthenticator { + // (undocumented) + authenticate( + options: { + req: Request_2; + }, + ctx: TContext, + ): Promise<{ + result: TResult; + }>; + // (undocumented) + defaultProfileTransform: ProfileTransform; + // (undocumented) + initialize(ctx: { config: Config }): Promise; +} + +// @public (undocumented) +export interface ProxyAuthRouteHandlersOptions { + // (undocumented) + authenticator: ProxyAuthenticator; + // (undocumented) + config: Config; + // (undocumented) + profileTransform?: ProfileTransform; + // (undocumented) + resolverContext: AuthResolverContext; + // (undocumented) + signInResolver: SignInResolver; +} + +// @public (undocumented) +export function readDeclarativeSignInResolver( + options: ReadDeclarativeSignInResolverOptions, +): SignInResolver | undefined; + +// @public (undocumented) +export interface ReadDeclarativeSignInResolverOptions { + // (undocumented) + config: Config; + // (undocumented) + signInResolverFactories: { + [name in string]: SignInResolverFactory; + }; +} + +// @public (undocumented) +export function sendWebMessageResponse( + res: Response_2, + appOrigin: string, + response: WebMessageResponse, +): void; + +// @public +export type SignInInfo = { + profile: ProfileInfo; + result: TAuthResult; +}; + +// @public +export type SignInResolver = ( + info: SignInInfo, + context: AuthResolverContext, +) => Promise; + +// @public (undocumented) +export interface SignInResolverFactory { + // (undocumented) + ( + ...options: undefined extends TOptions + ? [options?: TOptions] + : [options: TOptions] + ): SignInResolver; + // (undocumented) + optionsJsonSchema?: JsonObject; +} + +// @public (undocumented) +export interface SignInResolverFactoryOptions< + TAuthResult, + TOptionsOutput, + TOptionsInput, +> { + // (undocumented) + create(options: TOptionsOutput): SignInResolver; + // (undocumented) + optionsSchema?: ZodSchema; +} + +// @public +export type TokenParams = { + claims: { + sub: string; + ent?: string[]; + } & Record; +}; + +// @public +export type WebMessageResponse = + | { + type: 'authorization_response'; + response: ClientAuthResponse; + } + | { + type: 'authorization_response'; + error: Error; + }; ``` diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index c5846d60b4..bdbe466769 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.2.17-next.0", + "version": "0.2.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,19 +29,31 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/types": "workspace:^", "@types/express": "*", + "@types/passport": "^1.0.3", "express": "^4.17.1", "jose": "^4.6.0", + "lodash": "^4.17.21", "node-fetch": "^2.6.7", - "winston": "^3.2.1" + "passport": "^0.6.0", + "winston": "^3.2.1", + "zod": "^3.21.4", + "zod-to-json-schema": "^3.21.4" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", + "cookie-parser": "^1.4.6", + "express-promise-router": "^4.1.1", "lodash": "^4.17.21", "msw": "^1.0.0", + "supertest": "^6.1.3", "uuid": "^8.0.0" }, "files": [ diff --git a/plugins/auth-node/src/extensions/AuthProvidersExtensionPoint.ts b/plugins/auth-node/src/extensions/AuthProvidersExtensionPoint.ts new file mode 100644 index 0000000000..b67d5be017 --- /dev/null +++ b/plugins/auth-node/src/extensions/AuthProvidersExtensionPoint.ts @@ -0,0 +1,35 @@ +/* + * 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 { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { AuthProviderFactory } from '../types'; + +/** @public */ +export interface AuthProviderRegistrationOptions { + providerId: string; + factory: AuthProviderFactory; +} + +/** @public */ +export interface AuthProvidersExtensionPoint { + registerProvider(options: AuthProviderRegistrationOptions): void; +} + +/** @public */ +export const authProvidersExtensionPoint = + createExtensionPoint({ + id: 'auth.providers', + }); diff --git a/plugins/auth-node/src/extensions/index.ts b/plugins/auth-node/src/extensions/index.ts new file mode 100644 index 0000000000..43e1751685 --- /dev/null +++ b/plugins/auth-node/src/extensions/index.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export { + authProvidersExtensionPoint, + type AuthProviderRegistrationOptions, + type AuthProvidersExtensionPoint, +} from './AuthProvidersExtensionPoint'; diff --git a/plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts b/plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts new file mode 100644 index 0000000000..eda8469ac6 --- /dev/null +++ b/plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts @@ -0,0 +1,28 @@ +/* + * 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 { WebMessageResponse } from '../sendWebMessageResponse'; + +export function parseWebMessageResponse(text: string): { + response: WebMessageResponse; + origin: string; +} { + const [response, origin] = text.matchAll(/decodeURIComponent\('(.+?)'\)/g); + return { + response: JSON.parse(decodeURIComponent(response[1])), + origin: decodeURIComponent(origin[1]), + }; +} diff --git a/plugins/auth-node/src/flow/index.ts b/plugins/auth-node/src/flow/index.ts new file mode 100644 index 0000000000..51d91e5d58 --- /dev/null +++ b/plugins/auth-node/src/flow/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + sendWebMessageResponse, + type WebMessageResponse, +} from './sendWebMessageResponse'; diff --git a/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts b/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts new file mode 100644 index 0000000000..cc192638f2 --- /dev/null +++ b/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts @@ -0,0 +1,194 @@ +/* + * 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 express from 'express'; +import { + safelyEncodeURIComponent, + sendWebMessageResponse, + type WebMessageResponse, +} from './sendWebMessageResponse'; +import { parseWebMessageResponse } from './__testUtils__/parseWebMessageResponse'; + +describe('oauth helpers', () => { + describe('safelyEncodeURIComponent', () => { + it('encodes all occurrences of single quotes', () => { + expect(safelyEncodeURIComponent("a'ö'b")).toBe('a%27%C3%B6%27b'); + }); + }); + + describe('sendWebMessageResponse', () => { + const appOrigin = 'http://localhost:3000'; + it('should post a message back with payload success', () => { + const mockResponse = { + end: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + const data: WebMessageResponse = { + type: 'authorization_response', + response: { + providerInfo: { + accessToken: 'ACCESS_TOKEN', + idToken: 'ID_TOKEN', + expiresInSeconds: 10, + scope: 'email', + }, + profile: { + email: 'foo@bar.com', + }, + backstageIdentity: { + token: 'a.b.c', + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'a', + }, + }, + }, + }; + const encoded = safelyEncodeURIComponent(JSON.stringify(data)); + + sendWebMessageResponse(mockResponse, appOrigin, data); + expect(mockResponse.setHeader).toHaveBeenCalledTimes(3); + expect(mockResponse.end).toHaveBeenCalledTimes(1); + expect(mockResponse.end).toHaveBeenCalledWith( + expect.stringContaining(encoded), + ); + }); + + it('should post a message back with payload error', () => { + const mockResponse = { + end: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + const data: WebMessageResponse = { + type: 'authorization_response', + error: new Error('Unknown error occurred'), + }; + const encoded = safelyEncodeURIComponent( + JSON.stringify({ + type: 'authorization_response', + error: { name: 'Error', message: 'Unknown error occurred' }, + }), + ); + + sendWebMessageResponse(mockResponse, appOrigin, data); + expect(mockResponse.setHeader).toHaveBeenCalledTimes(3); + expect(mockResponse.end).toHaveBeenCalledTimes(1); + expect(mockResponse.end).toHaveBeenCalledWith( + expect.stringContaining(encoded), + ); + }); + + it('should call postMessage twice but only one of them with target *', () => { + let responseBody = ''; + + const mockResponse = { + end: jest.fn(body => { + responseBody = body; + return this; + }), + setHeader: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + const data: WebMessageResponse = { + type: 'authorization_response', + response: { + providerInfo: { + accessToken: 'ACCESS_TOKEN', + idToken: 'ID_TOKEN', + expiresInSeconds: 10, + scope: 'email', + }, + profile: { + email: 'foo@bar.com', + }, + backstageIdentity: { + token: 'a.b.c', + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'a', + }, + }, + }, + }; + sendWebMessageResponse(mockResponse, appOrigin, data); + expect(parseWebMessageResponse(responseBody).response).toEqual({ + type: 'authorization_response', + response: { + providerInfo: expect.any(Object), + profile: { + email: 'foo@bar.com', + }, + backstageIdentity: expect.any(Object), + }, + }); + + const errData: WebMessageResponse = { + type: 'authorization_response', + error: new Error('Unknown error occurred'), + }; + sendWebMessageResponse(mockResponse, appOrigin, errData); + expect(parseWebMessageResponse(responseBody).response).toEqual({ + type: 'authorization_response', + error: { + name: 'Error', + message: 'Unknown error occurred', + }, + }); + }); + + it('handles single quotes and unicode chars safely', () => { + const mockResponse = { + end: jest.fn().mockReturnThis(), + setHeader: jest.fn().mockReturnThis(), + } as unknown as express.Response; + + const data: WebMessageResponse = { + type: 'authorization_response', + response: { + providerInfo: { + accessToken: 'ACCESS_TOKEN', + idToken: 'ID_TOKEN', + expiresInSeconds: 10, + scope: 'email', + }, + profile: { + email: 'foo@bar.com', + displayName: "Adam l'Hôpital", + }, + backstageIdentity: { + token: 'a.b.c', + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'a', + }, + }, + }, + }; + + sendWebMessageResponse(mockResponse, appOrigin, data); + expect(mockResponse.setHeader).toHaveBeenCalledTimes(3); + expect(mockResponse.end).toHaveBeenCalledTimes(1); + expect(mockResponse.end).toHaveBeenCalledWith( + expect.stringContaining('Adam%20l%27H%C3%B4pital'), + ); + }); + }); +}); diff --git a/plugins/auth-node/src/flow/sendWebMessageResponse.ts b/plugins/auth-node/src/flow/sendWebMessageResponse.ts new file mode 100644 index 0000000000..4c7a85854d --- /dev/null +++ b/plugins/auth-node/src/flow/sendWebMessageResponse.ts @@ -0,0 +1,93 @@ +/* + * 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 { Response } from 'express'; +import crypto from 'crypto'; +import { ClientAuthResponse } from '../types'; +import { serializeError } from '@backstage/errors'; + +/** + * Payload sent as a post message after the auth request is complete. + * If successful then has a valid payload with Auth information else contains an error. + * + * @public + */ +export type WebMessageResponse = + | { + type: 'authorization_response'; + response: ClientAuthResponse; + } + | { + type: 'authorization_response'; + error: Error; + }; + +/** @internal */ +export function safelyEncodeURIComponent(value: string): string { + // Note the g at the end of the regex; all occurrences of single quotes must + // be replaced, which encodeURIComponent does not do itself by default + return encodeURIComponent(value).replace(/'/g, '%27'); +} + +/** @public */ +export function sendWebMessageResponse( + res: Response, + appOrigin: string, + response: WebMessageResponse, +): void { + const jsonData = JSON.stringify(response, (_, value) => { + if (value instanceof Error) { + return serializeError(value); + } + return value; + }); + const base64Data = safelyEncodeURIComponent(jsonData); + const base64Origin = safelyEncodeURIComponent(appOrigin); + + // NOTE: It is absolutely imperative that we use the safe encoder above, to + // be sure that the js code below does not allow the injection of malicious + // data. + + // TODO: Make target app origin configurable globally + + // + // postMessage fails silently if the targetOrigin is disallowed. + // So 2 postMessages are sent from the popup to the parent window. + // First, the origin being used to post the actual authorization response is + // shared with the parent window with a postMessage with targetOrigin '*'. + // Second, the actual authorization response is sent with the app origin + // as the targetOrigin. + // If the first message was received but the actual auth response was + // never received, the event listener can conclude that targetOrigin + // was disallowed, indicating potential misconfiguration. + // + const script = ` + var authResponse = decodeURIComponent('${base64Data}'); + var origin = decodeURIComponent('${base64Origin}'); + var originInfo = {'type': 'config_info', 'targetOrigin': origin}; + (window.opener || window.parent).postMessage(originInfo, '*'); + (window.opener || window.parent).postMessage(JSON.parse(authResponse), origin); + setTimeout(() => { + window.close(); + }, 100); // same as the interval of the core-app-api lib/loginPopup.ts (to address race conditions) + `; + const hash = crypto.createHash('sha256').update(script).digest('base64'); + + res.setHeader('Content-Type', 'text/html'); + res.setHeader('X-Frame-Options', 'sameorigin'); + res.setHeader('Content-Security-Policy', `script-src 'sha256-${hash}'`); + res.end(``); +} diff --git a/plugins/auth-node/src/DefaultIdentityClient.test.ts b/plugins/auth-node/src/identity/DefaultIdentityClient.test.ts similarity index 99% rename from plugins/auth-node/src/DefaultIdentityClient.test.ts rename to plugins/auth-node/src/identity/DefaultIdentityClient.test.ts index 13d9c5a17b..b9bf3cabd2 100644 --- a/plugins/auth-node/src/DefaultIdentityClient.test.ts +++ b/plugins/auth-node/src/identity/DefaultIdentityClient.test.ts @@ -26,7 +26,7 @@ import { setupServer } from 'msw/node'; import { v4 as uuid } from 'uuid'; import { DefaultIdentityClient } from './DefaultIdentityClient'; -import { IdentityApiGetIdentityRequest } from './types'; +import { IdentityApiGetIdentityRequest } from './IdentityApi'; interface AnyJWK extends Record { use: 'sig'; diff --git a/plugins/auth-node/src/DefaultIdentityClient.ts b/plugins/auth-node/src/identity/DefaultIdentityClient.ts similarity index 97% rename from plugins/auth-node/src/DefaultIdentityClient.ts rename to plugins/auth-node/src/identity/DefaultIdentityClient.ts index 8af8bf09e1..1095609dec 100644 --- a/plugins/auth-node/src/DefaultIdentityClient.ts +++ b/plugins/auth-node/src/identity/DefaultIdentityClient.ts @@ -25,12 +25,9 @@ import { jwtVerify, } from 'jose'; import { GetKeyFunction } from 'jose/dist/types/types'; -import { - BackstageIdentityResponse, - IdentityApiGetIdentityRequest, -} from './types'; import { getBearerTokenFromAuthorizationHeader } from './getBearerTokenFromAuthorizationHeader'; -import { IdentityApi } from './IdentityApi'; +import { IdentityApi, IdentityApiGetIdentityRequest } from './IdentityApi'; +import { BackstageIdentityResponse } from '../types'; const CLOCK_MARGIN_S = 10; diff --git a/plugins/auth-node/src/IdentityApi.ts b/plugins/auth-node/src/identity/IdentityApi.ts similarity index 80% rename from plugins/auth-node/src/IdentityApi.ts rename to plugins/auth-node/src/identity/IdentityApi.ts index 86171fa3ab..8322b9f827 100644 --- a/plugins/auth-node/src/IdentityApi.ts +++ b/plugins/auth-node/src/identity/IdentityApi.ts @@ -13,10 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - BackstageIdentityResponse, - IdentityApiGetIdentityRequest, -} from './types'; + +import { Request } from 'express'; +import { BackstageIdentityResponse } from '../types'; + +/** + * Options to request the identity from a Backstage backend request + * + * @public + */ +export type IdentityApiGetIdentityRequest = { + request: Request; +}; /** * An identity client api to authenticate Backstage diff --git a/plugins/auth-node/src/IdentityClient.test.ts b/plugins/auth-node/src/identity/IdentityClient.test.ts similarity index 100% rename from plugins/auth-node/src/IdentityClient.test.ts rename to plugins/auth-node/src/identity/IdentityClient.test.ts diff --git a/plugins/auth-node/src/IdentityClient.ts b/plugins/auth-node/src/identity/IdentityClient.ts similarity index 97% rename from plugins/auth-node/src/IdentityClient.ts rename to plugins/auth-node/src/identity/IdentityClient.ts index 8252a61f81..fd6580dbb2 100644 --- a/plugins/auth-node/src/IdentityClient.ts +++ b/plugins/auth-node/src/identity/IdentityClient.ts @@ -18,7 +18,7 @@ import { DefaultIdentityClient, IdentityClientOptions, } from './DefaultIdentityClient'; -import { BackstageIdentityResponse } from './types'; +import { BackstageIdentityResponse } from '../types'; /** * An identity client to interact with auth-backend and authenticate Backstage diff --git a/plugins/auth-node/src/getBearerTokenFromAuthorizationHeader.test.ts b/plugins/auth-node/src/identity/getBearerTokenFromAuthorizationHeader.test.ts similarity index 100% rename from plugins/auth-node/src/getBearerTokenFromAuthorizationHeader.test.ts rename to plugins/auth-node/src/identity/getBearerTokenFromAuthorizationHeader.test.ts diff --git a/plugins/auth-node/src/getBearerTokenFromAuthorizationHeader.ts b/plugins/auth-node/src/identity/getBearerTokenFromAuthorizationHeader.ts similarity index 100% rename from plugins/auth-node/src/getBearerTokenFromAuthorizationHeader.ts rename to plugins/auth-node/src/identity/getBearerTokenFromAuthorizationHeader.ts diff --git a/plugins/auth-node/src/identity/index.ts b/plugins/auth-node/src/identity/index.ts new file mode 100644 index 0000000000..24e2dcb691 --- /dev/null +++ b/plugins/auth-node/src/identity/index.ts @@ -0,0 +1,24 @@ +/* + * 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. + */ + +export { prepareBackstageIdentityResponse } from './prepareBackstageIdentityResponse'; +export { getBearerTokenFromAuthorizationHeader } from './getBearerTokenFromAuthorizationHeader'; +export { + DefaultIdentityClient, + type IdentityClientOptions, +} from './DefaultIdentityClient'; +export { IdentityClient } from './IdentityClient'; +export type { IdentityApi, IdentityApiGetIdentityRequest } from './IdentityApi'; diff --git a/plugins/auth-backend/src/providers/prepareBackstageIdentityResponse.test.ts b/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.test.ts similarity index 100% rename from plugins/auth-backend/src/providers/prepareBackstageIdentityResponse.test.ts rename to plugins/auth-node/src/identity/prepareBackstageIdentityResponse.test.ts diff --git a/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts b/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts new file mode 100644 index 0000000000..e2753d6648 --- /dev/null +++ b/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts @@ -0,0 +1,52 @@ +/* + * 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 { InputError } from '@backstage/errors'; +import { + BackstageIdentityResponse, + BackstageSignInResult, +} from '@backstage/plugin-auth-node'; + +function parseJwtPayload(token: string) { + const [_header, payload, _signature] = token.split('.'); + return JSON.parse(Buffer.from(payload, 'base64').toString()); +} + +/** + * Parses a Backstage-issued token and decorates the + * {@link @backstage/plugin-auth-node#BackstageIdentityResponse} with identity information sourced from the + * token. + * + * @public + */ +export function prepareBackstageIdentityResponse( + result: BackstageSignInResult, +): BackstageIdentityResponse { + if (!result.token) { + throw new InputError(`Identity response must return a token`); + } + + const { sub, ent } = parseJwtPayload(result.token); + + return { + ...result, + identity: { + type: 'user', + userEntityRef: sub, + ownershipEntityRefs: ent ?? [], + }, + }; +} diff --git a/plugins/auth-node/src/index.ts b/plugins/auth-node/src/index.ts index a70f667adf..e7b0b628b8 100644 --- a/plugins/auth-node/src/index.ts +++ b/plugins/auth-node/src/index.ts @@ -20,14 +20,27 @@ * @packageDocumentation */ -export { getBearerTokenFromAuthorizationHeader } from './getBearerTokenFromAuthorizationHeader'; -export { DefaultIdentityClient } from './DefaultIdentityClient'; -export { IdentityClient } from './IdentityClient'; -export type { IdentityApi } from './IdentityApi'; -export type { IdentityClientOptions } from './DefaultIdentityClient'; +export * from './extensions'; +export * from './flow'; +export * from './identity'; +export * from './oauth'; +export * from './passport'; +export * from './proxy'; +export * from './sign-in'; export type { + AuthProviderConfig, + AuthProviderRouteHandlers, + AuthProviderFactory, + AuthResolverCatalogUserQuery, + AuthResolverContext, BackstageIdentityResponse, BackstageSignInResult, BackstageUserIdentity, - IdentityApiGetIdentityRequest, + ClientAuthResponse, + CookieConfigurer, + ProfileInfo, + ProfileTransform, + SignInInfo, + SignInResolver, + TokenParams, } from './types'; diff --git a/plugins/auth-node/src/oauth/OAuthCookieManager.ts b/plugins/auth-node/src/oauth/OAuthCookieManager.ts new file mode 100644 index 0000000000..5fe2304610 --- /dev/null +++ b/plugins/auth-node/src/oauth/OAuthCookieManager.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 { Request, Response } from 'express'; +import { CookieConfigurer } from '../types'; + +const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000; +const TEN_MINUTES_MS = 600 * 1000; + +const defaultCookieConfigurer: CookieConfigurer = ({ + callbackUrl, + providerId, + appOrigin, +}) => { + const { hostname: domain, pathname, protocol } = new URL(callbackUrl); + const secure = protocol === 'https:'; + + // For situations where the auth-backend is running on a + // different domain than the app, we set the SameSite attribute + // to 'none' to allow third-party access to the cookie, but + // only if it's in a secure context (https). + let sameSite: ReturnType['sameSite'] = 'lax'; + if (new URL(appOrigin).hostname !== domain && secure) { + sameSite = 'none'; + } + + // If the provider supports callbackUrls, the pathname will + // contain the complete path to the frame handler so we need + // to slice off the trailing part of the path. + const path = pathname.endsWith(`${providerId}/handler/frame`) + ? pathname.slice(0, -'/handler/frame'.length) + : `${pathname}/${providerId}`; + + return { domain, path, secure, sameSite }; +}; + +/** @internal */ +export class OAuthCookieManager { + private readonly cookieConfigurer: CookieConfigurer; + private readonly nonceCookie: string; + private readonly refreshTokenCookie: string; + private readonly grantedScopeCookie: string; + + constructor( + private readonly options: { + providerId: string; + defaultAppOrigin: string; + baseUrl: string; + callbackUrl: string; + cookieConfigurer?: CookieConfigurer; + }, + ) { + this.cookieConfigurer = options.cookieConfigurer ?? defaultCookieConfigurer; + + this.nonceCookie = `${options.providerId}-nonce`; + this.refreshTokenCookie = `${options.providerId}-refresh-token`; + this.grantedScopeCookie = `${options.providerId}-granted-scope`; + } + + private getConfig(origin?: string, pathSuffix: string = '') { + const cookieConfig = this.cookieConfigurer({ + providerId: this.options.providerId, + baseUrl: this.options.baseUrl, + callbackUrl: this.options.callbackUrl, + appOrigin: origin ?? this.options.defaultAppOrigin, + }); + return { + httpOnly: true, + sameSite: 'lax' as const, + ...cookieConfig, + path: cookieConfig.path + pathSuffix, + }; + } + + setNonce(res: Response, nonce: string, origin?: string) { + res.cookie(this.nonceCookie, nonce, { + maxAge: TEN_MINUTES_MS, + ...this.getConfig(origin, '/handler'), + }); + } + + setRefreshToken(res: Response, refreshToken: string, origin?: string) { + res.cookie(this.refreshTokenCookie, refreshToken, { + maxAge: THOUSAND_DAYS_MS, + ...this.getConfig(origin), + }); + } + + removeRefreshToken(res: Response, origin?: string) { + res.cookie(this.refreshTokenCookie, '', { + maxAge: 0, + ...this.getConfig(origin), + }); + } + + setGrantedScopes(res: Response, scope: string, origin?: string) { + res.cookie(this.grantedScopeCookie, scope, { + maxAge: THOUSAND_DAYS_MS, + ...this.getConfig(origin), + }); + } + + getNonce(req: Request) { + return req.cookies[this.nonceCookie]; + } + + getRefreshToken(req: Request) { + return req.cookies[this.refreshTokenCookie]; + } + + getGrantedScopes(req: Request) { + return req.cookies[this.grantedScopeCookie]; + } +} diff --git a/plugins/auth-node/src/oauth/OAuthEnvironmentHandler.ts b/plugins/auth-node/src/oauth/OAuthEnvironmentHandler.ts new file mode 100644 index 0000000000..3a5ee00f8b --- /dev/null +++ b/plugins/auth-node/src/oauth/OAuthEnvironmentHandler.ts @@ -0,0 +1,97 @@ +/* + * 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 express from 'express'; +import { Config } from '@backstage/config'; +import { InputError, NotFoundError } from '@backstage/errors'; +import { AuthProviderRouteHandlers } from '../types'; +import { decodeOAuthState } from './state'; + +/** @public */ +export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { + static mapConfig( + config: Config, + factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers, + ) { + const envs = config.keys(); + const handlers = new Map(); + + for (const env of envs) { + const envConfig = config.getConfig(env); + const handler = factoryFunc(envConfig); + handlers.set(env, handler); + } + + return new OAuthEnvironmentHandler(handlers); + } + + constructor( + private readonly handlers: Map, + ) {} + + async start(req: express.Request, res: express.Response): Promise { + const provider = this.getProviderForEnv(req); + await provider.start(req, res); + } + + async frameHandler( + req: express.Request, + res: express.Response, + ): Promise { + const provider = this.getProviderForEnv(req); + await provider.frameHandler(req, res); + } + + async refresh(req: express.Request, res: express.Response): Promise { + const provider = this.getProviderForEnv(req); + await provider.refresh?.(req, res); + } + + async logout(req: express.Request, res: express.Response): Promise { + const provider = this.getProviderForEnv(req); + await provider.logout?.(req, res); + } + + private getEnvFromRequest(req: express.Request): string | undefined { + const reqEnv = req.query.env?.toString(); + if (reqEnv) { + return reqEnv; + } + const stateParams = req.query.state?.toString(); + if (!stateParams) { + return undefined; + } + const { env } = decodeOAuthState(stateParams); + return env; + } + + private getProviderForEnv(req: express.Request): AuthProviderRouteHandlers { + const env: string | undefined = this.getEnvFromRequest(req); + + if (!env) { + throw new InputError(`Must specify 'env' query to select environment`); + } + + const handler = this.handlers.get(env); + if (!handler) { + throw new NotFoundError( + `No configuration available for the '${env}' environment of this provider.`, + ); + } + + return handler; + } +} diff --git a/plugins/auth-node/src/oauth/PassportOAuthAuthenticatorHelper.ts b/plugins/auth-node/src/oauth/PassportOAuthAuthenticatorHelper.ts new file mode 100644 index 0000000000..420a661da5 --- /dev/null +++ b/plugins/auth-node/src/oauth/PassportOAuthAuthenticatorHelper.ts @@ -0,0 +1,137 @@ +/* + * 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 { Strategy } from 'passport'; +import { + PassportDoneCallback, + PassportHelpers, + PassportProfile, +} from '../passport'; +import { ProfileTransform } from '../types'; +import { + OAuthAuthenticatorAuthenticateInput, + OAuthAuthenticatorRefreshInput, + OAuthAuthenticatorResult, + OAuthAuthenticatorStartInput, +} from './types'; + +/** @public */ +export type PassportOAuthResult = { + fullProfile: PassportProfile; + params: { + id_token?: string; + scope: string; + token_type?: string; + expires_in: number; + }; + accessToken: string; +}; + +/** @public */ +export type PassportOAuthPrivateInfo = { + refreshToken?: string; +}; + +/** @public */ +export type PassportOAuthDoneCallback = PassportDoneCallback< + PassportOAuthResult, + PassportOAuthPrivateInfo +>; + +/** @public */ +export class PassportOAuthAuthenticatorHelper { + static defaultProfileTransform: ProfileTransform< + OAuthAuthenticatorResult + > = async input => ({ + profile: PassportHelpers.transformProfile( + input.fullProfile, + input.session.idToken, + ), + }); + + static from(strategy: Strategy) { + return new PassportOAuthAuthenticatorHelper(strategy); + } + + readonly #strategy: Strategy; + + private constructor(strategy: Strategy) { + this.#strategy = strategy; + } + + async start( + input: OAuthAuthenticatorStartInput, + options: Record, + ): Promise<{ url: string; status?: number }> { + return PassportHelpers.executeRedirectStrategy(input.req, this.#strategy, { + scope: input.scope, + state: input.state, + ...options, + }); + } + + async authenticate( + input: OAuthAuthenticatorAuthenticateInput, + ): Promise> { + const { result, privateInfo } = + await PassportHelpers.executeFrameHandlerStrategy< + PassportOAuthResult, + PassportOAuthPrivateInfo + >(input.req, this.#strategy); + + return { + fullProfile: result.fullProfile as PassportProfile, + session: { + accessToken: result.accessToken, + tokenType: result.params.token_type ?? 'bearer', + scope: result.params.scope, + expiresInSeconds: result.params.expires_in, + idToken: result.params.id_token, + refreshToken: privateInfo.refreshToken, + }, + }; + } + + async refresh( + input: OAuthAuthenticatorRefreshInput, + ): Promise> { + const result = await PassportHelpers.executeRefreshTokenStrategy( + this.#strategy, + input.refreshToken, + input.scope, + ); + const fullProfile = await this.fetchProfile(result.accessToken); + return { + fullProfile, + session: { + accessToken: result.accessToken, + tokenType: result.params.token_type ?? 'bearer', + scope: result.params.scope, + expiresInSeconds: result.params.expires_in, + idToken: result.params.id_token, + refreshToken: result.refreshToken, + }, + }; + } + + async fetchProfile(accessToken: string): Promise { + const profile = await PassportHelpers.executeFetchUserProfileStrategy( + this.#strategy, + accessToken, + ); + return profile; + } +} diff --git a/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts b/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts new file mode 100644 index 0000000000..87fde6a1a2 --- /dev/null +++ b/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts @@ -0,0 +1,66 @@ +/* + * 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 { readDeclarativeSignInResolver } from '../sign-in'; +import { + AuthProviderFactory, + ProfileTransform, + SignInResolver, +} from '../types'; +import { OAuthEnvironmentHandler } from './OAuthEnvironmentHandler'; +import { createOAuthRouteHandlers } from './createOAuthRouteHandlers'; +import { OAuthStateTransform } from './state'; +import { OAuthAuthenticator, OAuthAuthenticatorResult } from './types'; +import { SignInResolverFactory } from '../sign-in/createSignInResolverFactory'; + +/** @public */ +export function createOAuthProviderFactory(options: { + authenticator: OAuthAuthenticator; + stateTransform?: OAuthStateTransform; + profileTransform?: ProfileTransform>; + signInResolver?: SignInResolver>; + signInResolverFactories?: { + [name in string]: SignInResolverFactory< + OAuthAuthenticatorResult, + unknown + >; + }; +}): AuthProviderFactory { + return ctx => { + return OAuthEnvironmentHandler.mapConfig(ctx.config, envConfig => { + const signInResolver = + options.signInResolver ?? + readDeclarativeSignInResolver({ + config: envConfig, + signInResolverFactories: options.signInResolverFactories ?? {}, + }); + + return createOAuthRouteHandlers({ + authenticator: options.authenticator, + appUrl: ctx.appUrl, + baseUrl: ctx.baseUrl, + config: envConfig, + isOriginAllowed: ctx.isOriginAllowed, + cookieConfigurer: ctx.cookieConfigurer, + providerId: ctx.providerId, + resolverContext: ctx.resolverContext, + stateTransform: options.stateTransform, + profileTransform: options.profileTransform, + signInResolver, + }); + }); + }; +} diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts new file mode 100644 index 0000000000..2573d95ea1 --- /dev/null +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts @@ -0,0 +1,742 @@ +/* + * 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 { ConfigReader } from '@backstage/config'; +import express from 'express'; +import request, { SuperAgentTest } from 'supertest'; +import cookieParser from 'cookie-parser'; +import PromiseRouter from 'express-promise-router'; +import { AuthProviderRouteHandlers, AuthResolverContext } from '../types'; +import { createOAuthRouteHandlers } from './createOAuthRouteHandlers'; +import { OAuthAuthenticator } from './types'; +import { errorHandler } from '@backstage/backend-common'; +import { encodeOAuthState, OAuthState } from './state'; +import { PassportProfile } from '../passport'; +import { parseWebMessageResponse } from '../flow/__testUtils__/parseWebMessageResponse'; + +const mockAuthenticator: jest.Mocked> = { + initialize: jest.fn(_r => ({ ctx: 'authenticator' })), + start: jest.fn(), + authenticate: jest.fn(), + refresh: jest.fn(), + logout: jest.fn(), + defaultProfileTransform: jest.fn(async (_r, _c) => ({ profile: {} })), +}; + +const mockBackstageToken = `a.${btoa( + JSON.stringify({ sub: 'user:default/mock', ent: [] }), +)}.c`; + +const mockSession = { + accessToken: 'access-token', + expiresInSeconds: 3, + scope: 'my-scope', + tokenType: 'bear', + idToken: 'id-token', + refreshToken: 'refresh-token', +}; + +const baseConfig = { + authenticator: mockAuthenticator, + appUrl: 'http://127.0.0.1', + baseUrl: 'http://127.0.0.1:7007', + isOriginAllowed: () => true, + providerId: 'my-provider', + config: new ConfigReader({}), + resolverContext: { ctx: 'resolver' } as unknown as AuthResolverContext, +}; + +function wrapInApp(handlers: AuthProviderRouteHandlers) { + const app = express(); + + const router = PromiseRouter(); + + router.use(cookieParser()); + app.use('/my-provider', router); + app.use(errorHandler()); + + router.get('/start', handlers.start.bind(handlers)); + router.get('/handler/frame', handlers.frameHandler.bind(handlers)); + router.post('/handler/frame', handlers.frameHandler.bind(handlers)); + if (handlers.logout) { + router.post('/logout', handlers.logout.bind(handlers)); + } + if (handlers.refresh) { + router.get('/refresh', handlers.refresh.bind(handlers)); + router.post('/refresh', handlers.refresh.bind(handlers)); + } + + return app; +} + +function getNonceCookie(test: SuperAgentTest) { + return test.jar.getCookie('my-provider-nonce', { + domain: '127.0.0.1', + path: '/my-provider/handler', + script: false, + secure: false, + }); +} + +function getRefreshTokenCookie(test: SuperAgentTest) { + return test.jar.getCookie('my-provider-refresh-token', { + domain: '127.0.0.1', + path: '/my-provider', + script: false, + secure: false, + }); +} + +function getGrantedScopesCookie(test: SuperAgentTest) { + return test.jar.getCookie('my-provider-granted-scope', { + domain: '127.0.0.1', + path: '/my-provider', + script: false, + secure: false, + }); +} + +describe('createOAuthRouteHandlers', () => { + afterEach(() => jest.clearAllMocks()); + + it('should be created', () => { + const handlers = createOAuthRouteHandlers(baseConfig); + expect(handlers).toEqual({ + start: expect.any(Function), + frameHandler: expect.any(Function), + refresh: expect.any(Function), + logout: expect.any(Function), + }); + }); + + describe('start', () => { + it('should require an env query', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + const res = await request(app).get('/my-provider/start'); + + expect(res.status).toBe(400); + expect(res.body).toMatchObject({ + error: { + name: 'InputError', + message: 'No env provided in request query parameters', + }, + }); + }); + + it('should start', async () => { + const agent = request.agent( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ); + + mockAuthenticator.start.mockResolvedValue({ + url: 'https://example.com/redirect', + }); + + const res = await agent.get( + '/my-provider/start?env=development&scope=my-scope', + ); + + const { value: nonce } = getNonceCookie(agent); + + expect(res.text).toBe(''); + expect(res.status).toBe(302); + expect(res.get('Location')).toBe('https://example.com/redirect'); + expect(res.get('Content-Length')).toBe('0'); + + expect(mockAuthenticator.start).toHaveBeenCalledWith( + { + req: expect.anything(), + scope: 'my-scope', + state: encodeOAuthState({ + nonce: decodeURIComponent(nonce), + env: 'development', + }), + }, + { ctx: 'authenticator' }, + ); + }); + + it('should start with additional parameters, transform state, and persist scopes', async () => { + const agent = request.agent( + wrapInApp( + createOAuthRouteHandlers({ + ...baseConfig, + authenticator: { + ...mockAuthenticator, + shouldPersistScopes: true, + }, + stateTransform: async state => ({ + state: { ...state, nonce: '123' }, + }), + }), + ), + ); + + mockAuthenticator.start.mockResolvedValue({ + url: 'https://example.com/redirect', + }); + + const res = await agent.get('/my-provider/start').query({ + env: 'development', + scope: 'my-scope', + origin: 'https://remotehost', + redirectUrl: 'https://remotehost/redirect', + flow: 'redirect', + }); + + expect(res.text).toBe(''); + expect(res.status).toBe(302); + expect(res.get('Location')).toBe('https://example.com/redirect'); + expect(res.get('Content-Length')).toBe('0'); + + expect(mockAuthenticator.start).toHaveBeenCalledWith( + { + req: expect.anything(), + scope: 'my-scope', + state: encodeOAuthState({ + nonce: '123', + env: 'development', + origin: 'https://remotehost', + redirectUrl: 'https://remotehost/redirect', + flow: 'redirect', + scope: 'my-scope', + }), + }, + { ctx: 'authenticator' }, + ); + }); + }); + + describe('frameHandler', () => { + it('should authenticate', async () => { + const agent = request.agent( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ); + + agent.jar.setCookie( + 'my-provider-nonce=123', + '127.0.0.1', + '/my-provider/handler', + ); + + mockAuthenticator.authenticate.mockResolvedValue({ + fullProfile: { id: 'id' } as PassportProfile, + session: mockSession, + }); + + const res = await agent.get('/my-provider/handler/frame').query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + } as OAuthState), + }); + + expect(mockAuthenticator.authenticate).toHaveBeenCalledWith( + { req: expect.anything() }, + { ctx: 'authenticator' }, + ); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + response: { + profile: {}, + providerInfo: { + accessToken: 'access-token', + expiresInSeconds: 3, + idToken: 'id-token', + scope: 'my-scope', + }, + }, + }); + + expect(getRefreshTokenCookie(agent).value).toBe('refresh-token'); + expect(getGrantedScopesCookie(agent)).toBeUndefined(); + }); + + it('should authenticate with sign-in, profile transform, and persisted scopes', async () => { + const agent = request.agent( + wrapInApp( + createOAuthRouteHandlers({ + ...baseConfig, + authenticator: { + ...mockAuthenticator, + shouldPersistScopes: true, + }, + profileTransform: async () => ({ profile: { email: 'em@i.l' } }), + signInResolver: async () => ({ token: mockBackstageToken }), + }), + ), + ); + + agent.jar.setCookie( + 'my-provider-nonce=123', + '127.0.0.1', + '/my-provider/handler', + ); + + mockAuthenticator.authenticate.mockResolvedValue({ + fullProfile: { id: 'id' } as PassportProfile, + session: mockSession, + }); + + const res = await agent.get('/my-provider/handler/frame').query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + scope: 'my-scope', + } as OAuthState), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + response: { + profile: { email: 'em@i.l' }, + providerInfo: { + accessToken: 'access-token', + expiresInSeconds: 3, + idToken: 'id-token', + scope: 'my-scope', + }, + backstageIdentity: { + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'user:default/mock', + }, + token: mockBackstageToken, + }, + }, + }); + + expect(getRefreshTokenCookie(agent).value).toBe('refresh-token'); + expect(getGrantedScopesCookie(agent).value).toBe('my-scope'); + }); + + it('should redirect with persisted scope', async () => { + const agent = request.agent( + wrapInApp( + createOAuthRouteHandlers({ + ...baseConfig, + authenticator: { + ...mockAuthenticator, + shouldPersistScopes: true, + }, + profileTransform: async () => ({ profile: { email: 'em@i.l' } }), + signInResolver: async () => ({ token: mockBackstageToken }), + }), + ), + ); + + agent.jar.setCookie( + 'my-provider-nonce=123', + '127.0.0.1', + '/my-provider/handler', + ); + + mockAuthenticator.authenticate.mockResolvedValue({ + fullProfile: { id: 'id' } as PassportProfile, + session: mockSession, + }); + + const res = await agent.get('/my-provider/handler/frame').query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + scope: 'my-scope', + flow: 'redirect', + redirectUrl: 'https://127.0.0.1:3000/redirect', + } as OAuthState), + }); + + expect(res.status).toBe(302); + expect(res.get('Location')).toBe('https://127.0.0.1:3000/redirect'); + + expect(getRefreshTokenCookie(agent).value).toBe('refresh-token'); + expect(getGrantedScopesCookie(agent).value).toBe('my-scope'); + }); + + it('should require a valid origin', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + const res = await request(app) + .get('/my-provider/handler/frame') + .query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + origin: 'invalid-origin', + }), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + error: { + name: 'NotAllowedError', + message: 'App origin is invalid, failed to parse', + }, + }); + }); + + it('should reject origins that are not allowed', async () => { + const app = wrapInApp( + createOAuthRouteHandlers({ + ...baseConfig, + isOriginAllowed: () => false, + }), + ); + const res = await request(app) + .get('/my-provider/handler/frame') + .query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + origin: 'http://localhost:3000', + }), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + error: { + name: 'NotAllowedError', + message: "Origin 'http://localhost:3000' is not allowed", + }, + }); + }); + + it('should reject missing state env', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + const res = await request(app) + .get('/my-provider/handler/frame') + .query({ + state: encodeOAuthState({ + nonce: '123', + } as OAuthState), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + error: { + name: 'NotAllowedError', + message: 'OAuth state is invalid, missing env', + }, + }); + }); + + it('should reject missing cookie nonce', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + const res = await request(app) + .get('/my-provider/handler/frame') + .query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + }), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + error: { + name: 'NotAllowedError', + message: 'Auth response is missing cookie nonce', + }, + }); + }); + + it('should reject missing state nonce', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + const res = await request(app) + .get('/my-provider/handler/frame') + .query({ + state: encodeOAuthState({ + env: 'development', + } as OAuthState), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + error: { + name: 'NotAllowedError', + message: 'OAuth state is invalid, missing nonce', + }, + }); + }); + + it('should reject mismatched nonce', async () => { + const agent = request.agent( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ); + + agent.jar.setCookie( + 'my-provider-nonce=456', + '127.0.0.1', + '/my-provider/handler', + ); + + const res = await agent.get('/my-provider/handler/frame').query({ + state: encodeOAuthState({ + env: 'development', + nonce: '123', + } as OAuthState), + }); + + expect(res.status).toBe(200); + expect(parseWebMessageResponse(res.text).response).toEqual({ + type: 'authorization_response', + error: { + name: 'NotAllowedError', + message: 'Invalid nonce', + }, + }); + }); + }); + + describe('refresh', () => { + it('should refresh', async () => { + const agent = request.agent( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ); + + agent.jar.setCookie( + 'my-provider-refresh-token=refresh-token', + '127.0.0.1', + '/my-provider', + ); + + mockAuthenticator.refresh.mockImplementation(async ({ scope }) => ({ + fullProfile: { id: 'id' } as PassportProfile, + session: { ...mockSession, scope }, + })); + + const res = await agent + .post('/my-provider/refresh') + .set('X-Requested-With', 'XMLHttpRequest') + .query({ scope: 'my-scope' }); + + expect(mockAuthenticator.refresh).toHaveBeenCalledWith( + { + req: expect.anything(), + refreshToken: 'refresh-token', + scope: 'my-scope', + }, + { ctx: 'authenticator' }, + ); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ + profile: {}, + providerInfo: { + accessToken: 'access-token', + expiresInSeconds: 3, + idToken: 'id-token', + scope: 'my-scope', + }, + }); + }); + + it('should refresh with sign-in, profile transform, and persisted scopes', async () => { + const agent = request.agent( + wrapInApp( + createOAuthRouteHandlers({ + ...baseConfig, + authenticator: { + ...mockAuthenticator, + shouldPersistScopes: true, + }, + profileTransform: async () => ({ profile: { email: 'em@i.l' } }), + signInResolver: async () => ({ token: mockBackstageToken }), + }), + ), + ); + + agent.jar.setCookie( + 'my-provider-refresh-token=refresh-token', + '127.0.0.1', + '/my-provider', + ); + agent.jar.setCookie( + 'my-provider-granted-scope=persisted-scope', + '127.0.0.1', + '/my-provider', + ); + + mockAuthenticator.refresh.mockImplementation(async ({ scope }) => ({ + fullProfile: { id: 'id' } as PassportProfile, + session: { ...mockSession, scope, refreshToken: 'new-refresh-token' }, + })); + + const res = await agent + .post('/my-provider/refresh') + .set('X-Requested-With', 'XMLHttpRequest'); + + expect(mockAuthenticator.refresh).toHaveBeenCalledWith( + { + req: expect.anything(), + refreshToken: 'refresh-token', + scope: 'persisted-scope', + }, + { ctx: 'authenticator' }, + ); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ + profile: { email: 'em@i.l' }, + providerInfo: { + accessToken: 'access-token', + expiresInSeconds: 3, + idToken: 'id-token', + scope: 'persisted-scope', + }, + backstageIdentity: { + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'user:default/mock', + }, + token: mockBackstageToken, + }, + }); + expect(getRefreshTokenCookie(agent).value).toBe('new-refresh-token'); + }); + + it('should forward errors', async () => { + const agent = request.agent( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ); + + agent.jar.setCookie( + 'my-provider-refresh-token=refresh-token', + '127.0.0.1', + '/my-provider', + ); + + mockAuthenticator.refresh.mockRejectedValue(new Error('NOPE')); + + const res = await agent + .post('/my-provider/refresh') + .set('X-Requested-With', 'XMLHttpRequest'); + + expect(res.status).toBe(401); + expect(res.body).toMatchObject({ + error: { + name: 'AuthenticationError', + message: 'Refresh failed; caused by Error: NOPE', + }, + }); + }); + + it('should require refresh cookie', async () => { + const res = await request(wrapInApp(createOAuthRouteHandlers(baseConfig))) + .post('/my-provider/refresh') + .set('X-Requested-With', 'XMLHttpRequest'); + + expect(res.status).toBe(401); + expect(res.body).toMatchObject({ + error: { + name: 'AuthenticationError', + message: + 'Refresh failed; caused by InputError: Missing session cookie', + }, + }); + }); + + it('should reject requests without CSRF header', async () => { + const res = await request( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ).post('/my-provider/refresh'); + + expect(res.status).toBe(401); + expect(res.body).toMatchObject({ + error: { + name: 'AuthenticationError', + message: 'Invalid X-Requested-With header', + }, + }); + }); + + it('should reject requests with invalid CSRF header', async () => { + const res = await request(wrapInApp(createOAuthRouteHandlers(baseConfig))) + .post('/my-provider/refresh') + .set('X-Requested-With', 'invalid'); + + expect(res.status).toBe(401); + expect(res.body).toMatchObject({ + error: { + name: 'AuthenticationError', + message: 'Invalid X-Requested-With header', + }, + }); + }); + }); + + describe('logout', () => { + it('should log out', async () => { + const agent = request.agent( + wrapInApp(createOAuthRouteHandlers(baseConfig)), + ); + + agent.jar.setCookie( + 'my-provider-refresh-token=my-refresh-token', + '127.0.0.1', + '/my-provider', + ); + + expect(getRefreshTokenCookie(agent).value).toBe('my-refresh-token'); + + const res = await agent + .post('/my-provider/logout') + .set('X-Requested-With', 'XMLHttpRequest'); + + expect(res.status).toBe(200); + expect(res.body).toEqual({}); + + expect(getRefreshTokenCookie(agent)).toBeUndefined(); + }); + + it('should reject requests without CSRF header', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + + const res = await request(app).post('/my-provider/logout'); + expect(res.status).toBe(401); + expect(res.body).toMatchObject({ + error: { + name: 'AuthenticationError', + message: 'Invalid X-Requested-With header', + }, + }); + }); + + it('should reject requests with invalid CSRF header', async () => { + const app = wrapInApp(createOAuthRouteHandlers(baseConfig)); + + const res = await request(app) + .post('/my-provider/logout') + .set('X-Requested-With', 'wrong-value'); + expect(res.status).toBe(401); + expect(res.body).toMatchObject({ + error: { + name: 'AuthenticationError', + message: 'Invalid X-Requested-With header', + }, + }); + }); + }); +}); diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts new file mode 100644 index 0000000000..011b3fc0f0 --- /dev/null +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts @@ -0,0 +1,343 @@ +/* + * 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 express from 'express'; +import crypto from 'crypto'; +import { URL } from 'url'; +import { + AuthenticationError, + InputError, + isError, + NotAllowedError, +} from '@backstage/errors'; +import { + encodeOAuthState, + decodeOAuthState, + OAuthStateTransform, + OAuthState, +} from './state'; +import { sendWebMessageResponse } from '../flow'; +import { prepareBackstageIdentityResponse } from '../identity'; +import { OAuthCookieManager } from './OAuthCookieManager'; +import { + AuthProviderRouteHandlers, + AuthResolverContext, + ClientAuthResponse, + CookieConfigurer, + ProfileTransform, + SignInResolver, +} from '../types'; +import { OAuthAuthenticator, OAuthAuthenticatorResult } from './types'; +import { Config } from '@backstage/config'; + +/** @public */ +export interface OAuthRouteHandlersOptions { + authenticator: OAuthAuthenticator; + appUrl: string; + baseUrl: string; + isOriginAllowed: (origin: string) => boolean; + providerId: string; + config: Config; + resolverContext: AuthResolverContext; + stateTransform?: OAuthStateTransform; + profileTransform?: ProfileTransform>; + cookieConfigurer?: CookieConfigurer; + signInResolver?: SignInResolver>; +} + +/** @internal */ +type ClientOAuthResponse = ClientAuthResponse<{ + /** + * An access token issued for the signed in user. + */ + accessToken: string; + /** + * (Optional) Id token issued for the signed in user. + */ + idToken?: string; + /** + * Expiry of the access token in seconds. + */ + expiresInSeconds?: number; + /** + * Scopes granted for the access token. + */ + scope: string; +}>; + +/** @public */ +export function createOAuthRouteHandlers( + options: OAuthRouteHandlersOptions, +): AuthProviderRouteHandlers { + const { + authenticator, + config, + baseUrl, + appUrl, + providerId, + isOriginAllowed, + cookieConfigurer, + resolverContext, + signInResolver, + } = options; + + const defaultAppOrigin = new URL(appUrl).origin; + const callbackUrl = + config.getOptionalString('callbackUrl') ?? + `${baseUrl}/${providerId}/handler/frame`; + + const stateTransform = options.stateTransform ?? (state => ({ state })); + const profileTransform = + options.profileTransform ?? authenticator.defaultProfileTransform; + const authenticatorCtx = authenticator.initialize({ config, callbackUrl }); + const cookieManager = new OAuthCookieManager({ + baseUrl, + callbackUrl, + defaultAppOrigin, + providerId, + cookieConfigurer, + }); + + return { + async start( + this: never, + req: express.Request, + res: express.Response, + ): Promise { + // retrieve scopes from request + const scope = req.query.scope?.toString() ?? ''; + const env = req.query.env?.toString(); + const origin = req.query.origin?.toString(); + const redirectUrl = req.query.redirectUrl?.toString(); + const flow = req.query.flow?.toString(); + + if (!env) { + throw new InputError('No env provided in request query parameters'); + } + + const nonce = crypto.randomBytes(16).toString('base64'); + // set a nonce cookie before redirecting to oauth provider + cookieManager.setNonce(res, nonce, origin); + + const state: OAuthState = { nonce, env, origin, redirectUrl, flow }; + + // If scopes are persisted then we pass them through the state so that we + // can set the cookie on successful auth + if (authenticator.shouldPersistScopes) { + state.scope = scope; + } + + const { state: transformedState } = await stateTransform(state, { req }); + const encodedState = encodeOAuthState(transformedState); + + const { url, status } = await options.authenticator.start( + { req, scope, state: encodedState }, + authenticatorCtx, + ); + + res.statusCode = status || 302; + res.setHeader('Location', url); + res.setHeader('Content-Length', '0'); + res.end(); + }, + + async frameHandler( + this: never, + req: express.Request, + res: express.Response, + ): Promise { + let appOrigin = defaultAppOrigin; + + try { + const state = decodeOAuthState(req.query.state?.toString() ?? ''); + + if (state.origin) { + try { + appOrigin = new URL(state.origin).origin; + } catch { + throw new NotAllowedError('App origin is invalid, failed to parse'); + } + if (!isOriginAllowed(appOrigin)) { + throw new NotAllowedError(`Origin '${appOrigin}' is not allowed`); + } + } + + // The same nonce is passed through cookie and state, and they must match + const cookieNonce = cookieManager.getNonce(req); + const stateNonce = state.nonce; + if (!cookieNonce) { + throw new NotAllowedError('Auth response is missing cookie nonce'); + } + if (cookieNonce !== stateNonce) { + throw new NotAllowedError('Invalid nonce'); + } + + const result = await authenticator.authenticate( + { req }, + authenticatorCtx, + ); + const { profile } = await profileTransform(result, resolverContext); + + const response: ClientOAuthResponse = { + profile, + providerInfo: { + idToken: result.session.idToken, + accessToken: result.session.accessToken, + scope: result.session.scope, + expiresInSeconds: result.session.expiresInSeconds, + }, + }; + + if (signInResolver) { + const identity = await signInResolver( + { profile, result }, + resolverContext, + ); + response.backstageIdentity = + prepareBackstageIdentityResponse(identity); + } + + // Store the scope that we have been granted for this session. This is useful if + // the provider does not return granted scopes on refresh or if they are normalized. + if (authenticator.shouldPersistScopes && state.scope) { + cookieManager.setGrantedScopes(res, state.scope, appOrigin); + result.session.scope = state.scope; + } + + if (result.session.refreshToken) { + // set new refresh token + cookieManager.setRefreshToken( + res, + result.session.refreshToken, + appOrigin, + ); + } + + // When using the redirect flow we rely on refresh token we just + // acquired to get a new session once we're back in the app. + if (state.flow === 'redirect') { + if (!state.redirectUrl) { + throw new InputError( + 'No redirectUrl provided in request query parameters', + ); + } + res.redirect(state.redirectUrl); + return; + } + + // post message back to popup if successful + sendWebMessageResponse(res, appOrigin, { + type: 'authorization_response', + response, + }); + } catch (error) { + const { name, message } = isError(error) + ? error + : new Error('Encountered invalid error'); // Being a bit safe and not forwarding the bad value + // post error message back to popup if failure + sendWebMessageResponse(res, appOrigin, { + type: 'authorization_response', + error: { name, message }, + }); + } + }, + + async logout( + this: never, + req: express.Request, + res: express.Response, + ): Promise { + // We use this as a lightweight CSRF protection + if (req.header('X-Requested-With') !== 'XMLHttpRequest') { + throw new AuthenticationError('Invalid X-Requested-With header'); + } + + if (authenticator.logout) { + const refreshToken = cookieManager.getRefreshToken(req); + await authenticator.logout({ req, refreshToken }, authenticatorCtx); + } + + // remove refresh token cookie if it is set + cookieManager.removeRefreshToken(res, req.get('origin')); + + res.status(200).end(); + }, + + async refresh( + this: never, + req: express.Request, + res: express.Response, + ): Promise { + // We use this as a lightweight CSRF protection + if (req.header('X-Requested-With') !== 'XMLHttpRequest') { + throw new AuthenticationError('Invalid X-Requested-With header'); + } + + try { + const refreshToken = cookieManager.getRefreshToken(req); + + // throw error if refresh token is missing in the request + if (!refreshToken) { + throw new InputError('Missing session cookie'); + } + + let scope = req.query.scope?.toString() ?? ''; + if (authenticator.shouldPersistScopes) { + scope = cookieManager.getGrantedScopes(req); + } + + const result = await authenticator.refresh( + { req, scope, refreshToken }, + authenticatorCtx, + ); + + const { profile } = await profileTransform(result, resolverContext); + + const newRefreshToken = result.session.refreshToken; + if (newRefreshToken && newRefreshToken !== refreshToken) { + cookieManager.setRefreshToken( + res, + newRefreshToken, + req.get('origin'), + ); + } + + const response: ClientOAuthResponse = { + profile, + providerInfo: { + idToken: result.session.idToken, + accessToken: result.session.accessToken, + scope: result.session.scope, + expiresInSeconds: result.session.expiresInSeconds, + }, + }; + + if (signInResolver) { + const identity = await signInResolver( + { profile, result }, + resolverContext, + ); + response.backstageIdentity = + prepareBackstageIdentityResponse(identity); + } + + res.status(200).json(response); + } catch (error) { + throw new AuthenticationError('Refresh failed', error); + } + }, + }; +} diff --git a/plugins/auth-node/src/oauth/index.ts b/plugins/auth-node/src/oauth/index.ts new file mode 100644 index 0000000000..25213dd2f0 --- /dev/null +++ b/plugins/auth-node/src/oauth/index.ts @@ -0,0 +1,44 @@ +/* + * 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. + */ + +export { + createOAuthRouteHandlers, + type OAuthRouteHandlersOptions, +} from './createOAuthRouteHandlers'; +export { + PassportOAuthAuthenticatorHelper, + type PassportOAuthDoneCallback, + type PassportOAuthPrivateInfo, + type PassportOAuthResult, +} from './PassportOAuthAuthenticatorHelper'; +export { OAuthEnvironmentHandler } from './OAuthEnvironmentHandler'; +export { createOAuthProviderFactory } from './createOAuthProviderFactory'; +export { + encodeOAuthState, + decodeOAuthState, + type OAuthState, + type OAuthStateTransform, +} from './state'; +export { + createOAuthAuthenticator, + type OAuthAuthenticator, + type OAuthAuthenticatorAuthenticateInput, + type OAuthAuthenticatorLogoutInput, + type OAuthAuthenticatorRefreshInput, + type OAuthAuthenticatorResult, + type OAuthAuthenticatorStartInput, + type OAuthSession, +} from './types'; diff --git a/plugins/auth-node/src/oauth/state.ts b/plugins/auth-node/src/oauth/state.ts new file mode 100644 index 0000000000..fc747d08a5 --- /dev/null +++ b/plugins/auth-node/src/oauth/state.ts @@ -0,0 +1,62 @@ +/* + * 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 pickBy from 'lodash/pickBy'; +import { Request } from 'express'; +import { NotAllowedError } from '@backstage/errors'; + +/** + * A type for the serialized value in the `state` parameter of the OAuth authorization flow + * @public + */ +export type OAuthState = { + nonce: string; + env: string; + origin?: string; + scope?: string; + redirectUrl?: string; + flow?: string; +}; + +/** @public */ +export type OAuthStateTransform = ( + state: OAuthState, + context: { req: Request }, +) => Promise<{ state: OAuthState }>; + +/** @public */ +export function encodeOAuthState(state: OAuthState): string { + const stateString = new URLSearchParams( + pickBy(state, value => value !== undefined), + ).toString(); + + return Buffer.from(stateString, 'utf-8').toString('hex'); +} + +/** @public */ +export function decodeOAuthState(encodedState: string): OAuthState { + const state = Object.fromEntries( + new URLSearchParams(Buffer.from(encodedState, 'hex').toString('utf-8')), + ); + if (!state.env || state.env?.length === 0) { + throw new NotAllowedError('OAuth state is invalid, missing env'); + } + if (!state.nonce || state.nonce?.length === 0) { + throw new NotAllowedError('OAuth state is invalid, missing nonce'); + } + + return state as OAuthState; +} diff --git a/plugins/auth-node/src/oauth/types.ts b/plugins/auth-node/src/oauth/types.ts new file mode 100644 index 0000000000..c552de394e --- /dev/null +++ b/plugins/auth-node/src/oauth/types.ts @@ -0,0 +1,88 @@ +/* + * 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 { Config } from '@backstage/config'; +import { Request } from 'express'; +import { ProfileTransform } from '../types'; + +/** @public */ +export interface OAuthSession { + accessToken: string; + tokenType: string; + idToken?: string; + scope: string; + expiresInSeconds: number; + refreshToken?: string; +} + +/** @public */ +export interface OAuthAuthenticatorStartInput { + scope: string; + state: string; + req: Request; +} + +/** @public */ +export interface OAuthAuthenticatorAuthenticateInput { + req: Request; +} + +/** @public */ +export interface OAuthAuthenticatorRefreshInput { + scope: string; + refreshToken: string; + req: Request; +} + +/** @public */ +export interface OAuthAuthenticatorLogoutInput { + accessToken?: string; + refreshToken?: string; + req: Request; +} + +/** @public */ +export interface OAuthAuthenticatorResult { + fullProfile: TProfile; + session: OAuthSession; +} + +/** @public */ +export interface OAuthAuthenticator { + defaultProfileTransform: ProfileTransform>; + shouldPersistScopes?: boolean; + initialize(ctx: { callbackUrl: string; config: Config }): TContext; + start( + input: OAuthAuthenticatorStartInput, + ctx: TContext, + ): Promise<{ url: string; status?: number }>; + authenticate( + input: OAuthAuthenticatorAuthenticateInput, + ctx: TContext, + ): Promise>; + refresh( + input: OAuthAuthenticatorRefreshInput, + ctx: TContext, + ): Promise>; + logout?(input: OAuthAuthenticatorLogoutInput, ctx: TContext): Promise; +} + +/** @public */ +export function createOAuthAuthenticator( + authenticator: OAuthAuthenticator, +): OAuthAuthenticator { + return authenticator; +} diff --git a/plugins/auth-node/src/passport/PassportHelpers.ts b/plugins/auth-node/src/passport/PassportHelpers.ts new file mode 100644 index 0000000000..6c13523811 --- /dev/null +++ b/plugins/auth-node/src/passport/PassportHelpers.ts @@ -0,0 +1,249 @@ +/* + * 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 { Request } from 'express'; +import { Strategy } from 'passport'; +import { PassportProfile } from './types'; +import { ProfileInfo } from '../types'; + +// Re-declared here to avoid direct dependency on passport-oauth2 +/** @internal */ +interface InternalOAuthError extends Error { + oauthError?: { + data?: string; + }; +} + +/** @internal */ +function decodeJwtPayload(token: string): Record { + const payloadStr = token.split('.')[1]; + if (!payloadStr) { + throw new Error('Invalid JWT token'); + } + + let payload: unknown; + try { + payload = JSON.parse( + Buffer.from( + payloadStr.replace(/-/g, '+').replace(/_/g, '/'), + 'base64', + ).toString('utf8'), + ); + } catch (e) { + throw new Error('Invalid JWT token'); + } + if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { + throw new Error('Invalid JWT token'); + } + return payload as Record; +} + +/** @public */ +export class PassportHelpers { + private constructor() {} + + static transformProfile = ( + profile: PassportProfile, + idToken?: string, + ): ProfileInfo => { + let email: string | undefined = undefined; + if (profile.emails && profile.emails.length > 0) { + const [firstEmail] = profile.emails; + email = firstEmail.value; + } + + let picture: string | undefined = undefined; + if (profile.avatarUrl) { + picture = profile.avatarUrl; + } else if (profile.photos && profile.photos.length > 0) { + const [firstPhoto] = profile.photos; + picture = firstPhoto.value; + } + + let displayName: string | undefined = + profile.displayName ?? profile.username ?? profile.id; + + if ((!email || !picture || !displayName) && idToken) { + try { + const decoded: Record = decodeJwtPayload(idToken); + if (!email && decoded.email) { + email = decoded.email; + } + if (!picture && decoded.picture) { + picture = decoded.picture; + } + if (!displayName && decoded.name) { + displayName = decoded.name; + } + } catch (e) { + throw new Error(`Failed to parse id token and get profile info, ${e}`); + } + } + + return { + email, + picture, + displayName, + }; + }; + + static async executeRedirectStrategy( + req: Request, + providerStrategy: Strategy, + options: Record, + ): Promise<{ + /** + * URL to redirect to + */ + url: string; + /** + * Status code to use for the redirect + */ + status?: number; + }> { + return new Promise(resolve => { + const strategy = Object.create(providerStrategy); + strategy.redirect = (url: string, status?: number) => { + resolve({ url, status: status ?? undefined }); + }; + + strategy.authenticate(req, { ...options }); + }); + } + + static async executeFrameHandlerStrategy( + req: Request, + providerStrategy: Strategy, + options?: Record, + ): Promise<{ result: TResult; privateInfo: TPrivateInfo }> { + return new Promise((resolve, reject) => { + const strategy = Object.create(providerStrategy); + strategy.success = (result: any, privateInfo: any) => { + resolve({ result, privateInfo }); + }; + strategy.fail = ( + info: { type: 'success' | 'error'; message?: string }, + // _status: number, + ) => { + reject(new Error(`Authentication rejected, ${info.message ?? ''}`)); + }; + strategy.error = (error: InternalOAuthError) => { + let message = `Authentication failed, ${error.message}`; + + if (error.oauthError?.data) { + try { + const errorData = JSON.parse(error.oauthError.data); + + if (errorData.message) { + message += ` - ${errorData.message}`; + } + } catch (parseError) { + message += ` - ${error.oauthError}`; + } + } + + reject(new Error(message)); + }; + strategy.redirect = () => { + reject(new Error('Unexpected redirect')); + }; + strategy.authenticate(req, { ...(options ?? {}) }); + }); + } + + static async executeRefreshTokenStrategy( + providerStrategy: Strategy, + refreshToken: string, + scope: string, + ): Promise<{ + /** + * An access token issued for the signed in user. + */ + accessToken: string; + /** + * Optionally, the server can issue a new Refresh Token for the user + */ + refreshToken?: string; + params: any; + }> { + return new Promise((resolve, reject) => { + const anyStrategy = providerStrategy as any; + const OAuth2 = anyStrategy._oauth2.constructor; + const oauth2 = new OAuth2( + anyStrategy._oauth2._clientId, + anyStrategy._oauth2._clientSecret, + anyStrategy._oauth2._baseSite, + anyStrategy._oauth2._authorizeUrl, + anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl, + anyStrategy._oauth2._customHeaders, + ); + + oauth2.getOAuthAccessToken( + refreshToken, + { + scope, + grant_type: 'refresh_token', + }, + ( + err: Error | null, + accessToken: string, + newRefreshToken: string, + params: any, + ) => { + if (err) { + reject( + new Error(`Failed to refresh access token ${err.toString()}`), + ); + } + if (!accessToken) { + reject( + new Error( + `Failed to refresh access token, no access token received`, + ), + ); + } + + resolve({ + accessToken, + refreshToken: newRefreshToken, + params, + }); + }, + ); + }); + } + + static async executeFetchUserProfileStrategy( + providerStrategy: Strategy, + accessToken: string, + ): Promise { + return new Promise((resolve, reject) => { + const anyStrategy = providerStrategy as unknown as { + userProfile(accessToken: string, callback: Function): void; + }; + anyStrategy.userProfile( + accessToken, + (error: Error, rawProfile: PassportProfile) => { + if (error) { + reject(error); + } else { + resolve(rawProfile); + } + }, + ); + }); + } +} diff --git a/plugins/auth-node/src/passport/index.ts b/plugins/auth-node/src/passport/index.ts new file mode 100644 index 0000000000..f70c0af921 --- /dev/null +++ b/plugins/auth-node/src/passport/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export { PassportHelpers } from './PassportHelpers'; +export type { PassportDoneCallback, PassportProfile } from './types'; diff --git a/plugins/auth-node/src/passport/types.ts b/plugins/auth-node/src/passport/types.ts new file mode 100644 index 0000000000..4a3f41b6b2 --- /dev/null +++ b/plugins/auth-node/src/passport/types.ts @@ -0,0 +1,29 @@ +/* + * 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 { Profile } from 'passport'; + +/** @public */ +export type PassportProfile = Profile & { + avatarUrl?: string; +}; + +/** @public */ +export type PassportDoneCallback = ( + err?: Error, + result?: TResult, + privateInfo?: TPrivateInfo, +) => void; diff --git a/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts b/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts new file mode 100644 index 0000000000..8626c40b33 --- /dev/null +++ b/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts @@ -0,0 +1,61 @@ +/* + * 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 { + readDeclarativeSignInResolver, + SignInResolverFactory, +} from '../sign-in'; +import { + AuthProviderFactory, + ProfileTransform, + SignInResolver, +} from '../types'; +import { createProxyAuthRouteHandlers } from './createProxyRouteHandlers'; +import { ProxyAuthenticator } from './types'; + +/** @public */ +export function createProxyAuthProviderFactory(options: { + authenticator: ProxyAuthenticator; + profileTransform?: ProfileTransform; + signInResolver?: SignInResolver; + signInResolverFactories?: Record< + string, + SignInResolverFactory + >; +}): AuthProviderFactory { + return ctx => { + const signInResolver = + options.signInResolver ?? + readDeclarativeSignInResolver({ + config: ctx.config, + signInResolverFactories: options.signInResolverFactories ?? {}, + }); + + if (!signInResolver) { + throw new Error( + `No sign-in resolver configured for proxy auth provider '${ctx.providerId}'`, + ); + } + + return createProxyAuthRouteHandlers({ + signInResolver, + config: ctx.config, + authenticator: options.authenticator, + resolverContext: ctx.resolverContext, + profileTransform: options.profileTransform, + }); + }; +} diff --git a/plugins/auth-node/src/proxy/createProxyRouteHandlers.ts b/plugins/auth-node/src/proxy/createProxyRouteHandlers.ts new file mode 100644 index 0000000000..521fe39288 --- /dev/null +++ b/plugins/auth-node/src/proxy/createProxyRouteHandlers.ts @@ -0,0 +1,80 @@ +/* + * 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 { Request, Response } from 'express'; +import { Config } from '@backstage/config'; +import { + AuthProviderRouteHandlers, + AuthResolverContext, + ClientAuthResponse, + ProfileTransform, + SignInResolver, +} from '../types'; +import { ProxyAuthenticator } from './types'; +import { prepareBackstageIdentityResponse } from '../identity'; +import { NotImplementedError } from '@backstage/errors'; + +/** @public */ +export interface ProxyAuthRouteHandlersOptions { + authenticator: ProxyAuthenticator; + config: Config; + resolverContext: AuthResolverContext; + signInResolver: SignInResolver; + profileTransform?: ProfileTransform; +} + +/** @public */ +export function createProxyAuthRouteHandlers( + options: ProxyAuthRouteHandlersOptions, +): AuthProviderRouteHandlers { + const { authenticator, config, resolverContext, signInResolver } = options; + + const profileTransform = + options.profileTransform ?? authenticator.defaultProfileTransform; + const authenticatorCtx = authenticator.initialize({ config }); + + return { + async start(): Promise { + throw new NotImplementedError('Not implemented'); + }, + + async frameHandler(): Promise { + throw new NotImplementedError('Not implemented'); + }, + + async refresh(this: never, req: Request, res: Response): Promise { + const { result } = await authenticator.authenticate( + { req }, + authenticatorCtx, + ); + + const { profile } = await profileTransform(result, resolverContext); + + const identity = await signInResolver( + { profile, result }, + resolverContext, + ); + + const response: ClientAuthResponse<{}> = { + profile, + providerInfo: {}, + backstageIdentity: prepareBackstageIdentityResponse(identity), + }; + + res.status(200).json(response); + }, + }; +} diff --git a/plugins/auth-node/src/proxy/index.ts b/plugins/auth-node/src/proxy/index.ts new file mode 100644 index 0000000000..907f8829e5 --- /dev/null +++ b/plugins/auth-node/src/proxy/index.ts @@ -0,0 +1,22 @@ +/* + * 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. + */ + +export { createProxyAuthenticator, type ProxyAuthenticator } from './types'; +export { createProxyAuthProviderFactory } from './createProxyAuthProviderFactory'; +export { + createProxyAuthRouteHandlers, + type ProxyAuthRouteHandlersOptions, +} from './createProxyRouteHandlers'; diff --git a/plugins/auth-node/src/proxy/types.ts b/plugins/auth-node/src/proxy/types.ts new file mode 100644 index 0000000000..969b022abb --- /dev/null +++ b/plugins/auth-node/src/proxy/types.ts @@ -0,0 +1,36 @@ +/* + * 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 { Config } from '@backstage/config'; +import { Request } from 'express'; +import { ProfileTransform } from '../types'; + +/** @public */ +export interface ProxyAuthenticator { + defaultProfileTransform: ProfileTransform; + initialize(ctx: { config: Config }): Promise; + authenticate( + options: { req: Request }, + ctx: TContext, + ): Promise<{ result: TResult }>; +} + +/** @public */ +export function createProxyAuthenticator( + authenticator: ProxyAuthenticator, +): ProxyAuthenticator { + return authenticator; +} diff --git a/plugins/auth-node/src/sign-in/commonSignInResolvers.ts b/plugins/auth-node/src/sign-in/commonSignInResolvers.ts new file mode 100644 index 0000000000..5b5da07c73 --- /dev/null +++ b/plugins/auth-node/src/sign-in/commonSignInResolvers.ts @@ -0,0 +1,73 @@ +/* + * 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 { createSignInResolverFactory } from './createSignInResolverFactory'; + +/** + * A collection of common sign-in resolvers that work with any auth provider. + * + * @public + */ +export namespace commonSignInResolvers { + /** + * A common sign-in resolver that looks up the user using their email address + * as email of the entity. + */ + export const emailMatchingUserEntityProfileEmail = + createSignInResolverFactory({ + create() { + return async (info, ctx) => { + const { profile } = info; + + if (!profile.email) { + throw new Error( + 'Login failed, user profile does not contain an email', + ); + } + + return ctx.signInWithCatalogUser({ + filter: { + 'spec.profile.email': profile.email, + }, + }); + }; + }, + }); + + /** + * A common sign-in resolver that looks up the user using the local part of + * their email address as the entity name. + */ + export const emailLocalPartMatchingUserEntityName = + createSignInResolverFactory({ + create() { + return async (info, ctx) => { + const { profile } = info; + + if (!profile.email) { + throw new Error( + 'Login failed, user profile does not contain an email', + ); + } + const [localPart] = profile.email.split('@'); + + return ctx.signInWithCatalogUser({ + entityRef: { name: localPart }, + }); + }; + }, + }); +} diff --git a/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts b/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts new file mode 100644 index 0000000000..0632b704c3 --- /dev/null +++ b/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts @@ -0,0 +1,75 @@ +/* + * 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 { ZodSchema, ZodTypeDef } from 'zod'; +import { SignInResolver } from '../types'; +import zodToJsonSchema from 'zod-to-json-schema'; +import { JsonObject } from '@backstage/types'; +import { InputError } from '@backstage/errors'; + +/** @public */ +export interface SignInResolverFactory { + ( + ...options: undefined extends TOptions + ? [options?: TOptions] + : [options: TOptions] + ): SignInResolver; + optionsJsonSchema?: JsonObject; +} + +/** @public */ +export interface SignInResolverFactoryOptions< + TAuthResult, + TOptionsOutput, + TOptionsInput, +> { + optionsSchema?: ZodSchema; + create(options: TOptionsOutput): SignInResolver; +} + +/** @public */ +export function createSignInResolverFactory< + TAuthResult, + TOptionsOutput, + TOptionsInput, +>( + options: SignInResolverFactoryOptions< + TAuthResult, + TOptionsOutput, + TOptionsInput + >, +): SignInResolverFactory { + const { optionsSchema } = options; + if (!optionsSchema) { + return (resolverOptions?: TOptionsInput) => { + if (resolverOptions) { + throw new InputError('sign-in resolver does not accept options'); + } + return options.create(undefined as TOptionsOutput); + }; + } + const factory = ( + ...[resolverOptions]: undefined extends TOptionsInput + ? [options?: TOptionsInput] + : [options: TOptionsInput] + ) => { + const parsedOptions = optionsSchema.parse(resolverOptions); + return options.create(parsedOptions); + }; + + factory.optionsJsonSchema = zodToJsonSchema(optionsSchema) as JsonObject; + return factory; +} diff --git a/plugins/auth-node/src/sign-in/index.ts b/plugins/auth-node/src/sign-in/index.ts new file mode 100644 index 0000000000..736393a762 --- /dev/null +++ b/plugins/auth-node/src/sign-in/index.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +export { + createSignInResolverFactory, + type SignInResolverFactory, + type SignInResolverFactoryOptions, +} from './createSignInResolverFactory'; +export { + readDeclarativeSignInResolver, + type ReadDeclarativeSignInResolverOptions, +} from './readDeclarativeSignInResolver'; +export { commonSignInResolvers } from './commonSignInResolvers'; diff --git a/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts b/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts new file mode 100644 index 0000000000..82b4918326 --- /dev/null +++ b/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts @@ -0,0 +1,71 @@ +/* + * 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 { Config } from '@backstage/config'; +import { JsonObject } from '@backstage/types'; +import { SignInResolver } from '../types'; +import { SignInResolverFactory } from './createSignInResolverFactory'; + +/** @public */ +export interface ReadDeclarativeSignInResolverOptions { + config: Config; + signInResolverFactories: { + [name in string]: SignInResolverFactory; + }; +} + +/** @public */ +export function readDeclarativeSignInResolver( + options: ReadDeclarativeSignInResolverOptions, +): SignInResolver | undefined { + const resolvers = + options.config + .getOptionalConfigArray('signIn.resolvers') + ?.map(resolverConfig => { + const resolverName = resolverConfig.getString('resolver'); + if (!Object.hasOwn(options.signInResolverFactories, resolverName)) { + throw new Error( + `Sign-in resolver '${resolverName}' is not available`, + ); + } + const resolver = options.signInResolverFactories[resolverName]; + const { resolver: _ignored, ...resolverOptions } = + resolverConfig.get(); + + return resolver( + Object.keys(resolverOptions).length > 0 ? resolverOptions : undefined, + ); + }) ?? []; + + if (resolvers.length === 0) { + return undefined; + } + + return async (profile, context) => { + for (const resolver of resolvers) { + try { + return await resolver(profile, context); + } catch (error) { + if (error?.name === 'NotFoundError') { + continue; + } + throw error; + } + } + + throw new Error('Failed to sign-in, unable to resolve user identity'); + }; +} diff --git a/plugins/auth-node/src/types.ts b/plugins/auth-node/src/types.ts index 9aec98a372..d52abf00fb 100644 --- a/plugins/auth-node/src/types.ts +++ b/plugins/auth-node/src/types.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import { Request } from 'express'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { EntityFilterQuery } from '@backstage/catalog-client'; +import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { JsonValue } from '@backstage/types'; +import { Request, Response } from 'express'; /** * A representation of a successful Backstage sign-in. @@ -31,15 +36,6 @@ export interface BackstageSignInResult { token: string; } -/** - * Options to request the identity from a Backstage backend request - * - * @public - */ -export type IdentityApiGetIdentityRequest = { - request: Request; -}; - /** * Response object containing the {@link BackstageUserIdentity} and the token * from the authentication provider. @@ -76,3 +72,301 @@ export type BackstageUserIdentity = { */ ownershipEntityRefs: string[]; }; + +/** + * A query for a single user in the catalog. + * + * If `entityRef` is used, the default kind is `'User'`. + * + * If `annotations` are used, all annotations must be present and + * match the provided value exactly. Only entities of kind `'User'` will be considered. + * + * If `filter` are used they are passed on as they are to the `CatalogApi`. + * + * Regardless of the query method, the query must match exactly one entity + * in the catalog, or an error will be thrown. + * + * @public + */ +export type AuthResolverCatalogUserQuery = + | { + entityRef: + | string + | { + kind?: string; + namespace?: string; + name: string; + }; + } + | { + annotations: Record; + } + | { + filter: EntityFilterQuery; + }; + +/** + * Parameters used to issue new Backstage Tokens + * + * @public + */ +export type TokenParams = { + /** + * The claims that will be embedded within the token. At a minimum, this should include + * the subject claim, `sub`. It is common to also list entity ownership relations in the + * `ent` list. Additional claims may also be added at the developer's discretion except + * for the following list, which will be overwritten by the TokenIssuer: `iss`, `aud`, + * `iat`, and `exp`. The Backstage team also maintains the right add new claims in the future + * without listing the change as a "breaking change". + */ + claims: { + /** The token subject, i.e. User ID */ + sub: string; + /** A list of entity references that the user claims ownership through */ + ent?: string[]; + } & Record; +}; + +/** + * The context that is used for auth processing. + * + * @public + */ +export type AuthResolverContext = { + /** + * Issues a Backstage token using the provided parameters. + */ + issueToken(params: TokenParams): Promise<{ token: string }>; + + /** + * Finds a single user in the catalog using the provided query. + * + * See {@link AuthResolverCatalogUserQuery} for details. + */ + findCatalogUser( + query: AuthResolverCatalogUserQuery, + ): Promise<{ entity: Entity }>; + + /** + * Finds a single user in the catalog using the provided query, and then + * issues an identity for that user using default ownership resolution. + * + * See {@link AuthResolverCatalogUserQuery} for details. + */ + signInWithCatalogUser( + query: AuthResolverCatalogUserQuery, + ): Promise; +}; + +/** + * Any Auth provider needs to implement this interface which handles the routes in the + * auth backend. Any auth API requests from the frontend reaches these methods. + * + * The routes in the auth backend API are tied to these methods like below + * + * `/auth/[provider]/start -> start` + * `/auth/[provider]/handler/frame -> frameHandler` + * `/auth/[provider]/refresh -> refresh` + * `/auth/[provider]/logout -> logout` + * + * @public + */ +export interface AuthProviderRouteHandlers { + /** + * Handles the start route of the API. This initiates a sign in request with an auth provider. + * + * Request + * - scopes for the auth request (Optional) + * Response + * - redirect to the auth provider for the user to sign in or consent. + * - sets a nonce cookie and also pass the nonce as 'state' query parameter in the redirect request + */ + start(req: Request, res: Response): Promise; + + /** + * Once the user signs in or consents in the OAuth screen, the auth provider redirects to the + * callbackURL which is handled by this method. + * + * Request + * - to contain a nonce cookie and a 'state' query parameter + * Response + * - postMessage to the window with a payload that contains accessToken, expiryInSeconds?, idToken? and scope. + * - sets a refresh token cookie if the auth provider supports refresh tokens + */ + frameHandler(req: Request, res: Response): Promise; + + /** + * (Optional) If the auth provider supports refresh tokens then this method handles + * requests to get a new access token. + * + * Other types of providers may also use this method to implement its own logic to create new sessions + * upon request. For example, this can be used to create a new session for a provider that handles requests + * from an authenticating proxy. + * + * Request + * - to contain a refresh token cookie and scope (Optional) query parameter. + * Response + * - payload with accessToken, expiryInSeconds?, idToken?, scope and user profile information. + */ + refresh?(req: Request, res: Response): Promise; + + /** + * (Optional) Handles sign out requests + * + * Response + * - removes the refresh token cookie + */ + logout?(req: Request, res: Response): Promise; +} + +/** + * @public + * @deprecated Use top-level properties passed to `AuthProviderFactory` instead + */ +export type AuthProviderConfig = { + /** + * The protocol://domain[:port] where the app is hosted. This is used to construct the + * callbackURL to redirect to once the user signs in to the auth provider. + */ + baseUrl: string; + + /** + * The base URL of the app as provided by app.baseUrl + */ + appUrl: string; + + /** + * A function that is called to check whether an origin is allowed to receive the authentication result. + */ + isOriginAllowed: (origin: string) => boolean; + + /** + * The function used to resolve cookie configuration based on the auth provider options. + */ + cookieConfigurer?: CookieConfigurer; +}; + +/** @public */ +export type AuthProviderFactory = (options: { + providerId: string; + /** @deprecated Use top-level properties instead */ + globalConfig: AuthProviderConfig; + config: Config; + logger: LoggerService; + resolverContext: AuthResolverContext; + /** + * The protocol://domain[:port] where the app is hosted. This is used to construct the + * callbackURL to redirect to once the user signs in to the auth provider. + */ + baseUrl: string; + + /** + * The base URL of the app as provided by app.baseUrl + */ + appUrl: string; + + /** + * A function that is called to check whether an origin is allowed to receive the authentication result. + */ + isOriginAllowed: (origin: string) => boolean; + + /** + * The function used to resolve cookie configuration based on the auth provider options. + */ + cookieConfigurer?: CookieConfigurer; +}) => AuthProviderRouteHandlers; + +/** @public */ +export type ClientAuthResponse = { + providerInfo: TProviderInfo; + profile: ProfileInfo; + backstageIdentity?: BackstageIdentityResponse; +}; + +/** + * Type of sign in information context. Includes the profile information and + * authentication result which contains auth related information. + * + * @public + */ +export type SignInInfo = { + /** + * The simple profile passed down for use in the frontend. + */ + profile: ProfileInfo; + + /** + * The authentication result that was received from the authentication + * provider. + */ + result: TAuthResult; +}; + +/** + * Describes the function which handles the result of a successful + * authentication. Must return a valid {@link @backstage/plugin-auth-node#BackstageSignInResult}. + * + * @public + */ +export type SignInResolver = ( + info: SignInInfo, + context: AuthResolverContext, +) => Promise; + +/** + * Describes the function that transforms the result of a successful + * authentication into a {@link ProfileInfo} object. + * + * This function may optionally throw an error in order to reject authentication. + * + * @public + */ +export type ProfileTransform = ( + result: TResult, + context: AuthResolverContext, +) => Promise<{ profile: ProfileInfo }>; + +/** + * Used to display login information to user, i.e. sidebar popup. + * + * It is also temporarily used as the profile of the signed-in user's Backstage + * identity, but we want to replace that with data from identity and/org catalog + * service + * + * @public + */ +export type ProfileInfo = { + /** + * Email ID of the signed in user. + */ + email?: string; + /** + * Display name that can be presented to the signed in user. + */ + displayName?: string; + /** + * URL to an image that can be used as the display image or avatar of the + * signed in user. + */ + picture?: string; +}; + +/** + * The callback used to resolve the cookie configuration for auth providers that use cookies. + * @public + */ +export type CookieConfigurer = (ctx: { + /** ID of the auth provider that this configuration applies to */ + providerId: string; + /** The externally reachable base URL of the auth-backend plugin */ + baseUrl: string; + /** The configured callback URL of the auth provider */ + callbackUrl: string; + /** The origin URL of the app */ + appOrigin: string; +}) => { + domain: string; + path: string; + secure: boolean; + sameSite?: 'none' | 'lax' | 'strict'; +}; diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 8aa39659fa..fd9f614d75 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-azure-devops-backend +## 0.3.27 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.3.27-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-devops-common@0.3.0 + ## 0.3.27-next.0 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 706274df6d..6868de34fd 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.3.27-next.0", + "version": "0.3.27", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops-backend/src/plugin.ts b/plugins/azure-devops-backend/src/plugin.ts index 808bdfa248..ea0976a1f2 100644 --- a/plugins/azure-devops-backend/src/plugin.ts +++ b/plugins/azure-devops-backend/src/plugin.ts @@ -31,7 +31,7 @@ export const azureDevOpsPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, reader: coreServices.urlReader, httpRouter: coreServices.httpRouter, diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 1b405ad870..abf3c3e146 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-azure-devops +## 0.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-azure-devops-common@0.3.0 + +## 0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.3.3-next.0 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index daaedae825..96674d2cf2 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.3.3-next.0", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites-backend/CHANGELOG.md b/plugins/azure-sites-backend/CHANGELOG.md index 6aa958a860..6e6a662283 100644 --- a/plugins/azure-sites-backend/CHANGELOG.md +++ b/plugins/azure-sites-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-azure-sites-backend +## 0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-azure-sites-common@0.1.0 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/azure-sites-backend/package.json b/plugins/azure-sites-backend/package.json index 81d6c434a6..5b6f7ea83f 100644 --- a/plugins/azure-sites-backend/package.json +++ b/plugins/azure-sites-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites-backend", - "version": "0.1.10-next.0", + "version": "0.1.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites/CHANGELOG.md b/plugins/azure-sites/CHANGELOG.md index 636c69c76a..6f043950c6 100644 --- a/plugins/azure-sites/CHANGELOG.md +++ b/plugins/azure-sites/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-azure-sites +## 0.1.10 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-azure-sites-common@0.1.0 + +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json index 82b1bd1041..a4e50cccea 100644 --- a/plugins/azure-sites/package.json +++ b/plugins/azure-sites/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites", - "version": "0.1.10-next.0", + "version": "0.1.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 03ba2fa42b..2557b7469a 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-badges-backend +## 0.2.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.2.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.2.3-next.0 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 02f20826f2..f7acb9308f 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.2.3-next.0", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges-backend/src/plugin.ts b/plugins/badges-backend/src/plugin.ts index ecc03f0c15..3e462e88e9 100644 --- a/plugins/badges-backend/src/plugin.ts +++ b/plugins/badges-backend/src/plugin.ts @@ -31,7 +31,7 @@ export const badgesPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, discovery: coreServices.discovery, tokenManager: coreServices.tokenManager, diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 5d7527fba4..60e1ecc1be 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-badges +## 0.2.45 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.2.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.2.45-next.0 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 79add2944c..9146f64de5 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.45-next.0", + "version": "0.2.45", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index 15059fe6df..2cd3f586c2 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-bazaar-backend +## 0.2.11 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index d41127a9fa..d97267faf8 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.2.11-next.0", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/src/plugin.ts b/plugins/bazaar-backend/src/plugin.ts index 62622717c8..1c06bda705 100644 --- a/plugins/bazaar-backend/src/plugin.ts +++ b/plugins/bazaar-backend/src/plugin.ts @@ -31,7 +31,7 @@ export const bazaarPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, database: coreServices.database, identity: coreServices.identity, logger: coreServices.logger, diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 7168eaec27..b6bae0f5ed 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-bazaar +## 0.2.12 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli@0.22.10 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.2.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-catalog@1.12.1-next.2 + - @backstage/cli@0.22.10-next.1 + +## 0.2.12-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/cli@0.22.10-next.1 + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.2.12-next.0 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index 3181e99833..34d7490554 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.2.12-next.0", + "version": "0.2.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index d438d843fd..f1a842f892 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.2.9 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.6.0 + +## 0.2.9-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/integration@1.5.1 + ## 0.2.8 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 38ba256c35..2bc9fc3297 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", "description": "Common functionalities for bitbucket-cloud plugins", - "version": "0.2.8", + "version": "0.2.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index db09305c36..c44557a51c 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-bitrise +## 0.1.48 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.1.48-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.1.48-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.1.48-next.0 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 5388bb3542..6d63cb6384 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.48-next.0", + "version": "0.1.48", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 560e1cafb7..b2399d568f 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.2.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- b4222908b0c3: Added option to configure AWS `accountId` in `AwsS3EntityProvider` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-kubernetes-common@0.6.5 + +## 0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.2.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- b4222908b0c3: Added option to configure AWS `accountId` in `AwsS3EntityProvider` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-kubernetes-common@0.6.5 + ## 0.2.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index d27587a6b3..e593fcf90a 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.2.3-next.0", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.test.ts b/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.test.ts index 1475da9e79..c9fea8aa28 100644 --- a/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.test.ts +++ b/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.test.ts @@ -15,13 +15,15 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Duration } from 'luxon'; import { catalogModuleAwsS3EntityProvider } from './catalogModuleAwsS3EntityProvider'; @@ -45,7 +47,7 @@ describe('catalogModuleAwsS3EntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { awsS3: { @@ -57,16 +59,24 @@ describe('catalogModuleAwsS3EntityProvider', () => { }, }, }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleAwsS3EntityProvider(), + mockServices.rootConfig.factory({ data: config }), + createServiceFactory(() => ({ + service: coreServices.logger, + deps: {}, + factory: getVoidLogger, + }))(), + createServiceFactory(() => ({ + service: coreServices.scheduler, + deps: {}, + factory: () => scheduler, + }))(), ], - features: [catalogModuleAwsS3EntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); diff --git a/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts b/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts index 452284e526..521d61d3d8 100644 --- a/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts +++ b/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts @@ -33,7 +33,7 @@ export const catalogModuleAwsS3EntityProvider = createBackendModule({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, catalog: catalogProcessingExtensionPoint, logger: coreServices.logger, scheduler: coreServices.scheduler, diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 9d60b4a5f2..73b5489600 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.19 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.1.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.1.19-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index ac3cdd2b05..eb6a3eadf5 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.19-next.0", + "version": "0.1.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.test.ts b/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.test.ts index fa3b3d865e..3147a0871b 100644 --- a/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.test.ts +++ b/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Duration } from 'luxon'; import { catalogModuleAzureDevOpsEntityProvider } from './catalogModuleAzureDevOpsEntityProvider'; @@ -45,7 +46,7 @@ describe('catalogModuleAzureDevOpsEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { azureDevOps: { @@ -60,16 +61,20 @@ describe('catalogModuleAzureDevOpsEntityProvider', () => { }, }, }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleAzureDevOpsEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + mockServices.logger.factory(), + createServiceFactory(() => ({ + deps: {}, + service: coreServices.scheduler, + factory: async () => scheduler, + })), ], - features: [catalogModuleAzureDevOpsEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); diff --git a/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts b/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts index ec8b6441ba..804c0d4712 100644 --- a/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts +++ b/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts @@ -33,7 +33,7 @@ export const catalogModuleAzureDevOpsEntityProvider = createBackendModule({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, catalog: catalogProcessingExtensionPoint, logger: coreServices.logger, scheduler: coreServices.scheduler, diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 4aeca8609e..7503ee898e 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,54 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.15 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.9 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.1.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## 0.1.15-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.9-next.0 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 1baa14f00f..42df54688b 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.15-next.0", + "version": "0.1.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", 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 a34eae3750..83769fc3f5 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 @@ -14,7 +14,10 @@ * limitations under the License. */ -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, @@ -55,8 +58,9 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => { [catalogProcessingExtensionPoint, catalogExtensionPointImpl], [eventsExtensionPoint, eventsExtensionPointImpl], ], - services: [ - mockServices.config.factory({ + features: [ + catalogModuleBitbucketCloudEntityProvider(), + mockServices.rootConfig.factory({ data: { catalog: { providers: { @@ -71,9 +75,12 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => { }, }, }), - [coreServices.scheduler, scheduler], + createServiceFactory({ + service: coreServices.scheduler, + deps: {}, + factory: async () => scheduler, + }), ], - features: [catalogModuleBitbucketCloudEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); 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 7c4f4a30b2..e5e1ad1609 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts @@ -37,7 +37,7 @@ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({ deps: { catalog: catalogProcessingExtensionPoint, catalogApi: catalogServiceRef, - config: coreServices.config, + config: coreServices.rootConfig, // TODO(pjungermann): How to make this optional for those which only want the provider without event support? // Do we even want to support this? events: eventsExtensionPoint, diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index fa0c443e3a..1b5a65f254 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.13 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.1.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.1.13-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 4b35c8f7d7..7e488e31ff 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.13-next.0", + "version": "0.1.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts index d17b4a4c96..ae4a636008 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/config'; -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { catalogModuleBitbucketServerEntityProvider } from './catalogModuleBitbucketServerEntityProvider'; import { Duration } from 'luxon'; @@ -45,7 +46,7 @@ describe('catalogModuleBitbucketServerEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { bitbucketServer: { @@ -64,16 +65,20 @@ describe('catalogModuleBitbucketServerEntityProvider', () => { }, ], }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleBitbucketServerEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + mockServices.logger.factory(), + createServiceFactory({ + service: coreServices.scheduler, + deps: {}, + factory: async () => scheduler, + }), ], - features: [catalogModuleBitbucketServerEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts index 354bce7d6d..ef71df9b06 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts @@ -32,7 +32,7 @@ export const catalogModuleBitbucketServerEntityProvider = createBackendModule({ env.registerInit({ deps: { catalog: catalogProcessingExtensionPoint, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, scheduler: coreServices.scheduler, }, diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index 9c1da14fba..ef448f6b20 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.15 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.9 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.2.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.9-next.0 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.2.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index e968aad343..45ca64dc71 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.15-next.0", + "version": "0.2.15", "deprecated": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md new file mode 100644 index 0000000000..c30f91fa61 --- /dev/null +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -0,0 +1,45 @@ +# @backstage/plugin-catalog-backend-module-gcp + +## 0.1.0 + +### Minor Changes + +- 290eff6692aa: Added GCP catalog plugin with GKE provider + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/plugin-kubernetes-common@0.6.5 + +## 0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.1.0-next.0 + +### Minor Changes + +- 290eff6692aa: Added GCP catalog plugin with GKE provider + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-kubernetes-common@0.6.5 diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 12285d26eb..dcff6662dd 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", "description": "A Backstage catalog backend module that helps integrate towards GCP", - "version": "0.0.0", + "version": "0.1.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts b/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts index c13c6cbe0e..7f93bddbfe 100644 --- a/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts +++ b/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts @@ -33,7 +33,7 @@ export const catalogModuleGcpGkeEntityProvider = createBackendModule({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, catalog: catalogProcessingExtensionPoint, logger: coreServices.logger, scheduler: coreServices.scheduler, diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 09df93f029..3bb53b864b 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.16 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.1.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.1.16-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.1.16-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index aa0fc95907..99e9ed5e12 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.16-next.0", + "version": "0.1.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.test.ts b/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.test.ts index 0511aa7b4c..9807814f61 100644 --- a/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Duration } from 'luxon'; import { catalogModuleGerritEntityProvider } from './catalogModuleGerritEntityProvider'; @@ -45,7 +46,7 @@ describe('catalogModuleGerritEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { gerrit: { @@ -70,16 +71,20 @@ describe('catalogModuleGerritEntityProvider', () => { }, ], }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleGerritEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + mockServices.logger.factory(), + createServiceFactory({ + service: coreServices.scheduler, + deps: {}, + factory: async () => scheduler, + }), ], - features: [catalogModuleGerritEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); diff --git a/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts b/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts index 814259fb40..bb29ce6eef 100644 --- a/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts +++ b/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts @@ -32,7 +32,7 @@ export const catalogModuleGerritEntityProvider = createBackendModule({ env.registerInit({ deps: { catalog: catalogProcessingExtensionPoint, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, scheduler: coreServices.scheduler, }, diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 3743f035c4..4d14c2ce5a 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,60 @@ # @backstage/plugin-catalog-backend-module-github +## 0.3.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- 81c231c9c9ee: Fixed a bug where the visibility filter was case sensitive and casting was inconsistent. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## 0.3.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + ## 0.3.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 2e64d39638..101c0f54f0 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.3.3-next.0", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/src/lib/util.ts b/plugins/catalog-backend-module-github/src/lib/util.ts index bb91e9dd2a..58ae8d6551 100644 --- a/plugins/catalog-backend-module-github/src/lib/util.ts +++ b/plugins/catalog-backend-module-github/src/lib/util.ts @@ -107,5 +107,10 @@ export function satisfiesVisibilityFilter( if (!visibilities.length) { return true; } - return visibilities.includes(visibility); + const lowerCaseVisibilities = visibilities.map(v => + v.toLocaleLowerCase('en-US'), + ); + const lowerCaseVisibility = visibility.toLocaleLowerCase('en-US'); + + return lowerCaseVisibilities.includes(lowerCaseVisibility); } diff --git a/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.test.ts b/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.test.ts index 82a5552dba..9e36966eec 100644 --- a/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.test.ts @@ -14,18 +14,19 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Duration } from 'luxon'; import { catalogModuleGithubEntityProvider } from './catalogModuleGithubEntityProvider'; import { GithubEntityProvider } from '../providers/GithubEntityProvider'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; describe('catalogModuleGithubEntityProvider', () => { it('should register provider at the catalog extension point', async () => { @@ -45,7 +46,7 @@ describe('catalogModuleGithubEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { github: { @@ -57,16 +58,19 @@ describe('catalogModuleGithubEntityProvider', () => { }, }, }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleGithubEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + createServiceFactory({ + service: coreServices.scheduler, + deps: {}, + factory: async () => scheduler, + }), ], - features: [catalogModuleGithubEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); diff --git a/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts b/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts index 997da3023b..8e052bd0a0 100644 --- a/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts @@ -34,7 +34,7 @@ export const catalogModuleGithubEntityProvider = createBackendModule({ env.registerInit({ deps: { catalog: catalogProcessingExtensionPoint, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, scheduler: coreServices.scheduler, }, diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 2fcba33d47..0f860bb050 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.2.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- e6c721439f37: Added option to skip forked repos in GitlabDiscoveryEntityProvider +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- 2fe1f5973ff7: Filter Gitlab archived projects through APIs +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.2.4-next.2 + +### Patch Changes + +- 2fe1f5973ff7: Filter Gitlab archived projects through APIs +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.2.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- e6c721439f37: Added option to skip forked repos in GitlabDiscoveryEntityProvider +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.2.4-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 1a3f0245eb..3f31ffdf1d 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.2.4-next.0", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts index 6333184ea8..a56c229105 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts @@ -56,6 +56,7 @@ function setupFakeServer( listProjectsCallback: (request: { page: number; include_subgroups: boolean; + archived: boolean; }) => { data: GitLabProject[]; nextPage?: number; @@ -74,20 +75,24 @@ function setupFakeServer( } const page = req.url.searchParams.get('page'); const include_subgroups = req.url.searchParams.get('include_subgroups'); + const archived = req.url.searchParams.get('archived'); const response = listProjectsCallback({ page: parseInt(page!, 10), include_subgroups: include_subgroups === 'true', + archived: archived === 'true', }); // Filter the fake results based on the `last_activity_after` parameter const last_activity_after = req.url.searchParams.get( 'last_activity_after', ); - const filteredData = response.data.filter( - v => - !last_activity_after || - Date.parse(v.last_activity_at) >= Date.parse(last_activity_after), - ); + const filteredData = response.data + .filter( + v => + !last_activity_after || + Date.parse(v.last_activity_at) >= Date.parse(last_activity_after), + ) + .filter(v => archived || !v.archived); return res( ctx.set('x-next-page', response.nextPage?.toString() ?? ''), @@ -250,6 +255,7 @@ describe('GitlabDiscoveryProcessor', () => { }, ], }; + default: throw new Error('Invalid request'); } diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts index c9bd4d9187..02b1b1cb51 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts @@ -109,6 +109,7 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { const lastActivity = (await this.cache.get(this.getCacheKey())) as string; const opts = { + archived: false, group, page: 1, // We check for the existence of lastActivity and only set it if it's present to ensure @@ -125,10 +126,6 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { for await (const project of projects) { res.scanned++; - if (project.archived) { - continue; - } - if (branch === '*' && project.default_branch === undefined) { continue; } diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index 52829c343b..006a345c4a 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -35,6 +35,7 @@ export type CommonListOptions = { }; interface ListProjectOptions extends CommonListOptions { + archived?: boolean; group?: string; } 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 7a3d0a0e0d..22759cafa0 100644 --- a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Duration } from 'luxon'; import { catalogModuleGitlabDiscoveryEntityProvider } from './catalogModuleGitlabDiscoveryEntityProvider'; @@ -45,7 +46,7 @@ describe('catalogModuleGitlabDiscoveryEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { integrations: { gitlab: [ { @@ -69,16 +70,20 @@ describe('catalogModuleGitlabDiscoveryEntityProvider', () => { }, }, }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleGitlabDiscoveryEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + mockServices.logger.factory(), + createServiceFactory({ + deps: {}, + service: coreServices.scheduler, + factory: async () => scheduler, + }), ], - features: [catalogModuleGitlabDiscoveryEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M')); diff --git a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts index ffae233319..d192e8f3a5 100644 --- a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts @@ -33,7 +33,7 @@ export const catalogModuleGitlabDiscoveryEntityProvider = createBackendModule({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, catalog: catalogProcessingExtensionPoint, logger: coreServices.logger, scheduler: coreServices.scheduler, diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 08d2fe48a7..ce7a304746 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -155,6 +155,7 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { const projects = paginated( options => client.listProjects(options), { + archived: false, group: this.config.group, page: 1, per_page: 50, @@ -173,10 +174,6 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { res.scanned++; - if (project.archived) { - continue; - } - if ( this.config.skipForkedRepos && project.hasOwnProperty('forked_from_project') diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 583458667d..7fa5ac3c11 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.4.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- e2b6396a1274: Export new alpha `incrementalIngestionProvidersExtensionPoint` for registering incremental providers, rather than the providers being passed as options to the backend module. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## 0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + +## 0.4.1-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + ## 0.4.1-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/alpha-api-report.md b/plugins/catalog-backend-module-incremental-ingestion/alpha-api-report.md index b11ec43cf5..995c6e4893 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/alpha-api-report.md +++ b/plugins/catalog-backend-module-incremental-ingestion/alpha-api-report.md @@ -4,16 +4,23 @@ ```ts 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 -export const catalogModuleIncrementalIngestionEntityProvider: (options: { - providers: { - provider: IncrementalEntityProvider; +export const catalogModuleIncrementalIngestionEntityProvider: () => BackendFeature; + +// @alpha +export interface IncrementalIngestionProviderExtensionPoint { + addProvider(config: { options: IncrementalEntityProviderOptions; - }[]; -}) => BackendFeature; + provider: IncrementalEntityProvider; + }): void; +} + +// @alpha +export const incrementalIngestionProvidersExtensionPoint: ExtensionPoint; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index a6a947c93c..30a64c20b2 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", "description": "An entity provider for streaming large asset sources into the catalog", - "version": "0.4.1-next.0", + "version": "0.4.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.ts index 4738893c95..cc2954ea05 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/WrapperProviders.ts @@ -15,7 +15,7 @@ */ import { - ConfigService, + RootConfigService, LoggerService, SchedulerService, } from '@backstage/backend-plugin-api'; @@ -49,7 +49,7 @@ export class WrapperProviders { constructor( private readonly options: { - config: ConfigService; + config: RootConfigService; logger: LoggerService; client: Knex; scheduler: SchedulerService; diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts index ef07b4cb10..ed0c776fa8 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts @@ -14,13 +14,18 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createBackendModule, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { IncrementalEntityProvider } from '../types'; -import { catalogModuleIncrementalIngestionEntityProvider } from './catalogModuleIncrementalIngestionEntityProvider'; +import { + catalogModuleIncrementalIngestionEntityProvider, + incrementalIngestionProvidersExtensionPoint, +} from './catalogModuleIncrementalIngestionEntityProvider'; describe('catalogModuleIncrementalIngestionEntityProvider', () => { it('should register provider at the catalog extension point', async () => { @@ -37,37 +42,35 @@ describe('catalogModuleIncrementalIngestionEntityProvider', () => { const addEntityProvider = jest.fn(); const httpRouterUse = jest.fn(); - const scheduler = {}; - const database = { - getClient: jest.fn(), - }; - const httpRouter = { - use: httpRouterUse, - }; - await startTestBackend({ extensionPoints: [ [catalogProcessingExtensionPoint, { addEntityProvider }], ], - services: [ - [coreServices.config, new ConfigReader({})], - [coreServices.database, database], - [coreServices.httpRouter, httpRouter], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], - ], features: [ - catalogModuleIncrementalIngestionEntityProvider({ - providers: [ - { - provider: provider1, - options: { - burstInterval: { seconds: 1 }, - burstLength: { seconds: 1 }, - restLength: { seconds: 1 }, + createServiceFactory({ + service: coreServices.httpRouter, + deps: {}, + factory: () => ({ use: httpRouterUse }), + }), + catalogModuleIncrementalIngestionEntityProvider(), + createBackendModule({ + pluginId: 'catalog', + moduleId: 'incrementalTest', + register(env) { + env.registerInit({ + deps: { extension: incrementalIngestionProvidersExtensionPoint }, + async init({ extension }) { + extension.addProvider({ + provider: provider1, + options: { + burstInterval: { seconds: 1 }, + burstLength: { seconds: 1 }, + restLength: { seconds: 1 }, + }, + }); }, - }, - ], + }); + }, }), ], }); diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts index 19d5742b46..4e94088ef6 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts @@ -17,6 +17,7 @@ import { coreServices, createBackendModule, + createExtensionPoint, } from '@backstage/backend-plugin-api'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { @@ -25,56 +26,111 @@ import { } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; import { WrapperProviders } from './WrapperProviders'; +/** + * @alpha + * Interface for {@link incrementalIngestionProvidersExtensionPoint}. + */ +export interface IncrementalIngestionProviderExtensionPoint { + /** Adds a new incremental entity provider */ + addProvider(config: { + options: IncrementalEntityProviderOptions; + provider: IncrementalEntityProvider; + }): void; +} + +/** + * @alpha + * + * Extension point for registering incremental ingestion providers. + * The `catalogModuleIncrementalIngestionEntityProvider` must be installed for these providers to work. + * + * @example + * + * ```ts + * backend.add(createBackendModule({ + * pluginId: 'catalog', + * moduleId: 'myIncrementalProvider', + * register(env) { + * env.registerInit({ + * deps: { + * extension: incrementalIngestionProvidersExtensionPoint, + * }, + * async init({ extension }) { + * extension.addProvider({ + * burstInterval: ..., + * burstLength: ..., + * restLength: ..., + * }, { + * next(context, cursor) { + * ... + * }, + * ... + * }) + * }) + * }) + * } + * })) + * ``` + */ +export const incrementalIngestionProvidersExtensionPoint = + createExtensionPoint({ + id: 'catalog.incrementalIngestionProvider.providers', + }); + /** * Registers the incremental entity provider with the catalog processing extension point. * * @alpha */ export const catalogModuleIncrementalIngestionEntityProvider = - createBackendModule( - (options: { - providers: Array<{ + createBackendModule({ + pluginId: 'catalog', + moduleId: 'incrementalIngestionEntityProvider', + register(env) { + const addedProviders = new Array<{ provider: IncrementalEntityProvider; options: IncrementalEntityProviderOptions; - }>; - }) => ({ - pluginId: 'catalog', - moduleId: 'incrementalIngestionEntityProvider', - register(env) { - env.registerInit({ - deps: { - catalog: catalogProcessingExtensionPoint, - config: coreServices.config, - database: coreServices.database, - httpRouter: coreServices.httpRouter, - logger: coreServices.logger, - scheduler: coreServices.scheduler, - }, - async init({ - catalog, + }>(); + + env.registerExtensionPoint(incrementalIngestionProvidersExtensionPoint, { + addProvider({ options, provider }) { + addedProviders.push({ options, provider }); + }, + }); + + env.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + config: coreServices.rootConfig, + database: coreServices.database, + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + scheduler: coreServices.scheduler, + }, + async init({ + catalog, + config, + database, + httpRouter, + logger, + scheduler, + }) { + const client = await database.getClient(); + + const providers = new WrapperProviders({ config, - database, - httpRouter, logger, + client, scheduler, - }) { - const client = await database.getClient(); + }); - const providers = new WrapperProviders({ - config, - logger, - client, - scheduler, - }); + for (const entry of addedProviders) { + const wrapped = providers.wrap(entry.provider, entry.options); + catalog.addEntityProvider(wrapped); + } - for (const entry of options.providers) { - const wrapped = providers.wrap(entry.provider, entry.options); - catalog.addEntityProvider(wrapped); - } - - httpRouter.use(await providers.adminRouter()); - }, - }); - }, - }), - ); + httpRouter.use(await providers.adminRouter()); + }, + }); + }, + }); diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/index.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/index.ts index 9fcee99e01..37ecf60f7c 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/index.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/index.ts @@ -14,4 +14,8 @@ * limitations under the License. */ -export { catalogModuleIncrementalIngestionEntityProvider } from './catalogModuleIncrementalIngestionEntityProvider'; +export { + catalogModuleIncrementalIngestionEntityProvider, + incrementalIngestionProvidersExtensionPoint, + type IncrementalIngestionProviderExtensionPoint, +} from './catalogModuleIncrementalIngestionEntityProvider'; diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/run.ts b/plugins/catalog-backend-module-incremental-ingestion/src/run.ts index f94c988237..cfb4c20def 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/run.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/run.ts @@ -20,12 +20,16 @@ import { createBackend } from '@backstage/backend-defaults'; import { coreServices, + createBackendModule, createServiceFactory, } from '@backstage/backend-plugin-api'; import { ConfigReader } from '@backstage/config'; import { catalogPlugin } from '@backstage/plugin-catalog-backend/alpha'; import { IncrementalEntityProvider } from '.'; -import { catalogModuleIncrementalIngestionEntityProvider } from './alpha'; +import { + catalogModuleIncrementalIngestionEntityProvider, + incrementalIngestionProvidersExtensionPoint, +} from './alpha'; const provider: IncrementalEntityProvider = { getProviderName: () => 'test-provider', @@ -51,29 +55,36 @@ async function main() { }, }; - const backend = createBackend({ - services: [ - createServiceFactory({ - service: coreServices.config, - deps: {}, - factory: () => new ConfigReader(config), - }), - ], - }); + const backend = createBackend(); - backend.add(catalogPlugin()); backend.add( - catalogModuleIncrementalIngestionEntityProvider({ - providers: [ - { - provider: provider, - options: { - burstInterval: { seconds: 1 }, - burstLength: { seconds: 10 }, - restLength: { seconds: 10 }, + createServiceFactory({ + service: coreServices.rootConfig, + deps: {}, + factory: () => new ConfigReader(config), + }), + ); + backend.add(catalogPlugin()); + backend.add(catalogModuleIncrementalIngestionEntityProvider()); + backend.add( + createBackendModule({ + pluginId: 'catalog', + moduleId: 'incrementalTestProvider', + register(reg) { + reg.registerInit({ + deps: { extension: incrementalIngestionProvidersExtensionPoint }, + async init({ extension }) { + extension.addProvider({ + provider: provider, + options: { + burstInterval: { seconds: 1 }, + burstLength: { seconds: 10 }, + restLength: { seconds: 10 }, + }, + }); }, - }, - ], + }); + }, }), ); diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 4e9cabb577..9db8a14c9f 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.15 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.5.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.5.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + ## 0.5.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 742065735d..8d3a6277c3 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.15-next.0", + "version": "0.5.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 0edda3b91d..383ceffe0e 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.5.7 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- fb93323201bf: The alpha `catalogModuleMicrosoftGraphOrgEntityProvider` export no longer accepts options. Transformers are now instead configured via the `microsoftGraphOrgEntityProviderTransformExtensionPoint`. +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.5.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.5.7-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + ## 0.5.7-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/alpha-api-report.md b/plugins/catalog-backend-module-msgraph/alpha-api-report.md index 0fa093a1bc..6d877ba713 100644 --- a/plugins/catalog-backend-module-msgraph/alpha-api-report.md +++ b/plugins/catalog-backend-module-msgraph/alpha-api-report.md @@ -4,6 +4,7 @@ ```ts 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'; import { UserTransformer } from '@backstage/plugin-catalog-backend-module-msgraph'; @@ -12,12 +13,21 @@ import { UserTransformer } from '@backstage/plugin-catalog-backend-module-msgrap export const catalogModuleMicrosoftGraphOrgEntityProvider: () => BackendFeature; // @alpha -export interface CatalogModuleMicrosoftGraphOrgEntityProviderOptions { - groupTransformer?: GroupTransformer | Record; - organizationTransformer?: - | OrganizationTransformer - | Record; - userTransformer?: UserTransformer | Record; +export const microsoftGraphOrgEntityProviderTransformExtensionPoint: ExtensionPoint; + +// @alpha +export interface MicrosoftGraphOrgEntityProviderTransformsExtensionPoint { + setGroupTransformer( + transformer: GroupTransformer | Record, + ): void; + setOrganizationTransformer( + transformer: + | OrganizationTransformer + | Record, + ): void; + setUserTransformer( + transformer: UserTransformer | Record, + ): void; } // (No @packageDocumentation comment for this package) diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index c802b15d16..5d2afe69bf 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.5.7-next.0", + "version": "0.5.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.test.ts b/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.test.ts index 3362c75c95..96fec41cbe 100644 --- a/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.test.ts +++ b/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Duration } from 'luxon'; import { catalogModuleMicrosoftGraphOrgEntityProvider } from './catalogModuleMicrosoftGraphOrgEntityProvider'; @@ -45,7 +46,7 @@ describe('catalogModuleMicrosoftGraphOrgEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { microsoftGraphOrg: { @@ -62,16 +63,19 @@ describe('catalogModuleMicrosoftGraphOrgEntityProvider', () => { }, }, }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModuleMicrosoftGraphOrgEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + createServiceFactory(() => ({ + deps: {}, + service: coreServices.scheduler, + factory: async () => scheduler, + })), ], - features: [catalogModuleMicrosoftGraphOrgEntityProvider()], }); expect(usedSchedule?.frequency).toEqual(Duration.fromISO('PT30M')); diff --git a/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts index 7cd8b65f0b..b0d22ab0f4 100644 --- a/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts @@ -17,6 +17,7 @@ import { coreServices, createBackendModule, + createExtensionPoint, } from '@backstage/backend-plugin-api'; import { loggerToWinstonLogger } from '@backstage/backend-common'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; @@ -28,32 +29,50 @@ import { import { MicrosoftGraphOrgEntityProvider } from '../processors'; /** - * Options for {@link catalogModuleMicrosoftGraphOrgEntityProvider}. + * Interface for {@link microsoftGraphOrgEntityProviderTransformExtensionPoint}. * * @alpha */ -export interface CatalogModuleMicrosoftGraphOrgEntityProviderOptions { +export interface MicrosoftGraphOrgEntityProviderTransformsExtensionPoint { /** - * The function that transforms a user entry in msgraph to an entity. + * Set the function that transforms a user entry in msgraph to an entity. * Optionally, you can pass separate transformers per provider ID. */ - userTransformer?: UserTransformer | Record; + setUserTransformer( + transformer: UserTransformer | Record, + ): void; /** - * The function that transforms a group entry in msgraph to an entity. + * Set the function that transforms a group entry in msgraph to an entity. * Optionally, you can pass separate transformers per provider ID. */ - groupTransformer?: GroupTransformer | Record; + setGroupTransformer( + transformer: GroupTransformer | Record, + ): void; /** - * The function that transforms an organization entry in msgraph to an entity. + * Set the function that transforms an organization entry in msgraph to an entity. * Optionally, you can pass separate transformers per provider ID. */ - organizationTransformer?: - | OrganizationTransformer - | Record; + setOrganizationTransformer( + transformer: + | OrganizationTransformer + | Record, + ): void; } +/** + * Extension point used to customize the transforms used by the {@link catalogModuleMicrosoftGraphOrgEntityProvider}. + * + * @alpha + */ +export const microsoftGraphOrgEntityProviderTransformExtensionPoint = + createExtensionPoint( + { + id: 'catalog.microsoftGraphOrgEntityProvider.transforms', + }, + ); + /** * Registers the MicrosoftGraphOrgEntityProvider with the catalog processing extension point. * @@ -63,25 +82,59 @@ export const catalogModuleMicrosoftGraphOrgEntityProvider = createBackendModule( { pluginId: 'catalog', moduleId: 'microsoftGraphOrgEntityProvider', - register( - env, - options?: CatalogModuleMicrosoftGraphOrgEntityProviderOptions, - ) { + register(env) { + let userTransformer: + | UserTransformer + | Record + | undefined; + let groupTransformer: + | GroupTransformer + | Record + | undefined; + let organizationTransformer: + | OrganizationTransformer + | Record + | undefined; + + env.registerExtensionPoint( + microsoftGraphOrgEntityProviderTransformExtensionPoint, + { + setUserTransformer(transformer) { + if (userTransformer) { + throw new Error('User transformer may only be set once'); + } + userTransformer = transformer; + }, + setGroupTransformer(transformer) { + if (groupTransformer) { + throw new Error('Group transformer may only be set once'); + } + groupTransformer = transformer; + }, + setOrganizationTransformer(transformer) { + if (organizationTransformer) { + throw new Error('Organization transformer may only be set once'); + } + organizationTransformer = transformer; + }, + }, + ); + env.registerInit({ deps: { catalog: catalogProcessingExtensionPoint, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, scheduler: coreServices.scheduler, }, async init({ catalog, config, logger, scheduler }) { catalog.addEntityProvider( MicrosoftGraphOrgEntityProvider.fromConfig(config, { - groupTransformer: options?.groupTransformer, logger: loggerToWinstonLogger(logger), - organizationTransformer: options?.organizationTransformer, scheduler, - userTransformer: options?.userTransformer, + userTransformer: userTransformer, + groupTransformer: groupTransformer, + organizationTransformer: organizationTransformer, }), ); }, diff --git a/plugins/catalog-backend-module-msgraph/src/module/index.ts b/plugins/catalog-backend-module-msgraph/src/module/index.ts index f53f41141c..ef45c6a135 100644 --- a/plugins/catalog-backend-module-msgraph/src/module/index.ts +++ b/plugins/catalog-backend-module-msgraph/src/module/index.ts @@ -14,5 +14,8 @@ * limitations under the License. */ -export { catalogModuleMicrosoftGraphOrgEntityProvider } from './catalogModuleMicrosoftGraphOrgEntityProvider'; -export type { CatalogModuleMicrosoftGraphOrgEntityProviderOptions } from './catalogModuleMicrosoftGraphOrgEntityProvider'; +export { + catalogModuleMicrosoftGraphOrgEntityProvider, + microsoftGraphOrgEntityProviderTransformExtensionPoint, + type MicrosoftGraphOrgEntityProviderTransformsExtensionPoint, +} from './catalogModuleMicrosoftGraphOrgEntityProvider'; diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 8b6a4c3162..7a7bf51087 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.14 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.1.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + ## 0.1.14-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index cbc42851d9..f9c5fe88a1 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.14-next.0", + "version": "0.1.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index d7606911e3..31fbe07360 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.1.5 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.1.5-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 8adbd65941..9cd16fd3c3 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", - "version": "0.1.5-next.0", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.test.ts b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.test.ts index e2745e9268..3ea54d8c8f 100644 --- a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.test.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; -import { coreServices } from '@backstage/backend-plugin-api'; +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; import { PluginTaskScheduler, TaskScheduleDefinition, } from '@backstage/backend-tasks'; -import { startTestBackend } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { catalogModulePuppetDbEntityProvider } from './catalogModulePuppetDbEntityProvider'; import { PuppetDbEntityProvider } from '../providers/PuppetDbEntityProvider'; @@ -44,7 +45,7 @@ describe('catalogModulePuppetDbEntityProvider', () => { }, } as unknown as PluginTaskScheduler; - const config = new ConfigReader({ + const config = { catalog: { providers: { puppetdb: { @@ -56,16 +57,19 @@ describe('catalogModulePuppetDbEntityProvider', () => { }, }, }, - }); + }; await startTestBackend({ extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], - services: [ - [coreServices.config, config], - [coreServices.logger, getVoidLogger()], - [coreServices.scheduler, scheduler], + features: [ + catalogModulePuppetDbEntityProvider(), + mockServices.rootConfig.factory({ data: config }), + createServiceFactory(() => ({ + deps: {}, + service: coreServices.scheduler, + factory: async () => scheduler, + })), ], - features: [catalogModulePuppetDbEntityProvider()], }); expect(usedSchedule?.frequency).toEqual({ minutes: 10 }); diff --git a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts index 9a7d279cd3..5546b5283b 100644 --- a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts +++ b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts @@ -34,7 +34,7 @@ export const catalogModulePuppetDbEntityProvider = createBackendModule({ env.registerInit({ deps: { catalog: catalogProcessingExtensionPoint, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, scheduler: coreServices.scheduler, }, diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 999c7cf85f..96228acefd 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.2.0 + +### Minor Changes + +- 5156a94c2e2a: **BREAKING**: Fixing typo in exported module. You will have to rename the import to the correct spelling. `UnprocessedEntites` -> `UnprocessedEntities` + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-model@1.4.1 + +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.2.0-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-model@1.4.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index e9ca4662ac..8f303b9667 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", "description": "Backstage Catalog module to view unprocessed entities", - "version": "0.2.0-next.0", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 0c0f3c7be8..e1fd4e43ab 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,86 @@ # @backstage/plugin-catalog-backend +## 1.12.0 + +### Minor Changes + +- b8cccd8ee858: Support configuring applicable kinds for `AnnotateScmSlugEntityProcessor` +- f32252cdf631: Added OpenTelemetry spans for catalog processing +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- b8d6b22acd57: Internal refactor for load test +- Updated dependencies + - @backstage/plugin-search-backend-module-catalog@0.1.4 + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-openapi-utils@0.0.3 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-events-node@0.2.9 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 1.12.0-next.2 + +### Minor Changes + +- b8cccd8ee858: Support configuring applicable kinds for `AnnotateScmSlugEntityProcessor` + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-events-node@0.2.9-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 1.12.0-next.1 + +### Minor Changes + +- f32252cdf631: Added OpenTelemetry spans for catalog processing + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/plugin-search-backend-module-catalog@0.1.4-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-openapi-utils@0.0.3-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-events-node@0.2.9-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-search-common@1.2.5 + ## 1.12.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 84d8de4753..345874d919 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -91,9 +91,17 @@ export class AnnotateLocationEntityProcessor implements CatalogProcessor_2 { // @public (undocumented) export class AnnotateScmSlugEntityProcessor implements CatalogProcessor_2 { - constructor(opts: { scmIntegrationRegistry: ScmIntegrationRegistry }); + constructor(opts: { + scmIntegrationRegistry: ScmIntegrationRegistry; + kinds?: string[]; + }); // (undocumented) - static fromConfig(config: Config): AnnotateScmSlugEntityProcessor; + static fromConfig( + config: Config, + options?: { + kinds?: string[]; + }, + ): AnnotateScmSlugEntityProcessor; // (undocumented) getProcessorName(): string; // (undocumented) diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 8b4edcbc20..0082898ffc 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.12.0-next.0", + "version": "1.12.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 345102adcd..55c1c672d3 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -36,13 +36,13 @@ import { /** * @public - * @deprecated import from `@backstage/search-backend-module-catalog` instead + * @deprecated import from `@backstage/plugin-search-backend-module-catalog` instead */ export const DefaultCatalogCollatorFactory = _DefaultCatalogCollatorFactory; /** * @public - * @deprecated import from `@backstage/search-backend-module-catalog` instead + * @deprecated import from `@backstage/plugin-search-backend-module-catalog` instead */ export const defaultCatalogCollatorEntityTransformer = _defaultCatalogCollatorEntityTransformer; @@ -54,14 +54,14 @@ import type { /** * @public - * @deprecated import from `@backstage/search-backend-module-catalog` instead + * @deprecated import from `@backstage/plugin-search-backend-module-catalog` instead */ export type DefaultCatalogCollatorFactoryOptions = _DefaultCatalogCollatorFactoryOptions; /** * @public - * @deprecated import from `@backstage/search-backend-module-catalog` instead + * @deprecated import from `@backstage/plugin-search-backend-module-catalog` instead */ export type CatalogCollatorEntityTransformer = _CatalogCollatorEntityTransformer; diff --git a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.test.ts b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.test.ts index bafac6e3ac..0cde9417db 100644 --- a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.test.ts +++ b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.test.ts @@ -109,6 +109,73 @@ describe('AnnotateScmSlugEntityProcessor', () => { }, }); }); + + it('should only process applicable kinds', async () => { + const component: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + + const api: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-component', + }, + }; + + const system: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-component', + }, + }; + + const location: LocationSpec = { + type: 'url', + target: + 'https://github.com/backstage/backstage/blob/master/catalog-info.yaml', + }; + + const processor = AnnotateScmSlugEntityProcessor.fromConfig( + new ConfigReader({}), + { kinds: ['API', 'System'] }, + ); + + expect(await processor.preProcessEntity(component, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }); + + expect(await processor.preProcessEntity(api, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-component', + annotations: { + 'github.com/project-slug': 'backstage/backstage', + }, + }, + }); + + expect(await processor.preProcessEntity(system, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-component', + annotations: { + 'github.com/project-slug': 'backstage/backstage', + }, + }, + }); + }); }); describe('gitlab', () => { it('adds annotation', async () => { diff --git a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts index 2c18ba1d7d..241a27433e 100644 --- a/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts +++ b/plugins/catalog-backend/src/modules/core/AnnotateScmSlugEntityProcessor.ts @@ -31,16 +31,23 @@ const GITLAB_ACTIONS_ANNOTATION = 'gitlab.com/project-slug'; /** @public */ export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { constructor( - private readonly opts: { scmIntegrationRegistry: ScmIntegrationRegistry }, + private readonly opts: { + scmIntegrationRegistry: ScmIntegrationRegistry; + kinds?: string[]; + }, ) {} getProcessorName(): string { return 'AnnotateScmSlugEntityProcessor'; } - static fromConfig(config: Config): AnnotateScmSlugEntityProcessor { + static fromConfig( + config: Config, + options?: { kinds?: string[] }, + ): AnnotateScmSlugEntityProcessor { return new AnnotateScmSlugEntityProcessor({ scmIntegrationRegistry: ScmIntegrations.fromConfig(config), + kinds: options?.kinds, }); } @@ -48,7 +55,13 @@ export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { entity: Entity, location: LocationSpec, ): Promise { - if (entity.kind !== 'Component' || location.type !== 'url') { + const applicableKinds = (this.opts.kinds ?? ['Component']).map(k => + k.toLocaleLowerCase('en-US'), + ); + if ( + !applicableKinds.includes(entity.kind.toLocaleLowerCase('en-US')) || + location.type !== 'url' + ) { return entity; } diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index d77b034517..b8d5bdbeda 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -84,7 +84,7 @@ export const catalogPlugin = createBackendPlugin({ env.registerInit({ deps: { logger: coreServices.logger, - config: coreServices.config, + config: coreServices.rootConfig, reader: coreServices.urlReader, permissions: coreServices.permissions, database: coreServices.database, diff --git a/plugins/catalog-backend/src/tests/performance/lib/catalogModuleSyntheticLoadEntities.ts b/plugins/catalog-backend/src/tests/performance/lib/catalogModuleSyntheticLoadEntities.ts index 4d38b28bcf..7925b0b552 100644 --- a/plugins/catalog-backend/src/tests/performance/lib/catalogModuleSyntheticLoadEntities.ts +++ b/plugins/catalog-backend/src/tests/performance/lib/catalogModuleSyntheticLoadEntities.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { createBackendModule } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { LocationSpec } from '@backstage/plugin-catalog-common'; import { @@ -26,7 +25,6 @@ import { EntityProviderConnection, processingResult, } from '@backstage/plugin-catalog-node'; -import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; /** * Options for a fixed initial load of entities. @@ -143,12 +141,15 @@ export const common = { /** * The entity provider that drives the initial base entity injection + * @internal */ -class SyntheticLoadEntitiesProvider implements EntityProvider { +export class SyntheticLoadEntitiesProvider implements EntityProvider { constructor( private readonly load: SyntheticLoadOptions, private readonly events: SyntheticLoadEvents, - ) {} + ) { + validateSyntheticLoadOptions(load); + } getProviderName(): string { return 'SyntheticLoadEntitiesProvider'; @@ -180,9 +181,12 @@ class SyntheticLoadEntitiesProvider implements EntityProvider { /** * Supporting processor for emitting children and relations + * @internal */ -class SyntheticLoadEntitiesProcessor implements CatalogProcessor { - constructor(private readonly load: SyntheticLoadOptions) {} +export class SyntheticLoadEntitiesProcessor implements CatalogProcessor { + constructor(private readonly load: SyntheticLoadOptions) { + validateSyntheticLoadOptions(load); + } getProcessorName(): string { return 'SyntheticLoadEntitiesProcessor'; @@ -231,27 +235,3 @@ class SyntheticLoadEntitiesProcessor implements CatalogProcessor { return entity; } } - -export const catalogModuleSyntheticLoadEntities = createBackendModule( - (options: { load: SyntheticLoadOptions; events?: SyntheticLoadEvents }) => ({ - moduleId: 'syntheticLoadEntities', - pluginId: 'catalog', - register(reg) { - reg.registerInit({ - deps: { - catalog: catalogProcessingExtensionPoint, - }, - async init({ catalog }) { - const { load, events = {} } = options; - - validateSyntheticLoadOptions(load); - const provider = new SyntheticLoadEntitiesProvider(load, events); - const processor = new SyntheticLoadEntitiesProcessor(load); - - catalog.addEntityProvider(provider); - catalog.addProcessor(processor); - }, - }); - }, - }), -); diff --git a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts index 2f32fcd907..fb2bd06b96 100644 --- a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts @@ -16,16 +16,19 @@ import { coreServices, + createBackendModule, createServiceFactory, } from '@backstage/backend-plugin-api'; import { TestDatabases, startTestBackend } from '@backstage/backend-test-utils'; import { catalogPlugin } from '@backstage/plugin-catalog-backend/alpha'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Knex } from 'knex'; import { applyDatabaseMigrations } from '../../database/migrations'; import { SyntheticLoadEvents, SyntheticLoadOptions, - catalogModuleSyntheticLoadEntities, + SyntheticLoadEntitiesProvider, + SyntheticLoadEntitiesProcessor, } from './lib/catalogModuleSyntheticLoadEntities'; import { describePerformanceTest, performanceTraceEnabled } from './lib/env'; @@ -177,13 +180,28 @@ describePerformanceTest('stitchingPerformance', () => { const tracker = new Tracker(knex, load); const backend = await startTestBackend({ - services: [staticDatabase(knex)], features: [ catalogPlugin(), - catalogModuleSyntheticLoadEntities({ - load, - events: tracker.events(), + createBackendModule({ + moduleId: 'syntheticLoadEntities', + pluginId: 'catalog', + register(reg) { + reg.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + }, + async init({ catalog }) { + catalog.addEntityProvider( + new SyntheticLoadEntitiesProvider(load, tracker.events()), + ); + catalog.addProcessor( + new SyntheticLoadEntitiesProcessor(load), + ); + }, + }); + }, }), + staticDatabase(knex), ], }); diff --git a/plugins/catalog-customized/CHANGELOG.md b/plugins/catalog-customized/CHANGELOG.md index 6b573ac4d3..2c98b37be3 100644 --- a/plugins/catalog-customized/CHANGELOG.md +++ b/plugins/catalog-customized/CHANGELOG.md @@ -1,5 +1,29 @@ # @internal/plugin-catalog-customized +## 0.0.13 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1 + - @backstage/plugin-catalog-react@1.8.1 + +## 0.0.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-catalog@1.12.1-next.2 + +## 0.0.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.0.13-next.0 ### Patch Changes diff --git a/plugins/catalog-customized/package.json b/plugins/catalog-customized/package.json index b745c43aca..bb298bf0ae 100644 --- a/plugins/catalog-customized/package.json +++ b/plugins/catalog-customized/package.json @@ -1,7 +1,7 @@ { "name": "@internal/plugin-catalog-customized", "description": "The internal Backstage Customizable plugin for browsing the Backstage catalog", - "version": "0.0.13-next.0", + "version": "0.0.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 4e04cac514..f36eadb464 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-catalog-graph +## 0.2.33 + +### Patch Changes + +- 64ee2c0c7ca5: Propagate entity spec to EntityNode so that spec info such as type can be used for graph node customization +- 62dc7a2b1ad1: Added maximum depth parameter to the catalogGraphParams in CatalogGraphCard. +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## 0.2.33-next.2 + +### Patch Changes + +- 62dc7a2b1ad1: Added maximum depth parameter to the catalogGraphParams in CatalogGraphCard. +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.2.33-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.2.33-next.0 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 2e2b3c2402..b1a1abe6ba 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.33-next.0", + "version": "0.2.33", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx index adee03e298..cdd55e300f 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx @@ -133,7 +133,7 @@ describe('', () => { expect(button).toBeInTheDocument(); expect(button.closest('a')).toHaveAttribute( 'href', - '/catalog-graph?rootEntityRefs%5B%5D=b%3Ad%2Fc&unidirectional=true&mergeRelations=true&direction=LR', + '/catalog-graph?rootEntityRefs%5B%5D=b%3Ad%2Fc&maxDepth=1&unidirectional=true&mergeRelations=true&direction=LR', ); }); @@ -157,7 +157,7 @@ describe('', () => { expect(button).toBeInTheDocument(); expect(button.closest('a')).toHaveAttribute( 'href', - '/catalog-graph?rootEntityRefs%5B%5D=b%3Ad%2Fc&unidirectional=true&mergeRelations=false&direction=LR', + '/catalog-graph?rootEntityRefs%5B%5D=b%3Ad%2Fc&maxDepth=2&unidirectional=true&mergeRelations=false&direction=LR', ); }); diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx index ae4ad850a4..7976fa66bb 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx @@ -108,6 +108,7 @@ export const CatalogGraphCard = ( const catalogGraphParams = qs.stringify( { rootEntityRefs: [stringifyEntityRef(entity)], + maxDepth: maxDepth, unidirectional, mergeRelations, selectedKinds: kinds, diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 47b286216e..8a44026997 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-catalog-import +## 0.9.11 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + +## 0.9.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 0.9.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.9.11-next.0 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 19d475d94e..fe9ef62251 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.9.11-next.0", + "version": "0.9.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 633d39d4d5..4e7b43ae7c 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-catalog-node +## 1.4.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + +## 1.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + +## 1.4.1-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + ## 1.4.1-next.0 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 8aef496418..7c6e21d62c 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-node", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", - "version": "1.4.1-next.0", + "version": "1.4.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 6ae43f297c..953438ae7d 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-react +## 1.8.1 + +### Patch Changes + +- aa3feedce10a: Allow specifying screen size when catalog filters are hidden in drawer +- Updated dependencies + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## 1.8.1-next.1 + +### Patch Changes + +- aa3feedce10a: Allow specifying screen size when catalog filters are hidden in drawer + ## 1.8.1-next.0 ### Patch Changes diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 9bf87778dd..b5ab8b6c7f 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -63,7 +63,13 @@ export const catalogApiRef: ApiRef; // @public (undocumented) export const CatalogFilterLayout: { (props: { children: React_2.ReactNode }): JSX.Element; - Filters: (props: { children: React_2.ReactNode }) => JSX.Element; + Filters: (props: { + children: React_2.ReactNode; + options?: { + drawerBreakpoint?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number; + drawerAnchor?: 'left' | 'right' | 'top' | 'bottom'; + }; + }) => JSX.Element; Content: (props: { children: React_2.ReactNode }) => JSX.Element; }; diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 67a3409830..9853935353 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.8.1-next.0", + "version": "1.8.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-react/src/components/CatalogFilterLayout/CatalogFilterLayout.tsx b/plugins/catalog-react/src/components/CatalogFilterLayout/CatalogFilterLayout.tsx index 751eb35f1d..1a1404edf2 100644 --- a/plugins/catalog-react/src/components/CatalogFilterLayout/CatalogFilterLayout.tsx +++ b/plugins/catalog-react/src/components/CatalogFilterLayout/CatalogFilterLayout.tsx @@ -28,14 +28,20 @@ import FilterListIcon from '@material-ui/icons/FilterList'; import { BackstageTheme } from '@backstage/theme'; /** @public */ -export const Filters = (props: { children: React.ReactNode }) => { - const isMidSizeScreen = useMediaQuery(theme => - theme.breakpoints.down('md'), +export const Filters = (props: { + children: React.ReactNode; + options?: { + drawerBreakpoint?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number; + drawerAnchor?: 'left' | 'right' | 'top' | 'bottom'; + }; +}) => { + const isScreenSmallerThanBreakpoint = useMediaQuery(theme => + theme.breakpoints.down(props.options?.drawerBreakpoint ?? 'md'), ); const theme = useTheme(); const [filterDrawerOpen, setFilterDrawerOpen] = useState(false); - return isMidSizeScreen ? ( + return isScreenSmallerThanBreakpoint ? ( <> +
+ +
+ {windowOptions.map(opt => ( + + handleSubmitPresetDates(opt.value)} + > + {opt.name} + + + ))} +
+ + + + ); +}; + +export default React.memo(SelectWindow); diff --git a/plugins/opencost/src/components/Subtitle.jsx b/plugins/opencost/src/components/Subtitle.jsx new file mode 100644 index 0000000000..63b3df61ff --- /dev/null +++ b/plugins/opencost/src/components/Subtitle.jsx @@ -0,0 +1,59 @@ +/* + * 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 from 'react'; +import { makeStyles } from '@material-ui/styles'; +import { upperFirst } from 'lodash'; +import Breadcrumbs from '@material-ui/core/Breadcrumbs'; +import NavigateNextIcon from '@material-ui/icons/NavigateNext'; +import Typography from '@material-ui/core/Typography'; +import { toVerboseTimeRange } from '../util'; + +const useStyles = makeStyles({ + root: { + '& > * + *': { + marginTop: 2, + }, + }, + link: { + cursor: 'pointer', + }, +}); + +const Subtitle = ({ report }) => { + const classes = useStyles(); + + const { aggregateBy, window } = report; + + return ( +
+ } + aria-label="breadcrumb" + > + {aggregateBy && aggregateBy.length > 0 ? ( + + {toVerboseTimeRange(window)} by {upperFirst(aggregateBy)} + + ) : ( + {toVerboseTimeRange(window)} + )} + +
+ ); +}; + +export default React.memo(Subtitle); diff --git a/plugins/opencost/src/components/Warnings.jsx b/plugins/opencost/src/components/Warnings.jsx new file mode 100644 index 0000000000..ae1047ebc2 --- /dev/null +++ b/plugins/opencost/src/components/Warnings.jsx @@ -0,0 +1,53 @@ +/* + * 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 from 'react'; +import { makeStyles } from '@material-ui/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Paper from '@material-ui/core/Paper'; +import WarningIcon from '@material-ui/icons/Warning'; + +const useStyles = makeStyles({ + root: {}, +}); + +const Warnings = ({ warnings }) => { + const classes = useStyles(); + + if (!warnings || warnings.length === 0) { + return null; + } + + return ( + + + {warnings.map((warn, i) => ( + + + + + + + ))} + + + ); +}; + +export default Warnings; diff --git a/plugins/opencost/src/constants/colors.js b/plugins/opencost/src/constants/colors.js new file mode 100644 index 0000000000..8e0289541b --- /dev/null +++ b/plugins/opencost/src/constants/colors.js @@ -0,0 +1,52 @@ +/* + * 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 blue from '@material-ui/core/colors/blue'; +import brown from '@material-ui/core/colors/brown'; +import cyan from '@material-ui/core/colors/cyan'; +import deepOrange from '@material-ui/core/colors/deepOrange'; +import deepPurple from '@material-ui/core/colors/deepPurple'; +import green from '@material-ui/core/colors/green'; +import grey from '@material-ui/core/colors/grey'; +import indigo from '@material-ui/core/colors/indigo'; +import orange from '@material-ui/core/colors/orange'; +import red from '@material-ui/core/colors/red'; +import teal from '@material-ui/core/colors/teal'; +import yellow from '@material-ui/core/colors/yellow'; + +export const primary = [ + blue[500], + red[500], + green[500], + yellow[500], + cyan[500], + orange[500], + teal[500], + indigo[500], + deepOrange[500], + deepPurple[500], +]; + +export const greyscale = [ + grey[300], + grey[400], + grey[200], + grey[500], + grey[100], + grey[600], +]; + +export const browns = [brown[500]]; diff --git a/plugins/opencost/src/constants/currencyCodes.js b/plugins/opencost/src/constants/currencyCodes.js new file mode 100644 index 0000000000..a325df54d0 --- /dev/null +++ b/plugins/opencost/src/constants/currencyCodes.js @@ -0,0 +1,196 @@ +/* + * 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. + */ + +export const currencyCodes = [ + 'AED', + 'AFN', + 'ALL', + 'AMD', + 'ANG', + 'AOA', + 'ARS', + 'AUD', + 'AWG', + 'AZN', + 'BAM', + 'BBD', + 'BDT', + 'BGN', + 'BHD', + 'BIF', + 'BMD', + 'BND', + 'BOB', + 'BOV', + 'BRL', + 'BSD', + 'BTN', + 'BWP', + 'BYR', + 'BZD', + 'CAD', + 'CDF', + 'CHE', + 'CHF', + 'CHW', + 'CLF', + 'CLP', + 'CNY', + 'COP', + 'COU', + 'CRC', + 'CUC', + 'CUP', + 'CVE', + 'CZK', + 'DJF', + 'DKK', + 'DOP', + 'DZD', + 'EGP', + 'ERN', + 'ETB', + 'EUR', + 'FJD', + 'FKP', + 'GBP', + 'GEL', + 'GHS', + 'GIP', + 'GMD', + 'GNF', + 'GTQ', + 'GYD', + 'HKD', + 'HNL', + 'HRK', + 'HTG', + 'HUF', + 'IDR', + 'ILS', + 'INR', + 'IQD', + 'IRR', + 'ISK', + 'JMD', + 'JOD', + 'JPY', + 'KES', + 'KGS', + 'KHR', + 'KMF', + 'KPW', + 'KRW', + 'KWD', + 'KYD', + 'KZT', + 'LAK', + 'LBP', + 'LKR', + 'LRD', + 'LSL', + 'LTL', + 'LVL', + 'LYD', + 'MAD', + 'MDL', + 'MGA', + 'MKD', + 'MMK', + 'MNT', + 'MOP', + 'MRO', + 'MUR', + 'MVR', + 'MWK', + 'MXN', + 'MXV', + 'MYR', + 'MZN', + 'NAD', + 'NGN', + 'NIO', + 'NOK', + 'NPR', + 'NZD', + 'OMR', + 'PAB', + 'PEN', + 'PGK', + 'PHP', + 'PKR', + 'PLN', + 'PYG', + 'QAR', + 'RON', + 'RSD', + 'RUB', + 'RWF', + 'SAR', + 'SBD', + 'SCR', + 'SDG', + 'SEK', + 'SGD', + 'SHP', + 'SLL', + 'SOS', + 'SRD', + 'SSP', + 'STD', + 'SYP', + 'SZL', + 'THB', + 'TJS', + 'TMT', + 'TND', + 'TOP', + 'TRY', + 'TTD', + 'TWD', + 'TZS', + 'UAH', + 'UGX', + 'USD', + 'USN', + 'USS', + 'UYI', + 'UYU', + 'UZS', + 'VEF', + 'VND', + 'VUV', + 'WST', + 'XAF', + 'XAG', + 'XAU', + 'XBA', + 'XBB', + 'XBC', + 'XBD', + 'XCD', + 'XDR', + 'XFU', + 'XOF', + 'XPD', + 'XPF', + 'XPT', + 'XTS', + 'XXX', + 'YER', + 'ZAR', + 'ZMW', +]; diff --git a/plugins/opencost/src/images/pig.png b/plugins/opencost/src/images/pig.png new file mode 100644 index 0000000000..5de5409e87 Binary files /dev/null and b/plugins/opencost/src/images/pig.png differ diff --git a/plugins/opencost/src/index.ts b/plugins/opencost/src/index.ts new file mode 100644 index 0000000000..30153e0c32 --- /dev/null +++ b/plugins/opencost/src/index.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * A Backstage plugin that integrates with OpenCost + * + * @packageDocumentation + */ + +export { openCostPlugin, OpenCostPage } from './plugin'; diff --git a/plugins/opencost/src/plugin.test.ts b/plugins/opencost/src/plugin.test.ts new file mode 100644 index 0000000000..6d6d20c603 --- /dev/null +++ b/plugins/opencost/src/plugin.test.ts @@ -0,0 +1,23 @@ +/* + * 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 { openCostPlugin } from './plugin'; + +describe('opencost', () => { + it('should export plugin', () => { + expect(openCostPlugin).toBeDefined(); + }); +}); diff --git a/plugins/opencost/src/plugin.ts b/plugins/opencost/src/plugin.ts new file mode 100644 index 0000000000..2725cf3dde --- /dev/null +++ b/plugins/opencost/src/plugin.ts @@ -0,0 +1,40 @@ +/* + * 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 { + createPlugin, + createRoutableExtension, +} from '@backstage/core-plugin-api'; + +import { rootRouteRef } from './routes'; + +/** @public */ +export const openCostPlugin = createPlugin({ + id: 'opencost', + routes: { + root: rootRouteRef, + }, +}); + +/** @public */ +export const OpenCostPage = openCostPlugin.provide( + createRoutableExtension({ + name: 'OpenCostPage', + component: () => + import('./components/OpenCostPage').then(m => m.OpenCostPage), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/opencost/src/routes.ts b/plugins/opencost/src/routes.ts new file mode 100644 index 0000000000..5f1f9e4527 --- /dev/null +++ b/plugins/opencost/src/routes.ts @@ -0,0 +1,21 @@ +/* + * 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 { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'opencost', +}); diff --git a/plugins/opencost/src/services/allocation.js b/plugins/opencost/src/services/allocation.js new file mode 100644 index 0000000000..e7fba8feae --- /dev/null +++ b/plugins/opencost/src/services/allocation.js @@ -0,0 +1,36 @@ +/* + * 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 axios from 'axios'; + +class AllocationService { + async fetchAllocation(baseUrl, win, aggregate, options) { + const { accumulate } = options; + const params = { + window: win, + aggregate: aggregate, + step: '1d', + }; + if (typeof accumulate === 'boolean') { + params.accumulate = accumulate; + } + + const result = await axios.get(`${baseUrl}/allocation/compute`, { params }); + + return result.data; + } +} + +export default new AllocationService(); diff --git a/plugins/opencost/src/util.js b/plugins/opencost/src/util.js new file mode 100644 index 0000000000..5854718a55 --- /dev/null +++ b/plugins/opencost/src/util.js @@ -0,0 +1,371 @@ +/* + * 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 { forEach, get, round } from 'lodash'; + +// rangeToCumulative takes an AllocationSetRange (type: array[AllocationSet]) +// and accumulates the values into a single AllocationSet (type: object) +export function rangeToCumulative(allocationSetRange, aggregateBy) { + if (allocationSetRange.length === 0) { + return null; + } + + const result = {}; + + forEach(allocationSetRange, allocSet => { + forEach(allocSet, alloc => { + if (result[alloc.name] === undefined) { + const hrs = get(alloc, 'minutes', 0) / 60.0; + + result[alloc.name] = { + name: alloc.name, + [aggregateBy]: alloc.name, + cpuCost: get(alloc, 'cpuCost', 0), + gpuCost: get(alloc, 'gpuCost', 0), + ramCost: get(alloc, 'ramCost', 0), + pvCost: get(alloc, 'pvCost', 0), + networkCost: get(alloc, 'networkCost', 0), + sharedCost: get(alloc, 'sharedCost', 0), + externalCost: get(alloc, 'externalCost', 0), + totalCost: get(alloc, 'totalCost', 0), + cpuUseCoreHrs: get(alloc, 'cpuCoreUsageAverage', 0) * hrs, + cpuReqCoreHrs: get(alloc, 'cpuCoreRequestAverage', 0) * hrs, + ramUseByteHrs: get(alloc, 'ramByteUsageAverage', 0) * hrs, + ramReqByteHrs: get(alloc, 'ramByteRequestAverage', 0) * hrs, + cpuEfficiency: get(alloc, 'cpuEfficiency', 0), + ramEfficiency: get(alloc, 'ramEfficiency', 0), + totalEfficiency: get(alloc, 'totalEfficiency', 0), + }; + } else { + const hrs = get(alloc, 'minutes', 0) / 60.0; + + result[alloc.name].cpuCost += get(alloc, 'cpuCost', 0); + result[alloc.name].gpuCost += get(alloc, 'gpuCost', 0); + result[alloc.name].ramCost += get(alloc, 'ramCost', 0); + result[alloc.name].pvCost += get(alloc, 'pvCost', 0); + result[alloc.name].networkCost += get(alloc, 'networkCost', 0); + result[alloc.name].sharedCost += get(alloc, 'sharedCost', 0); + result[alloc.name].externalCost += get(alloc, 'externalCost', 0); + result[alloc.name].totalCost += get(alloc, 'totalCost', 0); + result[alloc.name].cpuUseCoreHrs += + get(alloc, 'cpuCoreUsageAverage', 0) * hrs; + result[alloc.name].cpuReqCoreHrs += + get(alloc, 'cpuCoreRequestAverage', 0) * hrs; + result[alloc.name].ramUseByteHrs += + get(alloc, 'ramByteUsageAverage', 0) * hrs; + result[alloc.name].ramReqByteHrs += + get(alloc, 'ramByteRequestAverage', 0) * hrs; + } + }); + }); + + // If the range is of length > 1 (i.e. it is not just a single set) then + // compute efficiency for each result after accumulating. + if (allocationSetRange.length > 1) { + forEach(result, (alloc, name) => { + // If we can't compute total efficiency, it defaults to 0.0 + let totalEfficiency = 0.0; + + // CPU efficiency is defined as (usage/request). If request == 0.0 but + // usage > 0, then efficiency gets set to 1.0. + let cpuEfficiency = 0.0; + if (alloc.cpuReqCoreHrs > 0) { + cpuEfficiency = alloc.cpuUseCoreHrs / alloc.cpuReqCoreHrs; + } else if (alloc.cpuUseCoreHrs > 0) { + cpuEfficiency = 1.0; + } + + // RAM efficiency is defined as (usage/request). If request == 0.0 but + // usage > 0, then efficiency gets set to 1.0. + let ramEfficiency = 0.0; + if (alloc.ramReqByteHrs > 0) { + ramEfficiency = alloc.ramUseByteHrs / alloc.ramReqByteHrs; + } else if (alloc.ramUseByteHrs > 0) { + ramEfficiency = 1.0; + } + + // Compute efficiency as the cost-weighted average of CPU and RAM + // efficiency + if (alloc.cpuCost + alloc.ramCost > 0.0) { + totalEfficiency = + (alloc.cpuCost * cpuEfficiency + alloc.ramCost * ramEfficiency) / + (alloc.cpuCost + alloc.ramCost); + } + + result[name].cpuEfficiency = cpuEfficiency; + result[name].ramEfficiency = ramEfficiency; + result[name].totalEfficiency = totalEfficiency; + }); + } + + return result; +} + +// cumulativeToTotals adds each entry in the given AllocationSet (type: object) +// and returns a single Allocation (type: object) representing the totals +export function cumulativeToTotals(allocationSet) { + const totals = { + name: 'Totals', + cpuCost: 0, + gpuCost: 0, + ramCost: 0, + pvCost: 0, + networkCost: 0, + sharedCost: 0, + externalCost: 0, + totalCost: 0, + cpuEfficiency: 0, + ramEfficiency: 0, + totalEfficiency: 0, + }; + + // Use these for computing efficiency. As such, idle will not factor into + // these numbers, including CPU and RAM cost. + let cpuReqCoreHrs = 0; + let cpuUseCoreHrs = 0; + let ramReqByteHrs = 0; + let ramUseByteHrs = 0; + let cpuCost = 0; + let ramCost = 0; + + forEach(allocationSet, (alloc, name) => { + // Accumulate efficiency-related fields + if (name !== '__idle__') { + cpuReqCoreHrs += get(alloc, 'cpuReqCoreHrs', 0.0); + cpuUseCoreHrs += get(alloc, 'cpuUseCoreHrs', 0.0); + ramReqByteHrs += get(alloc, 'ramReqByteHrs', 0.0); + ramUseByteHrs += get(alloc, 'ramUseByteHrs', 0.0); + cpuCost += get(alloc, 'cpuCost', 0.0); + ramCost += get(alloc, 'ramCost', 0.0); + } + + // Sum cumulative fields + totals.cpuCost += get(alloc, 'cpuCost', 0); + totals.gpuCost += get(alloc, 'gpuCost', 0); + totals.ramCost += get(alloc, 'ramCost', 0); + totals.pvCost += get(alloc, 'pvCost', 0); + totals.networkCost += get(alloc, 'networkCost', 0); + totals.sharedCost += get(alloc, 'sharedCost', 0); + totals.externalCost += get(alloc, 'externalCost', 0); + totals.totalCost += get(alloc, 'totalCost', 0); + }); + + // Compute efficiency + if (cpuReqCoreHrs > 0) { + totals.cpuEfficiency = cpuUseCoreHrs / cpuReqCoreHrs; + } else if (cpuUseCoreHrs > 0) { + totals.cpuEfficiency = 1.0; + } + + if (ramReqByteHrs > 0) { + totals.ramEfficiency = ramUseByteHrs / ramReqByteHrs; + } else if (ramUseByteHrs > 0) { + totals.ramEfficiency = 1.0; + } + + if (cpuCost + ramCost > 0) { + totals.totalEfficiency = + (cpuCost * totals.cpuEfficiency + ramCost * totals.ramEfficiency) / + (cpuCost + ramCost); + } + + totals.cpuReqCoreHrs = cpuReqCoreHrs; + totals.cpuUseCoreHrs = cpuUseCoreHrs; + totals.ramReqByteHrs = ramReqByteHrs; + totals.ramUseByteHrs = ramUseByteHrs; + + return totals; +} + +export function toVerboseTimeRange(window) { + const months = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + + const start = new Date(); + start.setUTCHours(0, 0, 0, 0); + + const end = new Date(); + end.setUTCHours(0, 0, 0, 0); + + switch (window) { + case 'today': + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()}`; + case 'yesterday': + start.setUTCDate(start.getUTCDate() - 1); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()}`; + case 'week': + start.setUTCDate(start.getUTCDate() - start.getUTCDay()); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} until now`; + case 'month': + start.setUTCDate(1); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} until now`; + case 'lastweek': + start.setUTCDate(start.getUTCDate() - (start.getUTCDay() + 7)); + end.setUTCDate(end.getUTCDate() - (end.getUTCDay() + 1)); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through ${end.getUTCDate()} ${ + months[end.getUTCMonth()] + } ${end.getUTCFullYear()}`; + case 'lastmonth': + end.setUTCDate(1); + end.setUTCDate(end.getUTCDate() - 1); + start.setUTCDate(1); + start.setUTCDate(start.getUTCDate() - 1); + start.setUTCDate(1); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through ${end.getUTCDate()} ${ + months[end.getUTCMonth()] + } ${end.getUTCFullYear()}`; + case '6d': + start.setUTCDate(start.getUTCDate() - 6); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through now`; + case '29d': + start.setUTCDate(start.getUTCDate() - 29); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through now`; + case '59d': + start.setUTCDate(start.getUTCDate() - 59); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through now`; + case '89d': + start.setUTCDate(start.getUTCDate() - 89); + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through now`; + // no default + } + + const splitDates = window.split(','); + if (checkCustomWindow(window) && splitDates.length > 1) { + const s = splitDates[0].split(/\D+/).slice(0, 3); + const e = splitDates[1].split(/\D+/).slice(0, 3); + if (s.length === 3 && e.length === 3) { + start.setUTCFullYear(s[0], s[1] - 1, s[2]); + end.setUTCFullYear(e[0], e[1] - 1, e[2]); + if (start === end) { + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()}`; + } + return `${start.getUTCDate()} ${ + months[start.getUTCMonth()] + } ${start.getUTCFullYear()} through ${end.getUTCDate()} ${ + months[end.getUTCMonth()] + } ${end.getUTCFullYear()}`; + } + } + return ''; +} + +export function bytesToString(bytes) { + const ei = Math.pow(1024, 6); + if (bytes >= ei) { + return `${round(bytes / ei, 1)} EiB`; + } + const pi = Math.pow(1024, 5); + if (bytes >= pi) { + return `${round(bytes / pi, 1)} PiB`; + } + const ti = Math.pow(1024, 4); + if (bytes >= ti) { + return `${round(bytes / ti, 1)} TiB`; + } + const gi = Math.pow(1024, 3); + if (bytes >= gi) { + return `${round(bytes / gi, 1)} GiB`; + } + const mi = Math.pow(1024, 2); + if (bytes >= mi) { + return `${round(bytes / mi, 1)} MiB`; + } + const ki = Math.pow(1024, 1); + if (bytes >= ki) { + return `${round(bytes / ki, 1)} KiB`; + } + + return `${round(bytes, 1)} B`; +} + +const currencyLocale = 'en-US'; + +export function toCurrency(amount, currency, precision) { + if (typeof amount !== 'number') { + Console.warn( + `Tried to convert "${amount}" to currency, but it is not a number`, + ); + return ''; + } + + let ctype = currency; + + if (currency === undefined || currency === '') { + ctype = 'USD'; + } + + const opts = { + style: 'currency', + currency: ctype, + }; + + if (typeof precision === 'number') { + opts.minimumFractionDigits = precision; + opts.maximumFractionDigits = precision; + } + + return amount.toLocaleString(currencyLocale, opts); +} + +export function checkCustomWindow(window) { + // Example ISO interval string: 2020-12-02T00:00:00Z,2020-12-03T23:59:59Z + const customDateRegex = + /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z,\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/; + return customDateRegex.test(window); +} + +export default { + rangeToCumulative, + cumulativeToTotals, + toVerboseTimeRange, + bytesToString, + toCurrency, + checkCustomWindow, +}; diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 760d798a44..fc89f7aa33 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-org-react +## 0.1.10 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.1.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.1.10-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 49765ba6d5..ce49dd71d0 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.10-next.0", + "version": "0.1.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 57d83db630..79dfedbd3d 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-org +## 0.6.11 + +### Patch Changes + +- 50331203e2a2: Fixed bug in MembersListCard component where the url for the members was static which was breaking the component when catalog route was changed +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.6.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.6.11-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.6.11-next.0 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 23af90d2b8..48d53ca521 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.6.11-next.0", + "version": "0.6.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx index 47e954eb17..f5a2d50cb5 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx @@ -19,6 +19,7 @@ import { CatalogApi, catalogApiRef, EntityProvider, + entityRouteRef, StarredEntitiesApi, starredEntitiesApiRef, } from '@backstage/plugin-catalog-react'; @@ -124,6 +125,11 @@ describe('MemberTab Test', () => { , , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ), ); @@ -152,6 +158,11 @@ describe('MemberTab Test', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ), ); @@ -176,12 +187,15 @@ describe('MemberTab Test', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ); - const toggleSwitch = screen.queryByRole('checkbox'); expect(toggleSwitch).toBeNull(); }); - it('Shows the aggregate members toggle if the showAggregateMembersToggle prop is true', async () => { await renderInTestApp( { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ); - expect(screen.queryByRole('checkbox')).toBeInTheDocument(); }); - it('Shows only direct members if the showAggregateMembersToggle prop is undefined', async () => { await renderInTestApp( { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ); - const displayedMemberNames = screen.queryAllByTestId('user-link'); const duplicatedUserText = screen.getByText('Duplicated User'); const groupAUserOneText = screen.getByText('Group A User One'); - expect(displayedMemberNames).toHaveLength(2); expect(duplicatedUserText).toBeInTheDocument(); expect(groupAUserOneText).toBeInTheDocument(); @@ -234,7 +254,6 @@ describe('MemberTab Test', () => { duplicatedUserText.compareDocumentPosition(groupAUserOneText), ).toBe(Node.DOCUMENT_POSITION_FOLLOWING); }); - it('Shows only direct members if the aggregate members switch is turned off', async () => { await renderInTestApp( { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ); - const displayedMemberNames = screen.queryAllByTestId('user-link'); const duplicatedUserText = screen.getByText('Duplicated User'); const groupAUserOneText = screen.getByText('Group A User One'); - expect(displayedMemberNames).toHaveLength(2); expect(duplicatedUserText).toBeInTheDocument(); expect(groupAUserOneText).toBeInTheDocument(); @@ -265,7 +287,6 @@ describe('MemberTab Test', () => { duplicatedUserText.compareDocumentPosition(groupAUserOneText), ).toBe(Node.DOCUMENT_POSITION_FOLLOWING); }); - it('Shows all descendant members of the group when the aggregate users switch is turned on, showing duplicated members only once', async () => { await renderInTestApp( { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ); - // Click the toggle switch await userEvent.click(screen.getByRole('checkbox')); - const displayedMemberNames = screen.queryAllByTestId('user-link'); const duplicatedUserText = screen.getByText('Duplicated User'); const groupAUserOneText = screen.getByText('Group A User One'); const groupBUserOneText = screen.getByText('Group B User One'); const groupDUserOneText = screen.getByText('Group D User One'); const groupEUserOneText = screen.getByText('Group E User One'); - expect(displayedMemberNames).toHaveLength(5); - expect(duplicatedUserText).toBeInTheDocument(); expect(groupAUserOneText).toBeInTheDocument(); expect(groupBUserOneText).toBeInTheDocument(); expect(groupDUserOneText).toBeInTheDocument(); expect(groupEUserOneText).toBeInTheDocument(); - expect( duplicatedUserText.compareDocumentPosition(groupAUserOneText), ).toBe(Node.DOCUMENT_POSITION_FOLLOWING); diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index 0a5386ea20..3716af5522 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -20,11 +20,7 @@ import { UserEntity, stringifyEntityRef, } from '@backstage/catalog-model'; -import { - catalogApiRef, - entityRouteParams, - useEntity, -} from '@backstage/plugin-catalog-react'; +import { catalogApiRef, useEntity } from '@backstage/plugin-catalog-react'; import { Box, createStyles, @@ -36,7 +32,6 @@ import { } from '@material-ui/core'; import Pagination from '@material-ui/lab/Pagination'; import React, { useState } from 'react'; -import { generatePath } from 'react-router-dom'; import useAsync from 'react-use/lib/useAsync'; import { @@ -52,6 +47,7 @@ import { getAllDesendantMembersForGroupEntity, removeDuplicateEntitiesFrom, } from '../../../../helpers/helpers'; +import { EntityRefLink } from '@backstage/plugin-catalog-react'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -102,15 +98,11 @@ const MemberComponent = (props: { member: UserEntity }) => { textAlign="center" > - - - + entityRef={props.member} + title={displayName} + /> {profile?.email && ( diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 4ff94ae479..adc01d7b32 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-pagerduty +## 0.6.2 + +### Patch Changes + +- 5003fc966741: Add new `disableChangeEvents` prop to `EntityPagerDutyCard` to hide the Change Events tab and disable fetching of change events for the PagerDuty service. +- Updated dependencies + - @backstage/plugin-home-react@0.1.2 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-home-react@0.1.2-next.0 + ## 0.6.2-next.0 ### Patch Changes diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index dd24aa9bec..f398a6bee7 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -22,6 +22,7 @@ export const EntityPagerDutyCard: ( // @public (undocumented) export type EntityPagerDutyCardProps = { readOnly?: boolean; + disableChangeEvents?: boolean; }; // @public (undocumented) diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 13d56fd3f2..e89f8715f4 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-pagerduty", "description": "A Backstage plugin that integrates towards PagerDuty", - "version": "0.6.2-next.0", + "version": "0.6.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx index 50e6f3a096..b252069b90 100644 --- a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx +++ b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx @@ -30,12 +30,19 @@ export const isPluginApplicableToEntity = (entity: Entity) => /** @public */ export type EntityPagerDutyCardProps = { readOnly?: boolean; + disableChangeEvents?: boolean; }; /** @public */ export const EntityPagerDutyCard = (props: EntityPagerDutyCardProps) => { - const { readOnly } = props; + const { readOnly, disableChangeEvents } = props; const { entity } = useEntity(); const pagerDutyEntity = getPagerDutyEntity(entity); - return ; + return ( + + ); }; diff --git a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx index bb89865c5f..332d823f1f 100644 --- a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx +++ b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx @@ -46,11 +46,12 @@ const BasicCard = ({ children }: { children: ReactNode }) => ( /** @public */ export type PagerDutyCardProps = PagerDutyEntity & { readOnly?: boolean; + disableChangeEvents?: boolean; }; /** @public */ export const PagerDutyCard = (props: PagerDutyCardProps) => { - const { readOnly, integrationKey, name } = props; + const { readOnly, disableChangeEvents, integrationKey, name } = props; const api = useApi(pagerDutyApiRef); const [refreshIncidents, setRefreshIncidents] = useState(false); const [refreshChangeEvents, setRefreshChangeEvents] = @@ -169,12 +170,16 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => { refreshIncidents={refreshIncidents} /> - - - + <> + {disableChangeEvents !== true && ( + + + + )} + diff --git a/plugins/periskop-backend/CHANGELOG.md b/plugins/periskop-backend/CHANGELOG.md index d2279571fd..5f80406474 100644 --- a/plugins/periskop-backend/CHANGELOG.md +++ b/plugins/periskop-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-periskop-backend +## 0.1.19 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + +## 0.1.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.1.19-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/periskop-backend/package.json b/plugins/periskop-backend/package.json index f515fd4088..bd828ed9b8 100644 --- a/plugins/periskop-backend/package.json +++ b/plugins/periskop-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop-backend", - "version": "0.1.19-next.0", + "version": "0.1.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/periskop-backend/src/plugin.ts b/plugins/periskop-backend/src/plugin.ts index 933c864d50..2123dcbece 100644 --- a/plugins/periskop-backend/src/plugin.ts +++ b/plugins/periskop-backend/src/plugin.ts @@ -31,7 +31,7 @@ export const periskopPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, httpRouter: coreServices.httpRouter, }, diff --git a/plugins/periskop/CHANGELOG.md b/plugins/periskop/CHANGELOG.md index 5e9155a931..6bf38267f7 100644 --- a/plugins/periskop/CHANGELOG.md +++ b/plugins/periskop/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-periskop +## 0.1.19 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.1.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.1.19-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index 2f9f23d592..7f044bc15f 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop", - "version": "0.1.19-next.0", + "version": "0.1.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 58701e2f41..848a6ffb91 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-permission-backend +## 0.5.23 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 0d6ddc62d8da: Refactor backend plugin creation parameter from callback to object. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## 0.5.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.5.23-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + ## 0.5.23-next.0 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 964a7ed5ac..0ab2eeee47 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.23-next.0", + "version": "0.5.23", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend/src/plugin.ts b/plugins/permission-backend/src/plugin.ts index 5c0a9a87a0..a131b6e4b4 100644 --- a/plugins/permission-backend/src/plugin.ts +++ b/plugins/permission-backend/src/plugin.ts @@ -80,7 +80,7 @@ export const permissionModuleAllowAllPolicy = createBackendModule({ * * @alpha */ -export const permissionPlugin = createBackendPlugin(() => ({ +export const permissionPlugin = createBackendPlugin({ pluginId: 'permission', register(env) { const policies = new PolicyExtensionPointImpl(); @@ -90,7 +90,7 @@ export const permissionPlugin = createBackendPlugin(() => ({ env.registerInit({ deps: { http: coreServices.httpRouter, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, discovery: coreServices.discovery, identity: coreServices.identity, @@ -115,4 +115,4 @@ export const permissionPlugin = createBackendPlugin(() => ({ }, }); }, -})); +}); diff --git a/plugins/permission-common/CHANGELOG.md b/plugins/permission-common/CHANGELOG.md index b0a2cb2b42..eaab7851bd 100644 --- a/plugins/permission-common/CHANGELOG.md +++ b/plugins/permission-common/CHANGELOG.md @@ -433,7 +433,7 @@ - b768259244: **BREAKING**: Authorize API request and response types have been updated. The existing `AuthorizeRequest` and `AuthorizeResponse` types now match the entire request and response objects for the /authorize endpoint, and new types `AuthorizeQuery` and `AuthorizeDecision` have been introduced for individual items in the request and response batches respectively. - **BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/permission-backend. + **BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/plugin-permission-backend. ### Patch Changes @@ -446,7 +446,7 @@ - b768259244: **BREAKING**: Authorize API request and response types have been updated. The existing `AuthorizeRequest` and `AuthorizeResponse` types now match the entire request and response objects for the /authorize endpoint, and new types `AuthorizeQuery` and `AuthorizeDecision` have been introduced for individual items in the request and response batches respectively. - **BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/permission-backend. + **BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/plugin-permission-backend. ### Patch Changes diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index d036c17985..7126954413 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-permission-node +## 0.7.11 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## 0.7.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.7.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + ## 0.7.11-next.0 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 52bca6caf0..cfccd9678d 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.7.11-next.0", + "version": "0.7.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist-backend/CHANGELOG.md b/plugins/playlist-backend/CHANGELOG.md index 351805dc01..980d957538 100644 --- a/plugins/playlist-backend/CHANGELOG.md +++ b/plugins/playlist-backend/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-playlist-backend +## 0.3.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-playlist-common@0.1.9 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + +## 0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.3.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-playlist-common@0.1.9-next.0 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + ## 0.3.4-next.0 ### Patch Changes diff --git a/plugins/playlist-backend/package.json b/plugins/playlist-backend/package.json index 24686f93e6..5a24b7cd6d 100644 --- a/plugins/playlist-backend/package.json +++ b/plugins/playlist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist-backend", - "version": "0.3.4-next.0", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist-common/CHANGELOG.md b/plugins/playlist-common/CHANGELOG.md index 3d93a98c07..de4601c537 100644 --- a/plugins/playlist-common/CHANGELOG.md +++ b/plugins/playlist-common/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-playlist-common +## 0.1.9 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-permission-common@0.7.7 + +## 0.1.9-next.0 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-permission-common@0.7.7 + ## 0.1.8 ### Patch Changes diff --git a/plugins/playlist-common/package.json b/plugins/playlist-common/package.json index 4303113910..43c65bbab3 100644 --- a/plugins/playlist-common/package.json +++ b/plugins/playlist-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-playlist-common", "description": "Common functionalities for the playlist plugin", - "version": "0.1.8", + "version": "0.1.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist/CHANGELOG.md b/plugins/playlist/CHANGELOG.md index e31fe279e8..09d4dd8560 100644 --- a/plugins/playlist/CHANGELOG.md +++ b/plugins/playlist/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-playlist +## 0.1.13 + +### Patch Changes + +- d1e0588324d1: Displaying an alert popup each time the Playlist is created or deleted +- 12a8c94eda8d: Add package repository and homepage metadata +- 8a997547f02a: Adding alert popup whenever user delete the entities from playlist entity table +- Updated dependencies + - @backstage/plugin-playlist-common@0.1.9 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + +## 0.1.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.1.13-next.1 + +### Patch Changes + +- d1e0588324d1: Displaying an alert popup each time the Playlist is created or deleted +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-playlist-common@0.1.9-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-permission-react@0.4.14 + - @backstage/plugin-search-react@1.6.4-next.0 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index 33f1a94a3c..574aa712ab 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist", - "version": "0.1.13-next.0", + "version": "0.1.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx index 3d8efe3bc5..3d6ffc5f8c 100644 --- a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx @@ -21,7 +21,7 @@ import { Table, TableFilter, } from '@backstage/core-components'; -import { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { errorApiRef, useApi, alertApiRef } from '@backstage/core-plugin-api'; import { EntityRefLink } from '@backstage/plugin-catalog-react'; import { usePermission } from '@backstage/plugin-permission-react'; import { permissions } from '@backstage/plugin-playlist-common'; @@ -42,6 +42,7 @@ export const PlaylistEntitiesTable = ({ }) => { const errorApi = useApi(errorApiRef); const playlistApi = useApi(playlistApiRef); + const alertApi = useApi(alertApiRef); const [openAddEntitiesDrawer, setOpenAddEntitiesDrawer] = useState(false); const { allowed: editAllowed } = usePermission({ @@ -73,16 +74,30 @@ export const PlaylistEntitiesTable = ({ const removeEntity = useCallback( async (_, entity: Entity | Entity[]) => { try { + const entityArray = [entity].flat(); + const entityNames = entityArray.map( + item => item.metadata.title ?? item.metadata.name, + ); await playlistApi.removePlaylistEntities( playlistId, [entity].flat().map(stringifyEntityRef), ); loadEntities(); + const message = + entityNames.length === 1 + ? `Removed entity '${entityNames[0]}'` + : `Removed entities: '${entityNames.join("', '")}'`; + + alertApi.post({ + message, + severity: 'success', + display: 'transient', + }); } catch (e) { errorApi.post(e); } }, - [errorApi, loadEntities, playlistApi, playlistId], + [errorApi, loadEntities, playlistApi, playlistId, alertApi], ); const singularTitleLowerCase = useTitle({ diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 9b15b97d5d..e9dd983ee4 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-proxy-backend +## 0.3.0 + +### Minor Changes + +- 7daf65bfcfa1: Defining proxy endpoints directly under the root `proxy` configuration key is deprecated. Endpoints should now be declared under `proxy.endpoints` instead. The `skipInvalidProxies` and `reviveConsumedRequestBodies` can now also be configured through static configuration. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/config@1.0.8 + +## 0.3.0-next.2 + +### Minor Changes + +- 7daf65bfcfa1: Defining proxy endpoints directly under the root `proxy` configuration key is deprecated. Endpoints should now be declared under `proxy.endpoints` instead. The `skipInvalidProxies` and `reviveConsumedRequestBodies` can now also be configured through static configuration. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.42-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 4b82382ed8c2: Fixed invalid configuration schema. The configuration schema may be more strict as a result. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + ## 0.2.42-next.0 ### Patch Changes diff --git a/plugins/proxy-backend/api-report.md b/plugins/proxy-backend/api-report.md index 12d799bdf7..9c5cf5f9ef 100644 --- a/plugins/proxy-backend/api-report.md +++ b/plugins/proxy-backend/api-report.md @@ -13,14 +13,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; export function createRouter(options: RouterOptions): Promise; // @alpha -export const proxyPlugin: ( - options?: - | { - skipInvalidProxies?: boolean | undefined; - reviveConsumedRequestBodies?: boolean | undefined; - } - | undefined, -) => BackendFeature; +export const proxyPlugin: () => BackendFeature; // @public (undocumented) export interface RouterOptions { diff --git a/plugins/proxy-backend/config.d.ts b/plugins/proxy-backend/config.d.ts index bc9cf7765a..79e1cfcba6 100644 --- a/plugins/proxy-backend/config.d.ts +++ b/plugins/proxy-backend/config.d.ts @@ -15,51 +15,63 @@ */ export interface Config { - /** - * A list of forwarding-proxies. Each key is a route to match, - * below the prefix that the proxy plugin is mounted on. It must - * start with a '/'. - */ proxy?: { - [key: string]: - | string - | { - /** - * Target of the proxy. Url string to be parsed with the url module. - */ - target: string; - /** - * Object with extra headers to be added to target requests. - */ - headers?: { - /** @visibility secret */ - Authorization?: string; - /** @visibility secret */ - authorization?: string; - /** @visibility secret */ - 'X-Api-Key'?: string; - /** @visibility secret */ - 'x-api-key'?: string; - [key: string]: string | undefined; + /** + * Rather than failing to start up, the proxy backend will instead just warn on invalid endpoints. + */ + skipInvalidProxies?: boolean; + + /** + * Revive request bodies that have already been consumed by earlier middleware. + */ + reviveConsumedRequestBodies?: boolean; + + /** + * A list of forwarding-proxies. Each key is a route to match, + * below the prefix that the proxy plugin is mounted on. It must + * start with a '/'. + */ + endpoints?: { + [key: string]: + | string + | { + /** + * Target of the proxy. Url string to be parsed with the url module. + */ + target: string; + /** + * Object with extra headers to be added to target requests. + */ + headers?: { + /** @visibility secret */ + Authorization?: string; + /** @visibility secret */ + authorization?: string; + /** @visibility secret */ + 'X-Api-Key'?: string; + /** @visibility secret */ + 'x-api-key'?: string; + [key: string]: string | undefined; + }; + /** + * Changes the origin of the host header to the target URL. Default: true. + */ + changeOrigin?: boolean; + /** + * Rewrite target's url path. Object-keys will be used as RegExp to match paths. + * If pathRewrite is not specified, it is set to a single rewrite that removes the entire prefix and route. + */ + pathRewrite?: { [regexp: string]: string }; + /** + * Limit the forwarded HTTP methods, for example allowedMethods: ['GET'] to enforce read-only access. + */ + allowedMethods?: string[]; + /** + * Limit the forwarded HTTP methods. By default, only the headers that are considered safe for CORS + * and headers that are set by the proxy will be forwarded. + */ + allowedHeaders?: string[]; }; - /** - * Changes the origin of the host header to the target URL. Default: true. - */ - changeOrigin?: boolean; - /** - * Rewrite target's url path. Object-keys will be used as RegExp to match paths. - * If pathRewrite is not specified, it is set to a single rewrite that removes the entire prefix and route. - */ - pathRewrite?: { [regexp: string]: string }; - /** - * Limit the forwarded HTTP methods, for example allowedMethods: ['GET'] to enforce read-only access. - */ - allowedMethods?: string[]; - /** - * Limit the forwarded HTTP methods. By default, only the headers that are considered safe for CORS - * and headers that are set by the proxy will be forwarded. - */ - allowedHeaders?: string[]; - }; + }; }; } diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 7ca210520c..b710d4d47d 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.2.42-next.0", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -48,7 +48,9 @@ "yup": "^0.32.9" }, "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", + "@backstage/config-loader": "workspace:^", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/proxy-backend/src/plugin.ts b/plugins/proxy-backend/src/plugin.ts index 66eeb4a7b2..e4c14b0037 100644 --- a/plugins/proxy-backend/src/plugin.ts +++ b/plugins/proxy-backend/src/plugin.ts @@ -26,32 +26,25 @@ import { createRouter } from './service/router'; * * @alpha */ -export const proxyPlugin = createBackendPlugin( - (options?: { - skipInvalidProxies?: boolean; - reviveConsumedRequestBodies?: boolean; - }) => ({ - pluginId: 'proxy', - register(env) { - env.registerInit({ - deps: { - config: coreServices.config, - discovery: coreServices.discovery, - logger: coreServices.logger, - httpRouter: coreServices.httpRouter, - }, - async init({ config, discovery, logger, httpRouter }) { - httpRouter.use( - await createRouter({ - config, - discovery, - logger: loggerToWinstonLogger(logger), - skipInvalidProxies: options?.skipInvalidProxies, - reviveConsumedRequestBodies: options?.reviveConsumedRequestBodies, - }), - ); - }, - }); - }, - }), -); +export const proxyPlugin = createBackendPlugin({ + pluginId: 'proxy', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + discovery: coreServices.discovery, + logger: coreServices.logger, + httpRouter: coreServices.httpRouter, + }, + async init({ config, discovery, logger, httpRouter }) { + httpRouter.use( + await createRouter({ + config, + discovery, + logger: loggerToWinstonLogger(logger), + }), + ); + }, + }); + }, +}); diff --git a/plugins/proxy-backend/src/service/router.config.test.ts b/plugins/proxy-backend/src/service/router.config.test.ts index 5f90f847e4..fffdcb5142 100644 --- a/plugins/proxy-backend/src/service/router.config.test.ts +++ b/plugins/proxy-backend/src/service/router.config.test.ts @@ -15,7 +15,11 @@ */ import { getVoidLogger, SingleHostDiscovery } from '@backstage/backend-common'; -import { ConfigReader } from '@backstage/config'; +import { + ConfigSources, + MutableConfigSource, + StaticConfigSource, +} from '@backstage/config-loader'; import express from 'express'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; @@ -56,34 +60,36 @@ describe('createRouter reloadable configuration', () => { const logger = getVoidLogger(); // Grab the subscriber function and use mutable config data to mock a config file change - let subscriber: () => void; - const mutableConfigData: any = { - backend: { - baseUrl: 'http://localhost:7007', - listen: { - port: 7007, - }, - }, - proxy: { - '/test': { - target: 'https://non-existing-example.com', - pathRewrite: { - '.*': '/', + const mutableConfigSource = MutableConfigSource.create({ data: {} }); + const config = await ConfigSources.toConfig( + ConfigSources.merge([ + StaticConfigSource.create({ + data: { + backend: { + baseUrl: 'http://localhost:7007', + listen: { + port: 7007, + }, + }, + proxy: { + endpoints: { + '/test': { + target: 'https://non-existing-example.com', + pathRewrite: { + '.*': '/', + }, + }, + }, + }, }, - }, - }, - }; + }), + mutableConfigSource, + ]), + ); - const mockConfig = Object.assign(new ConfigReader(mutableConfigData), { - subscribe: (s: () => void) => { - subscriber = s; - return { unsubscribe: () => {} }; - }, - }); - - const discovery = SingleHostDiscovery.fromConfig(mockConfig); + const discovery = SingleHostDiscovery.fromConfig(config); const router = await createRouter({ - config: mockConfig, + config, logger, discovery, }); @@ -100,13 +106,18 @@ describe('createRouter reloadable configuration', () => { expect(response1.status).toEqual(200); - mutableConfigData.proxy['/test2'] = { - target: 'https://non-existing-example.com', - pathRewrite: { - '.*': '/', + mutableConfigSource.setData({ + proxy: { + endpoints: { + '/test2': { + target: 'https://non-existing-example.com', + pathRewrite: { + '.*': '/', + }, + }, + }, }, - }; - subscriber!(); + }); const response2 = await agent.get('/test2'); diff --git a/plugins/proxy-backend/src/service/router.test.ts b/plugins/proxy-backend/src/service/router.test.ts index 9e8c909d25..2b58e52e94 100644 --- a/plugins/proxy-backend/src/service/router.test.ts +++ b/plugins/proxy-backend/src/service/router.test.ts @@ -15,6 +15,7 @@ */ import { getVoidLogger, SingleHostDiscovery } from '@backstage/backend-common'; +import { mockServices } from '@backstage/backend-test-utils'; import { ConfigReader } from '@backstage/config'; import { Request, Response } from 'express'; import * as http from 'http'; @@ -45,10 +46,12 @@ describe('createRouter', () => { }, }, proxy: { - '/test': { - target: 'https://example.com', - headers: { - Authorization: 'Bearer supersecret', + endpoints: { + '/test': { + target: 'https://example.com', + headers: { + Authorization: 'Bearer supersecret', + }, }, }, }, @@ -68,6 +71,32 @@ describe('createRouter', () => { expect(router).toBeDefined(); }); + it('supports deprecated proxy configuration', async () => { + const router = await createRouter({ + config: mockServices.rootConfig({ + data: { + proxy: { + '/test': { + target: 'https://example.com', + headers: { + Authorization: 'Bearer supersecret', + }, + }, + }, + }, + }), + logger, + discovery, + }); + expect(router).toBeDefined(); + expect(mockCreateProxyMiddleware).toHaveBeenCalledWith( + expect.any(Function), + expect.objectContaining({ + target: 'https://example.com', + }), + ); + }); + it('revives request bodies when set', async () => { const router = await createRouter({ config, @@ -112,9 +141,11 @@ describe('createRouter', () => { }, // no target would cause the buildMiddleware to fail proxy: { - '/test': { - headers: { - Authorization: 'Bearer supersecret', + endpoints: { + '/test': { + headers: { + Authorization: 'Bearer supersecret', + }, }, }, }, @@ -141,9 +172,11 @@ describe('createRouter', () => { }, // no target would cause the buildMiddleware to fail proxy: { - '/test': { - headers: { - Authorization: 'Bearer supersecret', + endpoints: { + '/test': { + headers: { + Authorization: 'Bearer supersecret', + }, }, }, }, diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 6912965659..61fddb60ed 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -191,6 +191,31 @@ export function buildMiddleware( return createProxyMiddleware(filter, fullConfig); } +function readProxyConfig(config: Config, logger: Logger): unknown { + const endpoints = config.getOptionalConfig('proxy.endpoints')?.get(); + if (endpoints) { + return endpoints; + } + + const root = config.getOptionalConfig('proxy')?.get(); + if (!root) { + return {}; + } + + const rootEndpoints = Object.fromEntries( + Object.entries(root).filter(([key]) => key.startsWith('/')), + ); + if (Object.keys(rootEndpoints).length === 0) { + return {}; + } + + logger.warn( + "Configuring proxy endpoints in the root 'proxy' configuration is deprecated. Move this configuration to 'proxy.endpoints' instead.", + ); + + return rootEndpoints; +} + /** * Creates a new {@link https://expressjs.com/en/api.html#router | "express router"} that proxy each target configured under the `proxy` key of the config * @example @@ -215,25 +240,39 @@ export async function createRouter( const router = Router(); let currentRouter = Router(); + const skipInvalidProxies = + options.skipInvalidProxies ?? + options.config.getOptionalBoolean('proxy.skipInvalidProxies') ?? + false; + const reviveConsumedRequestBodies = + options.reviveConsumedRequestBodies ?? + options.config.getOptionalBoolean('proxy.reviveConsumedRequestBodies') ?? + false; + const proxyOptions = { + skipInvalidProxies, + reviveConsumedRequestBodies, + logger: options.logger, + }; + const externalUrl = await options.discovery.getExternalBaseUrl('proxy'); const { pathname: pathPrefix } = new URL(externalUrl); - const proxyConfig = options.config.getOptional('proxy') ?? {}; - configureMiddlewares(options, currentRouter, pathPrefix, proxyConfig); + const proxyConfig = readProxyConfig(options.config, options.logger); + configureMiddlewares(proxyOptions, currentRouter, pathPrefix, proxyConfig); router.use((...args) => currentRouter(...args)); if (options.config.subscribe) { let currentKey = JSON.stringify(proxyConfig); options.config.subscribe(() => { - const newProxyConfig = options.config.getOptional('proxy') ?? {}; + const newProxyConfig = readProxyConfig(options.config, options.logger); const newKey = JSON.stringify(newProxyConfig); if (currentKey !== newKey) { currentKey = newKey; currentRouter = Router(); configureMiddlewares( - options, + proxyOptions, currentRouter, pathPrefix, newProxyConfig, @@ -246,7 +285,11 @@ export async function createRouter( } function configureMiddlewares( - options: RouterOptions, + options: { + reviveConsumedRequestBodies: boolean; + skipInvalidProxies: boolean; + logger: Logger; + }, router: express.Router, pathPrefix: string, proxyConfig: any, diff --git a/plugins/puppetdb/CHANGELOG.md b/plugins/puppetdb/CHANGELOG.md index dbd5fde6df..7253466e95 100644 --- a/plugins/puppetdb/CHANGELOG.md +++ b/plugins/puppetdb/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-puppetdb +## 0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/puppetdb/package.json b/plugins/puppetdb/package.json index ef9370200e..5973e91ee5 100644 --- a/plugins/puppetdb/package.json +++ b/plugins/puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-puppetdb", "description": "Backstage plugin to visualize resource information and Puppet facts from PuppetDB.", - "version": "0.1.4-next.0", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index 37f7d42ec8..c90db1c135 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-rollbar-backend +## 0.1.45 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + +## 0.1.45-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## 0.1.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + ## 0.1.45-next.0 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index e19f4c43d0..cdd3432a1f 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar-backend", "description": "A Backstage backend plugin that integrates towards Rollbar", - "version": "0.1.45-next.0", + "version": "0.1.45", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 6bacf806bc..c8664e3c1e 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-rollbar +## 0.4.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.4.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.4.21-next.0 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 8c17895e48..7b2c5ce5bb 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar", "description": "A Backstage plugin that integrates towards Rollbar", - "version": "0.4.21-next.0", + "version": "0.4.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index f48ac96986..3d77b220e1 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.2.1 + +### Patch Changes + +- 0a9b3b14e904: Added example for the `confluence:transform:markdown` that will show in the installed actions list +- 12a8c94eda8d: Add package repository and homepage metadata +- 0d347efc8f18: Use `fetchContents` directly instead of a `fetchPlainAction` +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.1-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- 0d347efc8f18: Use `fetchContents` directly instead of a `fetchPlainAction` +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.2.1-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index b8ce9ea8d9..1f93176163 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", - "version": "0.2.1-next.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,7 +34,6 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", "fs-extra": "10.1.0", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.examples.test.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.examples.test.ts new file mode 100644 index 0000000000..e5aeadbf00 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.examples.test.ts @@ -0,0 +1,162 @@ +/* + * 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 { PassThrough } from 'stream'; +import { createConfluenceToMarkdownAction } from './confluenceToMarkdown'; +import { getVoidLogger } from '@backstage/backend-common'; +import { UrlReader } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import mockFs from 'mock-fs'; +import os from 'os'; +import { readFile, writeFile, createWriteStream } from 'fs-extra'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { examples } from './confluenceToMarkdown.examples'; +import yaml from 'yaml'; +import { ActionContext } from '@backstage/plugin-scaffolder-node'; + +jest.mock('fs-extra', () => ({ + mkdirSync: jest.fn(), + readFile: jest.fn().mockResolvedValue('File contents'), + writeFile: jest.fn().mockImplementation(() => { + return Promise.resolve(); + }), + outputFile: jest.fn(), + openSync: jest.fn(), + createWriteStream: jest.fn().mockReturnValue(new PassThrough()), + ensureDir: jest.fn(), +})); + +describe('confluence:transform:markdown examples', () => { + const baseUrl = `https://confluence.example.com`; + const worker = setupServer(); + setupRequestMockHandlers(worker); + + const config = new ConfigReader({ + confluence: { + baseUrl: baseUrl, + auth: { + token: 'fake_token', + }, + }, + }); + + const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'github.com', token: 'token' }], + }, + }), + ); + + let reader: UrlReader; + let mockContext: ActionContext<{ + confluenceUrls: string[]; + repoUrl: string; + }>; + + const logger = getVoidLogger(); + jest.spyOn(logger, 'info'); + + const mockTmpDir = os.tmpdir(); + + beforeEach(() => { + reader = { + readUrl: jest.fn(), + readTree: jest.fn().mockResolvedValue({ + dir: jest.fn(), + }), + search: jest.fn(), + }; + mockContext = { + input: yaml.parse(examples[0].example).steps[0].input, + workspacePath: '/tmp', + logger, + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir), + }; + mockFs({ [`${mockTmpDir}/src/docs`]: {} }); + }); + afterEach(() => { + jest.clearAllMocks(); + mockFs.restore(); + }); + + it('should call confluence to markdown action successfully with results array', async () => { + const options = { + reader, + integrations, + config, + }; + const responseBody = { + results: [ + { + id: '4444444', + type: 'page', + title: 'Testing', + body: { + export_view: { + value: '

hello world

', + }, + }, + }, + ], + }; + const responseBodyTwo = { + results: [ + { + id: '4444444', + type: 'attachment', + title: 'testing.pdf', + metadata: { + mediaType: 'application/pdf', + }, + _links: { + download: '/download/attachments/4444444/testing.pdf', + }, + }, + ], + }; + + worker.use( + rest.get(`${baseUrl}/rest/api/content`, (_, res, ctx) => + res(ctx.status(200, 'OK'), ctx.json(responseBody)), + ), + rest.get( + `${baseUrl}/rest/api/content/4444444/child/attachment`, + (_, res, ctx) => res(ctx.status(200, 'OK'), ctx.json(responseBodyTwo)), + ), + rest.get( + `${baseUrl}/download/attachments/4444444/testing.pdf`, + (_, res, ctx) => res(ctx.status(200, 'OK'), ctx.body('hello')), + ), + ); + + const action = createConfluenceToMarkdownAction(options); + + await action.handler(mockContext); + + expect(logger.info).toHaveBeenCalledWith( + `Fetching the mkdocs.yml catalog from https://github.com/organization-name/repo-name/blob/main/mkdocs.yml`, + ); + expect(logger.info).toHaveBeenCalledTimes(5); + expect(createWriteStream).toHaveBeenCalledTimes(1); + expect(readFile).toHaveBeenCalledTimes(1); + expect(writeFile).toHaveBeenCalledTimes(1); + }); +}); diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.examples.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.examples.ts new file mode 100644 index 0000000000..e5d10644a6 --- /dev/null +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.examples.ts @@ -0,0 +1,41 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Downloads content from provided Confluence URLs and converts it to Markdown.', + example: yaml.stringify({ + steps: [ + { + action: 'confluence:transform:markdown', + id: 'confluence-transform-markdown', + name: 'Transform Confluence content to Markdown', + input: { + confluenceUrls: [ + 'https://confluence.example.com/display/SPACEKEY/Page+Title', + ], + repoUrl: + 'https://github.com/organization-name/repo-name/blob/main/mkdocs.yml', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts index 428f392555..e88f42e39c 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Config } from '@backstage/config'; import { UrlReader } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents } from '@backstage/plugin-scaffolder-backend'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; import { InputError, ConflictError } from '@backstage/errors'; import { NodeHtmlMarkdown } from 'node-html-markdown'; import fs from 'fs-extra'; @@ -30,6 +33,7 @@ import { createConfluenceVariables, getConfluenceConfig, } from './helpers'; +import { examples } from './confluenceToMarkdown.examples'; /** * @public @@ -50,6 +54,8 @@ export const createConfluenceToMarkdownAction = (options: { repoUrl: string; }>({ id: 'confluence:transform:markdown', + description: 'Transforms Confluence content to Markdown', + examples, schema: { input: { properties: { diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 390e1ae7bd..0c207a7d93 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.24 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.2.24-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.24-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.2.24-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index f95771822b..6283e20a78 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", - "version": "0.2.24-next.0", + "version": "0.2.24", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,7 +33,6 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", "command-exists": "^1.2.9", diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts index 9d7f06afc8..f944b6e598 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts @@ -33,8 +33,8 @@ const executeShellCommand = jest.fn(); const commandExists = jest.fn(); const fetchContents = jest.fn(); -jest.mock('@backstage/plugin-scaffolder-backend', () => ({ - ...jest.requireActual('@backstage/plugin-scaffolder-backend'), +jest.mock('@backstage/plugin-scaffolder-node', () => ({ + ...jest.requireActual('@backstage/plugin-scaffolder-node'), fetchContents: (...args: any[]) => fetchContents(...args), executeShellCommand: (...args: any[]) => executeShellCommand(...args), })); diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts index b63a044a20..8b084e6377 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts @@ -27,10 +27,10 @@ import fs from 'fs-extra'; import path, { resolve as resolvePath } from 'path'; import { Writable } from 'stream'; import { + createTemplateAction, fetchContents, executeShellCommand, -} from '@backstage/plugin-scaffolder-backend'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +} from '@backstage/plugin-scaffolder-node'; export class CookiecutterRunner { private readonly containerRunner?: ContainerRunner; diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 95b481acb0..ba75fd998f 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + +## 0.2.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.2.3-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 5099bb473b..e1f9a2fa2a 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.2.3-next.0", + "version": "0.2.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index ddf314f2bf..97cb1b9dec 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.17 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/integration@1.6.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.4.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.4.17-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- c186c631b429: Import helpers from the node package instead of the backend package +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/integration@1.5.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.4.17-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 9bc7bbf06f..c6feb27fef 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", "description": "A module for the scaffolder backend that lets you template projects using Rails", - "version": "0.4.17-next.0", + "version": "0.4.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,7 +33,6 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", "command-exists": "^1.2.9", diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.test.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.test.ts index 37d0bcac4a..2a93644f57 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.test.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.test.ts @@ -15,8 +15,8 @@ */ const mockRailsTemplater = { run: jest.fn() }; -jest.mock('@backstage/plugin-scaffolder-backend', () => ({ - ...jest.requireActual('@backstage/plugin-scaffolder-backend'), +jest.mock('@backstage/plugin-scaffolder-node', () => ({ + ...jest.requireActual('@backstage/plugin-scaffolder-node'), fetchContents: jest.fn(), })); jest.mock('./railsNewRunner', () => { @@ -39,7 +39,7 @@ import os from 'os'; import { resolve as resolvePath } from 'path'; import { PassThrough } from 'stream'; import { createFetchRailsAction } from './index'; -import { fetchContents } from '@backstage/plugin-scaffolder-backend'; +import { fetchContents } from '@backstage/plugin-scaffolder-node'; describe('fetch:rails', () => { const integrations = ScmIntegrations.fromConfig( diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts index 2a8a2554a2..2b34f978e3 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts @@ -19,8 +19,10 @@ import { JsonObject } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import fs from 'fs-extra'; -import { fetchContents } from '@backstage/plugin-scaffolder-backend'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; import { resolve as resolvePath } from 'path'; import { RailsNewRunner } from './railsNewRunner'; diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.test.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.test.ts index 433a1b7965..d7e7b12158 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.test.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.test.ts @@ -17,7 +17,7 @@ const executeShellCommand = jest.fn(); const commandExists = jest.fn(); -jest.mock('@backstage/plugin-scaffolder-backend', () => ({ +jest.mock('@backstage/plugin-scaffolder-node', () => ({ executeShellCommand: (...args: any[]) => executeShellCommand(...args), })); jest.mock( diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts index 0ed02e293c..7e78c25d62 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts @@ -17,7 +17,7 @@ import { ContainerRunner } from '@backstage/backend-common'; import fs from 'fs-extra'; import path from 'path'; -import { executeShellCommand } from '@backstage/plugin-scaffolder-backend'; +import { executeShellCommand } from '@backstage/plugin-scaffolder-node'; import commandExists from 'command-exists'; import { railsArgumentResolver, diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 47c9c7ec51..f252b34761 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.1.8 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.1.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + +## 0.1.8-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.1.8-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 0193427ec1..6114aa3088 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.8-next.0", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index eed4d1a4c0..b188a8bdf0 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.2.21 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 0.2.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + +## 0.2.21-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + ## 0.2.21-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 12e05629db..9319c8001f 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.2.21-next.0", + "version": "0.2.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 477d7ebb5e..67827a6384 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,95 @@ # @backstage/plugin-scaffolder-backend +## 1.16.0 + +### Minor Changes + +- e514aac3eac0: Introduce `each` property on action steps, allowing them to be ran repeatedly. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 349611126ae2: Removed the options from the alpha `scaffolderPlugin` export. To extend the scaffolder plugin you instead now use the available extension points, `scaffolderActionsExtensionPoint`, `scaffolderTaskBrokerExtensionPoint`, and `scaffolderTemplatingExtensionPoint`. +- 33c76caef72a: Added examples for the `fs:delete` and `fs:rename` actions +- e07a4914f621: Deprecated the following type exports, which have been moved to `@backstage/plugin-scaffolder-node` instead: + + - `TemplateFilter` + - `TemplateGlobal` + - `TaskStatus` + - `TaskCompletionState` + - `SerializedTask` + - `TaskEventType` + - `SerializedTaskEvent` + - `TaskBrokerDispatchResult` + - `TaskBrokerDispatchOptions` + - `TaskContext` + - `TaskBroker` + +- 0b1d775be05b: Adds examples to a few scaffolder actions. +- 88bc6e27a213: The `concurrentTasksLimit` option can now be configured via static configuration as well. Setting it to 0 will now also disable the task worker. +- 0f873325068d: Add examples for `github:repo:create` and `github:repo:push` scaffolder actions. +- 5c28ebc79fd6: Updated dependency `esbuild` to `^0.19.0`. +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-catalog-backend@1.12.0 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-scaffolder-node@0.2.0 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/integration@1.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + +## 1.15.2-next.2 + +### Patch Changes + +- 33c76caef72a: Added examples for the fs:delete and fs:rename actions +- 0b1d775be05b: Adds examples to a few scaffolder actions. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.12.0-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/plugin-scaffolder-node@0.1.6-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 1.15.2-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-catalog-backend@1.12.0-next.1 + - @backstage/plugin-scaffolder-node@0.1.6-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-scaffolder-common@1.3.2 + ## 1.15.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend/alpha-api-report.md b/plugins/scaffolder-backend/alpha-api-report.md index 363fcafd17..c1758f9b17 100644 --- a/plugins/scaffolder-backend/alpha-api-report.md +++ b/plugins/scaffolder-backend/alpha-api-report.md @@ -12,11 +12,7 @@ import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { ResourcePermission } from '@backstage/plugin-permission-common'; -import { TaskBroker } from '@backstage/plugin-scaffolder-backend'; -import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { TemplateFilter } from '@backstage/plugin-scaffolder-backend'; -import { TemplateGlobal } from '@backstage/plugin-scaffolder-backend'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; // @alpha @@ -90,18 +86,7 @@ export const scaffolderActionConditions: Conditions<{ }>; // @alpha -export const scaffolderPlugin: ( - options?: ScaffolderPluginOptions | undefined, -) => BackendFeature; - -// @alpha -export type ScaffolderPluginOptions = { - actions?: TemplateAction[]; - taskWorkers?: number; - taskBroker?: TaskBroker; - additionalTemplateFilters?: Record; - additionalTemplateGlobals?: Record; -}; +export const scaffolderPlugin: () => BackendFeature; // @alpha export const scaffolderTemplateConditions: Conditions<{ diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 1957c3b3d2..7fe4964827 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -3,8 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - import { ActionContext as ActionContext_2 } from '@backstage/plugin-scaffolder-node'; import { CatalogApi } from '@backstage/catalog-client'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; @@ -13,16 +11,17 @@ import { Config } from '@backstage/config'; import { createPullRequest } from 'octokit-plugin-create-pull-request'; import { Duration } from 'luxon'; import { Entity } from '@backstage/catalog-model'; +import { executeShellCommand as executeShellCommand_2 } from '@backstage/plugin-scaffolder-node'; +import { ExecuteShellCommandOptions } from '@backstage/plugin-scaffolder-node'; import express from 'express'; +import { fetchContents as fetchContents_2 } from '@backstage/plugin-scaffolder-node'; import { GithubCredentialsProvider } from '@backstage/integration'; import { HumanDuration } from '@backstage/types'; import { IdentityApi } from '@backstage/plugin-auth-node'; import { JsonObject } from '@backstage/types'; -import { JsonValue } from '@backstage/types'; import { Knex } from 'knex'; import { LocationSpec } from '@backstage/plugin-catalog-common'; import { Logger } from 'winston'; -import { Observable } from '@backstage/types'; import { Octokit } from 'octokit'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; @@ -34,16 +33,25 @@ import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder- import { Schema } from 'jsonschema'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ScmIntegrations } from '@backstage/integration'; -import { SpawnOptionsWithoutStdio } from 'child_process'; +import { SerializedTask as SerializedTask_2 } from '@backstage/plugin-scaffolder-node'; +import { SerializedTaskEvent as SerializedTaskEvent_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskBroker as TaskBroker_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskBrokerDispatchOptions as TaskBrokerDispatchOptions_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskBrokerDispatchResult as TaskBrokerDispatchResult_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskCompletionState as TaskCompletionState_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskContext as TaskContext_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskEventType as TaskEventType_2 } from '@backstage/plugin-scaffolder-node'; import { TaskSecrets as TaskSecrets_2 } from '@backstage/plugin-scaffolder-node'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { TaskStatus as TaskStatus_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateActionOptions } from '@backstage/plugin-scaffolder-node'; import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { TemplateFilter as TemplateFilter_2 } from '@backstage/plugin-scaffolder-node'; +import { TemplateGlobal as TemplateGlobal_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; import { UrlReader } from '@backstage/backend-common'; -import { Writable } from 'stream'; import { ZodType } from 'zod'; import { ZodTypeDef } from 'zod'; @@ -816,17 +824,11 @@ export type DatabaseTaskStoreOptions = { database: PluginDatabaseManager | Knex; }; -// @public -export const executeShellCommand: (options: RunCommandOptions) => Promise; +// @public @deprecated +export const executeShellCommand: typeof executeShellCommand_2; -// @public -export function fetchContents(options: { - reader: UrlReader; - integrations: ScmIntegrations; - baseUrl?: string; - fetchUrl?: string; - outputPath: string; -}): Promise; +// @public @deprecated +export const fetchContents: typeof fetchContents_2; // @public (undocumented) export type OctokitWithPullRequestPluginClient = Octokit & { @@ -876,13 +878,8 @@ export interface RouterOptions { taskWorkers?: number; } -// @public (undocumented) -export type RunCommandOptions = { - command: string; - args: string[]; - options?: SpawnOptionsWithoutStdio; - logStream?: Writable; -}; +// @public @deprecated +export type RunCommandOptions = ExecuteShellCommandOptions; // @public (undocumented) export class ScaffolderEntitiesProcessor implements CatalogProcessor { @@ -898,89 +895,29 @@ export class ScaffolderEntitiesProcessor implements CatalogProcessor { validateEntityKind(entity: Entity): Promise; } -// @public -export type SerializedTask = { - id: string; - spec: TaskSpec; - status: TaskStatus; - createdAt: string; - lastHeartbeatAt?: string; - createdBy?: string; - secrets?: TaskSecrets_2; -}; +// @public @deprecated +export type SerializedTask = SerializedTask_2; -// @public -export type SerializedTaskEvent = { - id: number; - taskId: string; - body: JsonObject; - type: TaskEventType; - createdAt: string; -}; +// @public @deprecated +export type SerializedTaskEvent = SerializedTaskEvent_2; -// @public -export interface TaskBroker { - // (undocumented) - cancel?(taskId: string): Promise; - // (undocumented) - claim(): Promise; - // (undocumented) - dispatch( - options: TaskBrokerDispatchOptions, - ): Promise; - // (undocumented) - event$(options: { taskId: string; after: number | undefined }): Observable<{ - events: SerializedTaskEvent[]; - }>; - // (undocumented) - get(taskId: string): Promise; - // (undocumented) - list?(options?: { createdBy?: string }): Promise<{ - tasks: SerializedTask[]; - }>; - // (undocumented) - vacuumTasks(options: { timeoutS: number }): Promise; -} +// @public @deprecated +export type TaskBroker = TaskBroker_2; -// @public -export type TaskBrokerDispatchOptions = { - spec: TaskSpec; - secrets?: TaskSecrets_2; - createdBy?: string; -}; +// @public @deprecated +export type TaskBrokerDispatchOptions = TaskBrokerDispatchOptions_2; -// @public -export type TaskBrokerDispatchResult = { - taskId: string; -}; +// @public @deprecated +export type TaskBrokerDispatchResult = TaskBrokerDispatchResult_2; -// @public -export type TaskCompletionState = 'failed' | 'completed'; +// @public @deprecated +export type TaskCompletionState = TaskCompletionState_2; -// @public -export interface TaskContext { - // (undocumented) - cancelSignal: AbortSignal; - // (undocumented) - complete(result: TaskCompletionState, metadata?: JsonObject): Promise; - // (undocumented) - createdBy?: string; - // (undocumented) - done: boolean; - // (undocumented) - emitLog(message: string, logMetadata?: JsonObject): Promise; - // (undocumented) - getWorkspaceName(): Promise; - // (undocumented) - isDryRun?: boolean; - // (undocumented) - secrets?: TaskSecrets_2; - // (undocumented) - spec: TaskSpec; -} +// @public @deprecated +export type TaskContext = TaskContext_2; -// @public -export type TaskEventType = 'completion' | 'log' | 'cancelled'; +// @public @deprecated +export type TaskEventType = TaskEventType_2; // @public export class TaskManager implements TaskContext { @@ -1012,13 +949,8 @@ export class TaskManager implements TaskContext { // @public @deprecated (undocumented) export type TaskSecrets = TaskSecrets_2; -// @public -export type TaskStatus = - | 'cancelled' - | 'completed' - | 'failed' - | 'open' - | 'processing'; +// @public @deprecated +export type TaskStatus = TaskStatus_2; // @public export interface TaskStore { @@ -1115,13 +1047,11 @@ export class TemplateActionRegistry { register(action: TemplateAction_2): void; } -// @public (undocumented) -export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; +// @public @deprecated (undocumented) +export type TemplateFilter = TemplateFilter_2; -// @public (undocumented) -export type TemplateGlobal = - | ((...args: JsonValue[]) => JsonValue | undefined) - | JsonValue; +// @public @deprecated (undocumented) +export type TemplateGlobal = TemplateGlobal_2; // @public (undocumented) export type TemplatePermissionRuleInput< diff --git a/plugins/scaffolder-backend/config.d.ts b/plugins/scaffolder-backend/config.d.ts index 7883284165..60ccd52257 100644 --- a/plugins/scaffolder-backend/config.d.ts +++ b/plugins/scaffolder-backend/config.d.ts @@ -28,5 +28,14 @@ export interface Config { * The commit message used when new components are created. */ defaultCommitMessage?: string; + + /** + * Sets the number of concurrent tasks that can be run at any given time on the TaskWorker. + * + * Defaults to 10. + * + * Set to 0 to disable task workers altogether. + */ + concurrentTasksLimit?: number; }; } diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 0978a828bb..636074393f 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "1.15.2-next.0", + "version": "1.16.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -110,7 +110,7 @@ "@types/nunjucks": "^3.1.4", "@types/supertest": "^2.0.8", "@types/zen-observable": "^0.8.0", - "esbuild": "^0.18.0", + "esbuild": "^0.19.0", "jest-when": "^3.1.0", "mock-fs": "^5.1.0", "msw": "^1.0.0", diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 90dcc868e2..98bd4a1d66 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -23,120 +23,104 @@ import { ScmIntegrations } from '@backstage/integration'; import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; import { scaffolderActionsExtensionPoint, - ScaffolderActionsExtensionPoint, + scaffolderTaskBrokerExtensionPoint, + scaffolderTemplatingExtensionPoint, + TaskBroker, TemplateAction, -} from '@backstage/plugin-scaffolder-node'; -import { TemplateFilter, TemplateGlobal, - TaskBroker, -} from '@backstage/plugin-scaffolder-backend'; +} from '@backstage/plugin-scaffolder-node'; import { createBuiltinActions } from './scaffolder'; import { createRouter } from './service/router'; /** - * Catalog plugin options + * Scaffolder plugin * * @alpha */ -export type ScaffolderPluginOptions = { - actions?: TemplateAction[]; - taskWorkers?: number; - taskBroker?: TaskBroker; - additionalTemplateFilters?: Record; - additionalTemplateGlobals?: Record; -}; +export const scaffolderPlugin = createBackendPlugin({ + pluginId: 'scaffolder', + register(env) { + const addedActions = new Array>(); + env.registerExtensionPoint(scaffolderActionsExtensionPoint, { + addActions(...newActions: TemplateAction[]) { + addedActions.push(...newActions); + }, + }); -class ScaffolderActionsExtensionPointImpl - implements ScaffolderActionsExtensionPoint -{ - #actions = new Array>(); + let taskBroker: TaskBroker | undefined; + env.registerExtensionPoint(scaffolderTaskBrokerExtensionPoint, { + setTaskBroker(newTaskBroker) { + if (taskBroker) { + throw new Error('Task broker may only be set once'); + } + taskBroker = newTaskBroker; + }, + }); - addActions(...actions: TemplateAction[]): void { - this.#actions.push(...actions); - } + const additionalTemplateFilters: Record = {}; + const additionalTemplateGlobals: Record = {}; + env.registerExtensionPoint(scaffolderTemplatingExtensionPoint, { + addTemplateFilters(newFilters) { + Object.assign(additionalTemplateFilters, newFilters); + }, + addTemplateGlobals(newGlobals) { + Object.assign(additionalTemplateGlobals, newGlobals); + }, + }); - get actions() { - return this.#actions; - } -} + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + reader: coreServices.urlReader, + permissions: coreServices.permissions, + database: coreServices.database, + httpRouter: coreServices.httpRouter, + catalogClient: catalogServiceRef, + }, + async init({ + logger, + config, + reader, + database, + httpRouter, + catalogClient, + permissions, + }) { + const log = loggerToWinstonLogger(logger); -/** - * Catalog plugin - * - * @alpha - */ -export const scaffolderPlugin = createBackendPlugin( - (options?: ScaffolderPluginOptions) => ({ - pluginId: 'scaffolder', - register(env) { - const actionsExtensions = new ScaffolderActionsExtensionPointImpl(); - - env.registerExtensionPoint( - scaffolderActionsExtensionPoint, - actionsExtensions, - ); - - env.registerInit({ - deps: { - logger: coreServices.logger, - config: coreServices.config, - reader: coreServices.urlReader, - permissions: coreServices.permissions, - database: coreServices.database, - httpRouter: coreServices.httpRouter, - catalogClient: catalogServiceRef, - }, - async init({ - logger, - config, - reader, - database, - httpRouter, - catalogClient, - permissions, - }) { - const { - additionalTemplateFilters, - taskBroker, - taskWorkers, - additionalTemplateGlobals, - } = options ?? {}; - const log = loggerToWinstonLogger(logger); - - const actions = options?.actions || [ - ...actionsExtensions.actions, - ...createBuiltinActions({ - integrations: ScmIntegrations.fromConfig(config), - catalogClient, - reader, - config, - additionalTemplateFilters, - additionalTemplateGlobals, - }), - ]; - - const actionIds = actions.map(action => action.id).join(', '); - log.info( - `Starting scaffolder with the following actions enabled ${actionIds}`, - ); - - const router = await createRouter({ - logger: log, - config, - database, + const actions = [ + ...addedActions, + ...createBuiltinActions({ + integrations: ScmIntegrations.fromConfig(config), catalogClient, reader, - actions, - taskBroker, - taskWorkers, + config, additionalTemplateFilters, additionalTemplateGlobals, - permissions, - }); - httpRouter.use(router); - }, - }); - }, - }), -); + }), + ]; + + const actionIds = actions.map(action => action.id).join(', '); + log.info( + `Starting scaffolder with the following actions enabled ${actionIds}`, + ); + + const router = await createRouter({ + logger: log, + config, + database, + catalogClient, + reader, + actions, + taskBroker, + additionalTemplateFilters, + additionalTemplateGlobals, + permissions, + }); + httpRouter.use(router); + }, + }); + }, +}); diff --git a/plugins/scaffolder-backend/src/alpha.ts b/plugins/scaffolder-backend/src/alpha.ts index 713f2569aa..e1b4bc1067 100644 --- a/plugins/scaffolder-backend/src/alpha.ts +++ b/plugins/scaffolder-backend/src/alpha.ts @@ -17,4 +17,3 @@ export * from './modules'; export * from './service'; export { scaffolderPlugin } from './ScaffolderPlugin'; -export type { ScaffolderPluginOptions } from './ScaffolderPlugin'; diff --git a/plugins/scaffolder-backend/src/deprecated.ts b/plugins/scaffolder-backend/src/deprecated.ts index f8358b9071..d1a0f9ad5b 100644 --- a/plugins/scaffolder-backend/src/deprecated.ts +++ b/plugins/scaffolder-backend/src/deprecated.ts @@ -19,6 +19,9 @@ import { createTemplateAction as createTemplateActionNode, TaskSecrets as TaskSecretsNode, TemplateAction as TemplateActionNode, + executeShellCommand as executeShellCommandNode, + ExecuteShellCommandOptions as ExecuteShellCommandOptionsNode, + fetchContents as fetchContentsNode, } from '@backstage/plugin-scaffolder-node'; import { JsonObject } from '@backstage/types'; @@ -47,3 +50,28 @@ export type TaskSecrets = TaskSecretsNode; */ export type TemplateAction = TemplateActionNode; + +/** + * Options for {@link executeShellCommand}. + * + * @public + * @deprecated Use `ExecuteShellCommandOptions` from `@backstage/plugin-scaffolder-node` instead + */ +export type RunCommandOptions = ExecuteShellCommandOptionsNode; + +/** + * Run a command in a sub-process, normally a shell command. + * + * @public + * @deprecated Use `executeShellCommand` from `@backstage/plugin-scaffolder-node` instead + */ +export const executeShellCommand = executeShellCommandNode; + +/** + * A helper function that reads the contents of a directory from the given URL. + * Can be used in your own actions, and also used behind fetch:template and fetch:plain + * + * @public + * @deprecated Use `fetchContents` from `@backstage/plugin-scaffolder-node` instead + */ +export const fetchContents = fetchContentsNode; diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts index 488604e981..701863c7f6 100644 --- a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -16,6 +16,10 @@ import { Isolate } from 'isolated-vm'; import { resolvePackagePath } from '@backstage/backend-common'; +import { + TemplateFilter as _TemplateFilter, + TemplateGlobal as _TemplateGlobal, +} from '@backstage/plugin-scaffolder-node'; import fs from 'fs-extra'; import { JsonValue } from '@backstage/types'; @@ -86,13 +90,17 @@ const { render, renderCompat } = (() => { })(); `; -/** @public */ -export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; +/** + * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. + */ +export type TemplateFilter = _TemplateFilter; -/** @public */ -export type TemplateGlobal = - | ((...args: JsonValue[]) => JsonValue | undefined) - | JsonValue; +/** + * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. + */ +export type TemplateGlobal = _TemplateGlobal; export interface SecureTemplaterOptions { /* Enables jinja compatibility and the "jsonify" filter */ diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.examples.test.ts new file mode 100644 index 0000000000..161ff9d1de --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.examples.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { PassThrough } from 'stream'; +import os from 'os'; +import { getVoidLogger } from '@backstage/backend-common'; +import { CatalogApi } from '@backstage/catalog-client'; +import { Entity } from '@backstage/catalog-model'; +import { createFetchCatalogEntityAction } from './fetch'; +import { examples } from './fetch.examples'; +import yaml from 'yaml'; + +describe('catalog:fetch examples', () => { + const getEntityByRef = jest.fn(); + const getEntitiesByRefs = jest.fn(); + + const catalogClient = { + getEntityByRef: getEntityByRef, + getEntitiesByRefs: getEntitiesByRefs, + }; + + const action = createFetchCatalogEntityAction({ + catalogClient: catalogClient as unknown as CatalogApi, + }); + + const mockContext = { + workspacePath: os.tmpdir(), + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + secrets: { backstageToken: 'secret' }, + }; + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('fetch single entity', () => { + it('should return entity from catalog', async () => { + getEntityByRef.mockReturnValueOnce({ + metadata: { + namespace: 'default', + name: 'name', + }, + kind: 'Component', + } as Entity); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(getEntityByRef).toHaveBeenCalledWith('component:default/name', { + token: 'secret', + }); + expect(mockContext.output).toHaveBeenCalledWith('entity', { + metadata: { + namespace: 'default', + name: 'name', + }, + kind: 'Component', + }); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.examples.ts new file mode 100644 index 0000000000..6459b17a71 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.examples.ts @@ -0,0 +1,51 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Fetch entity by reference', + example: yaml.stringify({ + steps: [ + { + action: 'catalog:fetch', + id: 'fetch', + name: 'Fetch catalog entity', + input: { + entityRef: 'component:default/name', + }, + }, + ], + }), + }, + { + description: 'Fetch multiple entities by reference', + example: yaml.stringify({ + steps: [ + { + action: 'catalog:fetch', + id: 'fetchMultiple', + name: 'Fetch catalog entities', + input: { + entityRefs: ['component:default/name'], + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts index 437da8ae73..75c4bc94ce 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/fetch.ts @@ -16,45 +16,12 @@ import { CatalogApi } from '@backstage/catalog-client'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import yaml from 'yaml'; import { z } from 'zod'; import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model'; +import { examples } from './fetch.examples'; const id = 'catalog:fetch'; -const examples = [ - { - description: 'Fetch entity by reference', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'fetch', - name: 'Fetch catalog entity', - input: { - entityRef: 'component:default/name', - }, - }, - ], - }), - }, - { - description: 'Fetch multiple entities by referencse', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'fetchMultiple', - name: 'Fetch catalog entities', - input: { - entityRefs: ['component:default/name'], - }, - }, - ], - }), - }, -]; - /** * Returns entity or entities from the catalog by entity reference(s). * diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.examples.test.ts new file mode 100644 index 0000000000..eb5aa88c0f --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.examples.test.ts @@ -0,0 +1,108 @@ +/* + * 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 { PassThrough } from 'stream'; +import os from 'os'; +import { getVoidLogger } from '@backstage/backend-common'; +import { CatalogApi } from '@backstage/catalog-client'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { createCatalogRegisterAction } from './register'; +import { Entity } from '@backstage/catalog-model'; +import { examples } from './register.examples'; +import yaml from 'yaml'; + +describe('catalog:register', () => { + const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'github.com', token: 'token' }], + }, + }), + ); + + const addLocation = jest.fn(); + const catalogClient = { + addLocation: addLocation, + }; + + const action = createCatalogRegisterAction({ + integrations, + catalogClient: catalogClient as unknown as CatalogApi, + }); + + const mockContext = { + workspacePath: os.tmpdir(), + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should register location in catalog', async () => { + addLocation + .mockResolvedValueOnce({ + entities: [], + }) + .mockResolvedValueOnce({ + entities: [ + { + metadata: { + namespace: 'default', + name: 'test', + }, + kind: 'Component', + } as Entity, + ], + }); + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(addLocation).toHaveBeenNthCalledWith( + 1, + { + type: 'url', + target: + 'http://github.com/backstage/backstage/blob/master/catalog-info.yaml', + }, + {}, + ); + expect(addLocation).toHaveBeenNthCalledWith( + 2, + { + dryRun: true, + type: 'url', + target: + 'http://github.com/backstage/backstage/blob/master/catalog-info.yaml', + }, + {}, + ); + + expect(mockContext.output).toHaveBeenCalledWith( + 'entityRef', + 'component:default/test', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'catalogInfoUrl', + 'http://github.com/backstage/backstage/blob/master/catalog-info.yaml', + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.examples.ts new file mode 100644 index 0000000000..198ff52085 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.examples.ts @@ -0,0 +1,37 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Register with the catalog', + example: yaml.stringify({ + steps: [ + { + action: 'catalog:register', + id: 'register-with-catalog', + name: 'Register with the catalog', + input: { + catalogInfoUrl: + 'http://github.com/backstage/backstage/blob/master/catalog-info.yaml', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts index 3e35e16508..39372f484f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts @@ -19,29 +19,10 @@ import { ScmIntegrations } from '@backstage/integration'; import { CatalogApi } from '@backstage/catalog-client'; import { stringifyEntityRef, Entity } from '@backstage/catalog-model'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import yaml from 'yaml'; +import { examples } from './register.examples'; const id = 'catalog:register'; -const examples = [ - { - description: 'Register with the catalog', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'register-with-catalog', - name: 'Register with the catalog', - input: { - catalogInfoUrl: - 'http://github.com/backstage/backstage/blob/master/catalog-info.yaml', - }, - }, - ], - }), - }, -]; - /** * Registers entities from a catalog descriptor file in the workspace into the software catalog. * @public diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.examples.test.ts new file mode 100644 index 0000000000..daa8f4f6b1 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.examples.test.ts @@ -0,0 +1,72 @@ +/* + * 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 fs from 'fs-extra'; + +jest.mock('fs-extra'); + +const fsMock = fs as jest.Mocked; + +import { PassThrough } from 'stream'; +import os from 'os'; +import { getVoidLogger } from '@backstage/backend-common'; +import { createCatalogWriteAction } from './write'; +import { resolve as resolvePath } from 'path'; +import * as yaml from 'yaml'; +import { examples } from './write.examples'; + +describe('catalog:write', () => { + const action = createCatalogWriteAction(); + + const mockContext = { + workspacePath: os.tmpdir(), + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should write the catalog-info.yml in the workspace', async () => { + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + annotations: {}, + }, + spec: { + type: 'service', + lifecycle: 'production', + owner: 'default/owner', + }, + }; + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(fsMock.writeFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledWith( + resolvePath(mockContext.workspacePath, 'catalog-info.yaml'), + yaml.stringify(entity), + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.examples.ts new file mode 100644 index 0000000000..b66cff23d7 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.examples.ts @@ -0,0 +1,48 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import * as yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Write a catalog yaml file', + example: yaml.stringify({ + steps: [ + { + action: 'catalog:write', + id: 'create-catalog-info-file', + name: 'Create catalog file', + input: { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + annotations: {}, + }, + spec: { + type: 'service', + lifecycle: 'production', + owner: 'default/owner', + }, + }, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts index 81fdc73e71..7663ad15c8 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts @@ -19,39 +19,10 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import * as yaml from 'yaml'; import { resolveSafeChildPath } from '@backstage/backend-common'; import { z } from 'zod'; +import { examples } from './write.examples'; const id = 'catalog:write'; -const examples = [ - { - description: 'Write a catalog yaml file', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'create-catalog-info-file', - name: 'Create catalog file', - input: { - entity: { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'test', - annotations: {}, - }, - spec: { - type: 'service', - lifecycle: 'production', - owner: 'default/owner', - }, - }, - }, - }, - ], - }), - }, -]; - /** * Writes a catalog descriptor file containing the provided entity to a path in the workspace. * @public diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.test.ts new file mode 100644 index 0000000000..a7f5a1804a --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import mockFs from 'mock-fs'; +import os from 'os'; +import { Writable } from 'stream'; +import { createDebugLogAction } from './log'; +import { join } from 'path'; +import yaml from 'yaml'; +import { examples } from './log.examples'; + +describe('debug:log examples', () => { + const logStream = { + write: jest.fn(), + } as jest.Mocked> as jest.Mocked; + + const mockTmpDir = os.tmpdir(); + const mockContext = { + input: {}, + baseUrl: 'somebase', + workspacePath: mockTmpDir, + logger: getVoidLogger(), + logStream, + output: jest.fn(), + createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir), + }; + + const action = createDebugLogAction(); + + beforeEach(() => { + mockFs({ + [`${mockContext.workspacePath}/README.md`]: '', + [`${mockContext.workspacePath}/a-directory/index.md`]: '', + }); + jest.resetAllMocks(); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should log message', async () => { + const context = { + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }; + + await action.handler(context); + + expect(logStream.write).toHaveBeenCalledTimes(1); + expect(logStream.write).toHaveBeenCalledWith( + expect.stringContaining('Hello Backstage!'), + ); + }); + + it('should log the workspace content, if active', async () => { + const context = { + ...mockContext, + input: yaml.parse(examples[1].example).steps[0].input, + }; + + await action.handler(context); + + expect(logStream.write).toHaveBeenCalledTimes(1); + expect(logStream.write).toHaveBeenCalledWith( + expect.stringContaining('README.md'), + ); + expect(logStream.write).toHaveBeenCalledWith( + expect.stringContaining(join('a-directory', 'index.md')), + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts new file mode 100644 index 0000000000..0e9173bfa6 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts @@ -0,0 +1,51 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Write a debug message', + example: yaml.stringify({ + steps: [ + { + action: 'debug:log', + id: 'write-debug-line', + name: 'Write "Hello Backstage!" log line', + input: { + message: 'Hello Backstage!', + }, + }, + ], + }), + }, + { + description: 'List the workspace directory', + example: yaml.stringify({ + steps: [ + { + action: 'debug:log', + id: 'write-workspace-directory', + name: 'List the workspace directory', + input: { + listWorkspace: true, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts index 481eb9dfdd..524e12b5c9 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts @@ -17,43 +17,10 @@ import { readdir, stat } from 'fs-extra'; import { relative, join } from 'path'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import yaml from 'yaml'; +import { examples } from './log.examples'; const id = 'debug:log'; -const examples = [ - { - description: 'Write a debug message', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'write-debug-line', - name: 'Write "Hello Backstage!" log line', - input: { - message: 'Hello Backstage!', - }, - }, - ], - }), - }, - { - description: 'List the workspace directory', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'write-workspace-directory', - name: 'List the workspace directory', - input: { - listWorkspace: true, - }, - }, - ], - }), - }, -]; - /** * Writes a message into the log or lists all files in the workspace * diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.examples.test.ts new file mode 100644 index 0000000000..595ae7c3b6 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.examples.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import mockFs from 'mock-fs'; +import { createWaitAction } from './wait'; +import { Writable } from 'stream'; +import os from 'os'; +import { examples } from './wait.examples'; +import yaml from 'yaml'; + +describe('debug:wait examples', () => { + const action = createWaitAction(); + + const logStream = { + write: jest.fn(), + } as jest.Mocked> as jest.Mocked; + + const mockTmpDir = os.tmpdir(); + const mockContext = { + input: {}, + baseUrl: 'somebase', + workspacePath: mockTmpDir, + logger: getVoidLogger(), + logStream, + output: jest.fn(), + createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should wait for specified period of seconds', async () => { + const context = { + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }; + const start = new Date().getTime(); + await action.handler(context); + const end = new Date().getTime(); + expect(end - start).toBeGreaterThanOrEqual(50); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.examples.ts new file mode 100644 index 0000000000..1cc69b1aed --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.examples.ts @@ -0,0 +1,66 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Waiting for 50 milliseconds', + example: yaml.stringify({ + steps: [ + { + action: 'debug:wait', + id: 'wait-milliseconds', + name: 'Waiting for 50 milliseconds', + input: { + milliseconds: 50, + }, + }, + ], + }), + }, + { + description: 'Waiting for 5 seconds', + example: yaml.stringify({ + steps: [ + { + action: 'debug:wait', + id: 'wait-5sec', + name: 'Waiting for 5 seconds', + input: { + seconds: 5, + }, + }, + ], + }), + }, + { + description: 'Waiting for 1 minutes', + example: yaml.stringify({ + steps: [ + { + action: 'debug:wait', + id: 'wait-1min', + name: 'Waiting for 1 minutes', + input: { + minutes: 1, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.ts index 02cab239cb..ae0af4db5a 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/wait.ts @@ -16,46 +16,13 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { HumanDuration } from '@backstage/types'; -import yaml from 'yaml'; import { Duration } from 'luxon'; +import { examples } from './wait.examples'; const id = 'debug:wait'; const MAX_WAIT_TIME_IN_ISO = 'T00:00:30'; -const examples = [ - { - description: 'Waiting for 5 seconds', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'wait-5sec', - name: 'Waiting for 5 seconds', - input: { - seconds: 5, - }, - }, - ], - }), - }, - { - description: 'Waiting for 5 minutes', - example: yaml.stringify({ - steps: [ - { - action: id, - id: 'wait-5min', - name: 'Waiting for 5 minutes', - input: { - minutes: 5, - }, - }, - ], - }), - }, -]; - /** * Waits for a certain period of time. * diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts index 14a40f53c3..67f1bc360f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts @@ -17,4 +17,3 @@ export { createFetchPlainAction } from './plain'; export { createFetchPlainFileAction } from './plainFile'; export { createFetchTemplateAction } from './template'; -export { fetchContents } from './helpers'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.examples.test.ts new file mode 100644 index 0000000000..6b308a97eb --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.examples.test.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import yaml from 'yaml'; + +import os from 'os'; +import { resolve as resolvePath } from 'path'; +import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { createFetchPlainAction } from './plain'; +import { PassThrough } from 'stream'; +import { fetchContents } from '@backstage/plugin-scaffolder-node'; +import { examples } from './plain.examples'; + +jest.mock('@backstage/plugin-scaffolder-node', () => ({ + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + fetchContents: jest.fn(), +})); + +describe('fetch:plain examples', () => { + const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'github.com', token: 'token' }], + }, + }), + ); + const reader: UrlReader = { + readUrl: jest.fn(), + readTree: jest.fn(), + search: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + const action = createFetchPlainAction({ integrations, reader }); + const mockContext = { + workspacePath: os.tmpdir(), + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + it('should fetch plain', async () => { + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + expect(fetchContents).toHaveBeenCalledWith( + expect.objectContaining({ + outputPath: resolvePath(mockContext.workspacePath), + fetchUrl: + 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets', + }), + ); + }); + + it('should fetch plain to a specified directory', async () => { + await action.handler({ + ...mockContext, + input: yaml.parse(examples[1].example).steps[0].input, + }); + expect(fetchContents).toHaveBeenCalledWith( + expect.objectContaining({ + outputPath: resolvePath(mockContext.workspacePath, 'fetched-data'), + fetchUrl: + 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets', + }), + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.examples.ts new file mode 100644 index 0000000000..4937b72991 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.examples.ts @@ -0,0 +1,53 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Downloads content and places it in the workspace.', + example: yaml.stringify({ + steps: [ + { + action: 'fetch:plain', + id: 'fetch-plain', + name: 'Fetch plain', + input: { + url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets', + }, + }, + ], + }), + }, + { + description: + 'Optionally, if you would prefer the data to be downloaded to a subdirectory in the workspace you may specify the ‘targetPath’ input option.', + example: yaml.stringify({ + steps: [ + { + action: 'fetch:plain', + id: 'fetch-plain', + name: 'Fetch plain', + input: { + url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets', + targetPath: 'fetched-data', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts index ed37a39a98..249941096d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts @@ -14,16 +14,19 @@ * limitations under the License. */ -jest.mock('./helpers'); +jest.mock('@backstage/plugin-scaffolder-node', () => { + const actual = jest.requireActual('@backstage/plugin-scaffolder-node'); + return { ...actual, fetchContents: jest.fn() }; +}); import os from 'os'; import { resolve as resolvePath } from 'path'; import { getVoidLogger, UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; +import { fetchContents } from '@backstage/plugin-scaffolder-node'; import { createFetchPlainAction } from './plain'; import { PassThrough } from 'stream'; -import { fetchContents } from './helpers'; describe('fetch:plain', () => { const integrations = ScmIntegrations.fromConfig( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts index c10f696bb9..57f3d1121b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts @@ -16,8 +16,14 @@ import { UrlReader, resolveSafeChildPath } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents } from './helpers'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { examples } from './plain.examples'; + +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; + +export const ACTION_ID = 'fetch:plain'; /** * Downloads content and places it in the workspace, or optionally @@ -31,7 +37,8 @@ export function createFetchPlainAction(options: { const { reader, integrations } = options; return createTemplateAction<{ url: string; targetPath?: string }>({ - id: 'fetch:plain', + id: ACTION_ID, + examples, description: 'Downloads content and places it in the workspace, or optionally in a subdirectory specified by the `targetPath` input option.', schema: { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.examples.test.ts new file mode 100644 index 0000000000..2ee17c29fe --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.examples.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +jest.mock('@backstage/plugin-scaffolder-node', () => { + const actual = jest.requireActual('@backstage/plugin-scaffolder-node'); + return { ...actual, fetchFile: jest.fn() }; +}); + +import yaml from 'yaml'; +import os from 'os'; +import { resolve as resolvePath } from 'path'; +import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import { createFetchPlainFileAction } from './plainFile'; +import { PassThrough } from 'stream'; +import { fetchFile } from '@backstage/plugin-scaffolder-node'; +import { examples } from './plainFile.examples'; + +describe('fetch:plain:file examples', () => { + const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'github.com', token: 'token' }], + }, + }), + ); + const reader: UrlReader = { + readUrl: jest.fn(), + readTree: jest.fn(), + search: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + const action = createFetchPlainFileAction({ integrations, reader }); + const mockContext = { + workspacePath: os.tmpdir(), + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + it('should fetch plain', async () => { + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + expect(fetchFile).toHaveBeenCalledWith( + expect.objectContaining({ + outputPath: resolvePath(mockContext.workspacePath, 'target-path'), + fetchUrl: + 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets/Backstage%20Community%20Sessions.png', + }), + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.examples.ts new file mode 100644 index 0000000000..bf71068bf1 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.examples.ts @@ -0,0 +1,37 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Downloads a file and places it in the workspace.', + example: yaml.stringify({ + steps: [ + { + action: 'fetch:plain:file', + id: 'fetch-plain-file', + name: 'Fetch plain file', + input: { + url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets/Backstage%20Community%20Sessions.png', + targetPath: 'target-path', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts index 5d9e99fc7d..084b08d640 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts @@ -14,16 +14,19 @@ * limitations under the License. */ -jest.mock('./helpers'); +jest.mock('@backstage/plugin-scaffolder-node', () => { + const actual = jest.requireActual('@backstage/plugin-scaffolder-node'); + return { ...actual, fetchFile: jest.fn() }; +}); import os from 'os'; import { resolve as resolvePath } from 'path'; import { getVoidLogger, UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; +import { fetchFile } from '@backstage/plugin-scaffolder-node'; import { createFetchPlainFileAction } from './plainFile'; import { PassThrough } from 'stream'; -import { fetchFile } from './helpers'; describe('fetch:plain:file', () => { const integrations = ScmIntegrations.fromConfig( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts index 7b33ad93b4..0a791a31a4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts @@ -16,8 +16,11 @@ import { UrlReader, resolveSafeChildPath } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchFile } from './helpers'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { examples } from './plainFile.examples'; +import { + createTemplateAction, + fetchFile, +} from '@backstage/plugin-scaffolder-node'; /** * Downloads content and places it in the workspace, or optionally @@ -33,6 +36,7 @@ export function createFetchPlainFileAction(options: { return createTemplateAction<{ url: string; targetPath: string }>({ id: 'fetch:plain:file', description: 'Downloads single file and places it in the workspace.', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.test.ts new file mode 100644 index 0000000000..43bed5a048 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.test.ts @@ -0,0 +1,231 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import os from 'os'; +import { join as joinPath, sep as pathSep } from 'path'; +import fs from 'fs-extra'; +import mockFs from 'mock-fs'; +import { + getVoidLogger, + resolvePackagePath, + UrlReader, +} from '@backstage/backend-common'; +import { ScmIntegrations } from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { createFetchTemplateAction } from './template'; +import { + ActionContext, + TemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; +import { examples } from './template.examples'; +import yaml from 'yaml'; + +jest.mock('@backstage/plugin-scaffolder-node', () => ({ + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + fetchContents: jest.fn(), +})); + +type FetchTemplateInput = ReturnType< + typeof createFetchTemplateAction +> extends TemplateAction + ? U + : never; + +const realFiles = Object.fromEntries( + [ + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'assets', + 'nunjucks.js.txt', + ), + ].map(k => [k, mockFs.load(k)]), +); + +const aBinaryFile = fs.readFileSync( + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'fixtures/test-nested-template/public/react-logo192.png', + ), +); + +const mockFetchContents = fetchContents as jest.MockedFunction< + typeof fetchContents +>; + +describe('fetch:template examples', () => { + let action: TemplateAction; + + const workspacePath = os.tmpdir(); + const createTemporaryDirectory: jest.MockedFunction< + ActionContext['createTemporaryDirectory'] + > = jest.fn(() => + Promise.resolve( + joinPath(workspacePath, `${createTemporaryDirectory.mock.calls.length}`), + ), + ); + + const logger = getVoidLogger(); + + const mockContext = (input: any) => ({ + templateInfo: { + baseUrl: 'base-url', + entityRef: 'template:default/test-template', + }, + input: input, + output: jest.fn(), + logStream: new PassThrough(), + logger, + workspacePath, + createTemporaryDirectory, + }); + + beforeEach(() => { + mockFs({ + ...realFiles, + }); + + action = createFetchTemplateAction({ + reader: Symbol('UrlReader') as unknown as UrlReader, + integrations: Symbol('Integrations') as unknown as ScmIntegrations, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + describe('handler', () => { + describe('with valid input', () => { + let context: ActionContext; + + beforeEach(async () => { + context = mockContext(yaml.parse(examples[0].example).steps[0].input); + + mockFetchContents.mockImplementation(({ outputPath }) => { + mockFs({ + ...realFiles, + [outputPath]: { + 'an-executable.sh': mockFs.file({ + content: '#!/usr/bin/env bash', + mode: parseInt('100755', 8), + }), + 'empty-dir-${{ values.count }}': {}, + 'static.txt': 'static content', + '${{ values.name }}.txt': 'static content', + subdir: { + 'templated-content.txt': + '${{ values.name }}: ${{ values.count }}', + }, + '.${{ values.name }}': '${{ values.itemList | dump }}', + 'a-binary-file.png': aBinaryFile, + symlink: mockFs.symlink({ + path: 'a-binary-file.png', + }), + brokenSymlink: mockFs.symlink({ + path: './not-a-real-file.txt', + }), + }, + }); + + return Promise.resolve(); + }); + + await action.handler(context); + }); + + it('uses fetchContents to retrieve the template content', () => { + expect(mockFetchContents).toHaveBeenCalledWith( + expect.objectContaining({ + baseUrl: context.templateInfo?.baseUrl, + fetchUrl: context.input.url, + }), + ); + }); + + it('copies files with no templating in names or content successfully', async () => { + await expect( + fs.readFile(`${workspacePath}/target/static.txt`, 'utf-8'), + ).resolves.toEqual('static content'); + }); + + it('copies files with templated names successfully', async () => { + await expect( + fs.readFile(`${workspacePath}/target/test-project.txt`, 'utf-8'), + ).resolves.toEqual('static content'); + }); + + it('copies files with templated content successfully', async () => { + await expect( + fs.readFile( + `${workspacePath}/target/subdir/templated-content.txt`, + 'utf-8', + ), + ).resolves.toEqual('test-project: 1234'); + }); + + it('processes dotfiles', async () => { + await expect( + fs.readFile(`${workspacePath}/target/.test-project`, 'utf-8'), + ).resolves.toEqual('["first","second","third"]'); + }); + + it('copies empty directories', async () => { + await expect( + fs.readdir(`${workspacePath}/target/empty-dir-1234`, 'utf-8'), + ).resolves.toEqual([]); + }); + + it('copies binary files as-is without processing them', async () => { + await expect( + fs.readFile(`${workspacePath}/target/a-binary-file.png`), + ).resolves.toEqual(aBinaryFile); + }); + + it('copies files and maintains the original file permissions', async () => { + await expect( + fs + .stat(`${workspacePath}/target/an-executable.sh`) + .then(fObj => fObj.mode), + ).resolves.toEqual(parseInt('100755', 8)); + }); + + it('copies file symlinks as-is without processing them', async () => { + await expect( + fs + .lstat(`${workspacePath}/target/symlink`) + .then(i => i.isSymbolicLink()), + ).resolves.toBe(true); + + await expect( + fs.realpath(`${workspacePath}/target/symlink`), + ).resolves.toBe(joinPath(workspacePath, 'target', 'a-binary-file.png')); + }); + + it('copies broken symlinks as-is without processing them', async () => { + await expect( + fs + .lstat(`${workspacePath}/target/brokenSymlink`) + .then(i => i.isSymbolicLink()), + ).resolves.toBe(true); + + await expect( + fs.readlink(`${workspacePath}/target/brokenSymlink`), + ).resolves.toEqual(`.${pathSep}not-a-real-file.txt`); + }); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts new file mode 100644 index 0000000000..4c4628e715 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts @@ -0,0 +1,44 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Downloads a skelaton directory that lives alongside the template file and fill it out with values.', + example: yaml.stringify({ + steps: [ + { + action: 'fetch:template', + id: 'fetch-template', + name: 'Fetch template', + input: { + url: './skeleton', + targetPath: './target', + values: { + name: 'test-project', + count: 1234, + itemList: ['first', 'second', 'third'], + showDummyFile: false, + }, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index 15548ffea7..95f8c7b954 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -14,6 +14,11 @@ * limitations under the License. */ +jest.mock('@backstage/plugin-scaffolder-node', () => { + const actual = jest.requireActual('@backstage/plugin-scaffolder-node'); + return { ...actual, fetchContents: jest.fn() }; +}); + import os from 'os'; import { join as joinPath, sep as pathSep } from 'path'; import fs from 'fs-extra'; @@ -25,17 +30,13 @@ import { } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; import { PassThrough } from 'stream'; -import { fetchContents } from './helpers'; import { createFetchTemplateAction } from './template'; import { + fetchContents, ActionContext, TemplateAction, } from '@backstage/plugin-scaffolder-node'; -jest.mock('./helpers', () => ({ - fetchContents: jest.fn(), -})); - type FetchTemplateInput = ReturnType< typeof createFetchTemplateAction > extends TemplateAction diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index be7b47521c..1116d4dd5d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -18,8 +18,10 @@ import { extname } from 'path'; import { resolveSafeChildPath, UrlReader } from '@backstage/backend-common'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents } from './helpers'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; import globby from 'globby'; import fs from 'fs-extra'; import { isBinaryFile } from 'isbinaryfile'; @@ -29,6 +31,7 @@ import { TemplateGlobal, } from '../../../../lib/templating/SecureTemplater'; import { createDefaultFilters } from '../../../../lib/templating/filters'; +import { examples } from './template.examples'; /** * Downloads a skeleton, templates variables into file and directory names and content. @@ -70,6 +73,7 @@ export function createFetchTemplateAction(options: { id: 'fetch:template', description: 'Downloads a skeleton, templates variables into file and directory names and content, and places the result in the workspace, or optionally in a subdirectory specified by the `targetPath` input option.', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.test.ts new file mode 100644 index 0000000000..10994e3824 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createFilesystemDeleteAction } from './delete'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; +import { resolve as resolvePath } from 'path'; +import * as os from 'os'; +import mockFs from 'mock-fs'; +import fs from 'fs-extra'; +import yaml from 'yaml'; +import { examples } from './delete.examples'; + +const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; +const workspacePath = resolvePath(root, 'my-workspace'); + +describe('fs:delete examples', () => { + const action = createFilesystemDeleteAction(); + + const files: string[] = yaml.parse(examples[0].example).steps[0].input.files; + + const mockContext = { + input: { + files: files, + }, + workspacePath, + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.restoreAllMocks(); + + mockFs({ + [workspacePath]: { + [files[0]]: 'hello', + [files[1]]: 'world', + 'a-folder': { + 'unit-test-in-a-folder.js2': 'content', + }, + }, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should call fs.rm with the correct values', async () => { + files.forEach(file => { + const filePath = resolvePath(workspacePath, file); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(true); + }); + + await action.handler(mockContext); + + files.forEach(file => { + const filePath = resolvePath(workspacePath, file); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(false); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts new file mode 100644 index 0000000000..8aea076b9d --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts @@ -0,0 +1,36 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import * as yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Delete specified files', + example: yaml.stringify({ + steps: [ + { + action: 'fs:delete', + id: 'deleteFiles', + name: 'Delete files', + input: { + files: ['file1.txt', 'file2.txt'], + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts index 2f955c16ff..5787d245ab 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts @@ -18,6 +18,7 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { InputError } from '@backstage/errors'; import { resolveSafeChildPath } from '@backstage/backend-common'; import fs from 'fs-extra'; +import { examples } from './delete.examples'; /** * Creates new action that enables deletion of files and directories in the workspace. @@ -27,6 +28,7 @@ export const createFilesystemDeleteAction = () => { return createTemplateAction<{ files: string[] }>({ id: 'fs:delete', description: 'Deletes files and directories from the workspace', + examples, schema: { input: { required: ['files'], diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.examples.test.ts new file mode 100644 index 0000000000..c4c27cc68e --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.examples.test.ts @@ -0,0 +1,104 @@ +/* + * 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 * as os from 'os'; +import mockFs from 'mock-fs'; +import { resolve as resolvePath } from 'path'; +import { createFilesystemRenameAction } from './rename'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; +import fs from 'fs-extra'; +import yaml from 'yaml'; +import { examples } from './rename.examples'; + +const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; +const workspacePath = resolvePath(root, 'my-workspace'); + +describe('fs:rename examples', () => { + const action = createFilesystemRenameAction(); + const files: { from: string; to: string }[] = yaml.parse(examples[0].example) + .steps[0].input.files; + + const mockContext = { + input: { + files: files, + }, + workspacePath, + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.restoreAllMocks(); + + mockFs({ + [workspacePath]: { + [files[0].from]: 'hello', + [files[1].from]: 'world', + [files[2].from]: '!!!', + 'file3Renamed.txt': 'I will be overwritten :(', + 'a-folder': { + 'file.md': 'content', + }, + }, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('should call fs.move with the correct values', async () => { + mockContext.input.files.forEach(file => { + const filePath = resolvePath(workspacePath, file.from); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(true); + }); + + await action.handler(mockContext); + + mockContext.input.files.forEach(file => { + const filePath = resolvePath(workspacePath, file.from); + const fileExists = fs.existsSync(filePath); + expect(fileExists).toBe(false); + }); + }); + + it('should override when requested', async () => { + const sourceFile = files[2].from; + const destFile = files[2].to; + const sourceFilePath = resolvePath(workspacePath, sourceFile); + const destFilePath = resolvePath(workspacePath, destFile); + + const sourceBeforeContent = fs.readFileSync(sourceFilePath, 'utf-8'); + const destBeforeContent = fs.readFileSync(destFilePath, 'utf-8'); + + expect(sourceBeforeContent).not.toEqual(destBeforeContent); + + await action.handler({ + ...mockContext, + input: { + files, + }, + }); + + const destAfterContent = fs.readFileSync(destFilePath, 'utf-8'); + + expect(sourceBeforeContent).toEqual(destAfterContent); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.examples.ts new file mode 100644 index 0000000000..59d7483a10 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.examples.ts @@ -0,0 +1,40 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import * as yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Rename specified files ', + example: yaml.stringify({ + steps: [ + { + action: 'fs:rename', + id: 'renameFiles', + name: 'Rename files', + input: { + files: [ + { from: 'file1.txt', to: 'file1Renamed.txt' }, + { from: 'file2.txt', to: 'file2Renamed.txt' }, + { from: 'file3.txt', to: 'file3Renamed.txt', overwrite: true }, + ], + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts index c29ba221c4..5d9d666d15 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts @@ -18,6 +18,7 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { resolveSafeChildPath } from '@backstage/backend-common'; import { InputError } from '@backstage/errors'; import fs from 'fs-extra'; +import { examples } from './rename.examples'; /** * Creates a new action that allows renames of files and directories in the workspace. @@ -33,6 +34,7 @@ export const createFilesystemRenameAction = () => { }>({ id: 'fs:rename', description: 'Renames files and directories within the workspace', + examples, schema: { input: { required: ['files'], diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.examples.test.ts new file mode 100644 index 0000000000..71bbc7c1c6 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.examples.test.ts @@ -0,0 +1,192 @@ +/* + * 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 { TemplateAction } from '@backstage/plugin-scaffolder-node'; + +jest.mock('../helpers'); + +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { createGithubRepoCreateAction } from './githubRepoCreate'; +import { entityRefToName } from '../helpers'; +import yaml from 'yaml'; +import { examples } from './githubRepoCreate.examples'; + +const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU='; + +const mockOctokit = { + rest: { + users: { + getByUsername: jest.fn(), + }, + repos: { + addCollaborator: jest.fn(), + createInOrg: jest.fn(), + createForAuthenticatedUser: jest.fn(), + replaceAllTopics: jest.fn(), + }, + teams: { + addOrUpdateRepoPermissionsInOrg: jest.fn(), + getByName: jest.fn(), + }, + actions: { + createRepoVariable: jest.fn(), + createOrUpdateRepoSecret: jest.fn(), + getRepoPublicKey: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('github:repo:create examples', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + let githubCredentialsProvider: GithubCredentialsProvider; + let action: TemplateAction; + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + action = createGithubRepoCreateAction({ + integrations, + githubCredentialsProvider, + }); + (entityRefToName as jest.Mock).mockImplementation((s: string) => s); + mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({ + data: { + key: publicKey, + key_id: 'keyid', + }, + }); + }); + + afterEach(jest.resetAllMocks); + + it('should call the githubApis with the correct values for createInOrg', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'Organization' }, + }); + + mockOctokit.rest.repos.createInOrg.mockResolvedValue({ data: {} }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + name: 'repo', + org: 'owner', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + visibility: 'private', + }); + }); + + it('should call the githubApis with a description for createInOrg', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'Organization' }, + }); + + mockOctokit.rest.repos.createInOrg.mockResolvedValue({ data: {} }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[1].example).steps[0].input, + }); + + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + name: 'repo', + description: 'My new repository', + org: 'owner', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + visibility: 'private', + }); + }); + + it('should call the githubApis with wiki and issues disabled for createInOrg', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'Organization' }, + }); + + mockOctokit.rest.repos.createInOrg.mockResolvedValue({ data: {} }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[2].example).steps[0].input, + }); + + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + name: 'repo', + org: 'owner', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + visibility: 'private', + has_issues: false, // disable issues + has_wiki: false, // disable wiki + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.examples.ts new file mode 100644 index 0000000000..a58da1ee29 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.examples.ts @@ -0,0 +1,66 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Creates a GitHub repository with default configuration.', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:create', + name: 'Create a new GitHub repository', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + }, + }, + ], + }), + }, + { + description: 'Add a description.', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:create', + name: 'Create a new GitHub repository with a description', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + description: 'My new repository', + }, + }, + ], + }), + }, + { + description: 'Disable wiki and issues.', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:create', + name: 'Create a new GitHub repository without wiki and issues', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + hasIssues: false, + hasWiki: false, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts index 0ec1c83910..e0fde3b193 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts @@ -28,6 +28,7 @@ import { } from './helpers'; import * as inputProps from './inputProperties'; import * as outputProps from './outputProperties'; +import { examples } from './githubRepoCreate.examples'; /** * Creates a new action that initializes a git repository @@ -96,6 +97,7 @@ export function createGithubRepoCreateAction(options: { }>({ id: 'github:repo:create', description: 'Creates a GitHub repository.', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.examples.test.ts new file mode 100644 index 0000000000..84d489272d --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.examples.test.ts @@ -0,0 +1,131 @@ +/* + * 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 { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { initRepoAndPush } from '../helpers'; +import { createGithubRepoPushAction } from './githubRepoPush'; +import { examples } from './githubRepoPush.examples'; +import yaml from 'yaml'; + +const mockGit = { + init: jest.fn(), + add: jest.fn(), + checkout: jest.fn(), + commit: jest + .fn() + .mockResolvedValue('220f19cc36b551763d157f1b5e4a4b446165dbd6'), + fetch: jest.fn(), + addRemote: jest.fn(), + push: jest.fn(), +}; + +jest.mock('@backstage/backend-common', () => ({ + Git: { + fromAuth() { + return mockGit; + }, + }, + getVoidLogger: jest.requireActual('@backstage/backend-common').getVoidLogger, +})); + +jest.mock('../helpers'); + +const initRepoAndPushMocked = initRepoAndPush as jest.Mock< + Promise<{ commitHash: string }> +>; + +const mockOctokit = { + rest: { + repos: { + get: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('github:repo:push examples', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + let githubCredentialsProvider: GithubCredentialsProvider; + let action: TemplateAction; + + const mockContext = { + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + + initRepoAndPushMocked.mockResolvedValue({ commitHash: 'test123' }); + + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + action = createGithubRepoPushAction({ + integrations, + config, + githubCredentialsProvider, + }); + }); + + it('should call initRepoAndPush with the correct values', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://github.com/clone/url.git', + defaultBranch: 'master', + auth: { username: 'x-access-token', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.examples.ts new file mode 100644 index 0000000000..5d3ba1e9d8 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.examples.ts @@ -0,0 +1,65 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Setup repo with no modifications to branch protection rules', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:push', + name: 'Create test repo with testuser as owner.', + input: { + repoUrl: 'github.com?repo=test&owner=testuser', + }, + }, + ], + }), + }, + { + description: 'Setup repo with required codeowners check', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:push', + name: 'Require codeowner branch protection rule', + input: { + repoUrl: 'github.com?repo=reponame&owner=owner', + requireCodeOwnerReviews: true, + }, + }, + ], + }), + }, + { + description: 'Change the default required number of approvals', + example: yaml.stringify({ + steps: [ + { + action: 'github:repo:push', + name: 'Require two approvals before merging', + input: { + repoUrl: 'github.com?repo=reponame&owner=owner', + requiredApprovingReviewCount: 2, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts index fb15535453..e30eedad02 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts @@ -26,6 +26,7 @@ import { parseRepoUrl } from '../publish/util'; import { getOctokitOptions, initRepoPushAndProtect } from './helpers'; import * as inputProps from './inputProperties'; import * as outputProps from './outputProperties'; +import { examples } from './githubRepoPush.examples'; /** * Creates a new action that initializes a git repository of the content in the workspace @@ -76,6 +77,7 @@ export function createGithubRepoPushAction(options: { id: 'github:repo:push', description: 'Initializes a git repository of contents in workspace and publishes it to GitHub.', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts index f51ab47f16..5ac92128ca 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts @@ -17,61 +17,9 @@ import { Git } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { assertError } from '@backstage/errors'; -import { spawn, SpawnOptionsWithoutStdio } from 'child_process'; import { Octokit } from 'octokit'; -import { PassThrough, Writable } from 'stream'; import { Logger } from 'winston'; -/** @public */ -export type RunCommandOptions = { - /** command to run */ - command: string; - /** arguments to pass the command */ - args: string[]; - /** options to pass to spawn */ - options?: SpawnOptionsWithoutStdio; - /** stream to capture stdout and stderr output */ - logStream?: Writable; -}; - -/** - * Run a command in a sub-process, normally a shell command. - * - * @public - */ -export const executeShellCommand = async (options: RunCommandOptions) => { - const { - command, - args, - options: spawnOptions, - logStream = new PassThrough(), - } = options; - await new Promise((resolve, reject) => { - const process = spawn(command, args, spawnOptions); - - process.stdout.on('data', stream => { - logStream.write(stream); - }); - - process.stderr.on('data', stream => { - logStream.write(stream); - }); - - process.on('error', error => { - return reject(error); - }); - - process.on('close', code => { - if (code !== 0) { - return reject( - new Error(`Command ${command} failed, exit code: ${code}`), - ); - } - return resolve(); - }); - }); -}; - export async function initRepoAndPush({ dir, remoteUrl, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts index 7846ff96fc..7ab98482ff 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts @@ -21,6 +21,3 @@ export * from './fetch'; export * from './filesystem'; export * from './publish'; export * from './github'; - -export { executeShellCommand } from './helpers'; -export type { RunCommandOptions } from './helpers'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.examples.test.ts new file mode 100644 index 0000000000..4d87ec97c4 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.examples.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +jest.mock('../helpers'); + +import { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { entityRefToName, initRepoAndPush } from '../helpers'; +import { createPublishGithubAction } from './github'; +import { examples } from './github.examples'; +import yaml from 'yaml'; + +const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU='; + +const initRepoAndPushMocked = initRepoAndPush as jest.Mock< + Promise<{ commitHash: string }> +>; + +const mockOctokit = { + rest: { + users: { + getByUsername: jest.fn(), + }, + repos: { + addCollaborator: jest.fn(), + createInOrg: jest.fn(), + createForAuthenticatedUser: jest.fn(), + replaceAllTopics: jest.fn(), + }, + teams: { + getByName: jest.fn(), + addOrUpdateRepoPermissionsInOrg: jest.fn(), + }, + actions: { + createRepoVariable: jest.fn(), + createOrUpdateRepoSecret: jest.fn(), + getRepoPublicKey: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('publish:github', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const { entityRefToName: realFamiliarizeEntityName } = + jest.requireActual('../helpers'); + const integrations = ScmIntegrations.fromConfig(config); + let githubCredentialsProvider: GithubCredentialsProvider; + let action: TemplateAction; + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + description: 'description', + repoVisibility: 'private' as const, + access: 'owner/blam', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + initRepoAndPushMocked.mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }); + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + action = createPublishGithubAction({ + integrations, + config, + githubCredentialsProvider, + }); + + // restore real implmentation + (entityRefToName as jest.Mock).mockImplementation( + realFamiliarizeEntityName, + ); + mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({ + data: { + key: publicKey, + key_id: 'keyid', + }, + }); + }); + + afterEach(jest.resetAllMocks); + + it('should call initRepoAndPush with the correct values', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://github.com/clone/url.git', + defaultBranch: 'master', + auth: { username: 'x-access-token', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.examples.ts new file mode 100644 index 0000000000..65f2ccdaa3 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.examples.ts @@ -0,0 +1,70 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Initializes a git repository of contents in workspace and publish it to GitHub with default configuration.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:github', + name: 'Publish to GitHub', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + }, + }, + ], + }), + }, + { + description: 'Add a description.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:github', + name: 'Publish to GitHub', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + description: 'Initialize a git repository', + }, + }, + ], + }), + }, + { + description: 'Change visibility of the repository.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:github', + name: 'Publish to GitHub', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + description: 'Initialize a git repository', + repoVisibility: 'public', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index 9336c38b63..e5b6071863 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -30,6 +30,7 @@ import { import * as inputProps from '../github/inputProperties'; import * as outputProps from '../github/outputProperties'; import { parseRepoUrl } from './util'; +import { examples } from './github.examples'; /** * Creates a new action that initializes a git repository of the content in the workspace @@ -111,6 +112,7 @@ export function createPublishGithubAction(options: { id: 'publish:github', description: 'Initializes a git repository of contents in workspace and publishes it to GitHub.', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.examples.test.ts new file mode 100644 index 0000000000..5d58431c61 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.examples.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import yaml from 'yaml'; + +jest.mock('../helpers', () => { + return { + initRepoAndPush: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + commitAndPushRepo: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + }; +}); + +import { createPublishGitlabAction } from './gitlab'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; +import { initRepoAndPush } from '../helpers'; +import { examples } from './gitlab.examples'; + +const mockGitlabClient = { + Namespaces: { + show: jest.fn(), + }, + Projects: { + create: jest.fn(), + }, + Users: { + current: jest.fn(), + }, + ProjectMembers: { + add: jest.fn(), + }, +}; +jest.mock('@gitbeaker/node', () => ({ + Gitlab: class { + constructor() { + return mockGitlabClient; + } + }, +})); + +describe('publish:gitlab', () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + { + host: 'hosted.gitlab.com', + apiBaseUrl: 'https://api.hosted.gitlab.com', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishGitlabAction({ integrations, config }); + const mockContext = { + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should call initRepoAndPush with the correct values', async () => { + mockGitlabClient.Users.current.mockResolvedValue({ id: 12345 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Projects.create.mockResolvedValue({ + http_url_to_repo: 'http://mockurl.git', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[0].example).steps[0].input, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + defaultBranch: 'master', + remoteUrl: 'http://mockurl.git', + auth: { username: 'oauth2', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.examples.ts new file mode 100644 index 0000000000..ac259866e3 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.examples.ts @@ -0,0 +1,71 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Initializes a git repository of the content in the workspace, and publishes it to GitLab.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitlab', + name: 'Publish to GitLab', + input: { + repoUrl: 'gitlab.com?repo=project_name&owner=group_name', + }, + }, + ], + }), + }, + { + description: 'Add a description.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitlab', + name: 'Publish to GitLab', + input: { + repoUrl: 'gitlab.com?repo=project_name&owner=group_name', + description: 'Initialize a git repository', + }, + }, + ], + }), + }, + { + description: + 'Sets the commit message on the repository. The default value is `initial commit`.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitlab', + name: 'Publish to GitLab', + input: { + repoUrl: 'gitlab.com?repo=project_name&owner=group_name', + description: 'Initialize a git repository', + gitCommitMessage: 'Started a project.', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts index 4045fd6958..dbab7b11b8 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts @@ -21,6 +21,7 @@ import { Gitlab } from '@gitbeaker/node'; import { initRepoAndPush } from '../helpers'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { Config } from '@backstage/config'; +import { examples } from './gitlab.examples'; /** * Creates a new action that initializes a git repository of the content in the workspace @@ -49,6 +50,7 @@ export function createPublishGitlabAction(options: { id: 'publish:gitlab', description: 'Initializes a git repository of the content in the workspace, and publishes it to GitLab.', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index 5d4f7b73cc..253a6c4388 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -573,6 +573,101 @@ describe('DefaultWorkflowRunner', () => { }); }); + describe('each', () => { + it('should run a step repeatedly - flat values', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + steps: [ + { + id: 'test', + name: 'name', + each: '${{parameters.colors}}', + action: 'jest-mock-action', + input: { color: '${{each.value}}' }, + }, + ], + output: {}, + parameters: { + colors: ['blue', 'green', 'red'], + }, + }); + + await runner.execute(task); + + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ input: { color: 'blue' } }), + ); + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ input: { color: 'green' } }), + ); + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ input: { color: 'red' } }), + ); + }); + + it('should run a step repeatedly - object list', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + steps: [ + { + id: 'test', + name: 'name', + each: '${{parameters.settings}}', + action: 'jest-mock-action', + input: { + key: '${{each.key}}', + value: '${{each.value}}', + }, + }, + ], + output: {}, + parameters: { + settings: [{ color: 'blue' }], + }, + }); + + await runner.execute(task); + + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ + input: { key: '0', value: { color: 'blue' } }, + }), + ); + }); + + it('should run a step repeatedly - object', async () => { + const task = createMockTaskWithSpec({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + steps: [ + { + id: 'test', + name: 'name', + each: '${{parameters.settings}}', + action: 'jest-mock-action', + input: { key: '${{each.key}}', value: '${{each.value}}' }, + }, + ], + output: {}, + parameters: { + settings: { color: 'blue', transparent: 'yes' }, + }, + }); + + await runner.execute(task); + + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ + input: { key: 'color', value: 'blue' }, + }), + ); + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ + input: { key: 'transparent', value: 'yes' }, + }), + ); + }); + }); + describe('secrets', () => { it('should pass through the secrets to the context', async () => { const task = createMockTaskWithSpec( diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index da690bcb82..1f93ded94c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -25,7 +25,7 @@ import * as winston from 'winston'; import fs from 'fs-extra'; import path from 'path'; import nunjucks from 'nunjucks'; -import { JsonObject, JsonValue } from '@backstage/types'; +import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; import { InputError, NotAllowedError } from '@backstage/errors'; import { PassThrough } from 'stream'; import { generateExampleOutput, isTruthy } from './helper'; @@ -76,6 +76,7 @@ type TemplateContext = { entity?: UserEntity; ref?: string; }; + each?: JsonValue; }; const isValidTaskSpec = (taskSpec: TaskSpec): taskSpec is TaskSpecV1beta3 => { @@ -311,25 +312,63 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const tmpDirs = new Array(); const stepOutput: { [outputName: string]: JsonValue } = {}; - await action.handler({ - input, - secrets: task.secrets ?? {}, - logger: taskLogger, - logStream: streamLogger, - workspacePath, - createTemporaryDirectory: async () => { - const tmpDir = await fs.mkdtemp(`${workspacePath}_step-${step.id}-`); - tmpDirs.push(tmpDir); - return tmpDir; - }, - output(name: string, value: JsonValue) { - stepOutput[name] = value; - }, - templateInfo: task.spec.templateInfo, - user: task.spec.user, - isDryRun: task.isDryRun, - signal: task.cancelSignal, - }); + const iterations = new Array(); + if (step.each) { + const each = await this.render(step.each, context, renderTemplate); + iterations.push( + ...Object.keys(each).map((key: any) => { + return { key: key, value: each[key] }; + }), + ); + } else { + iterations.push({}); + } + + let actionInput = input; + for (const iteration of iterations) { + if (step.each) { + taskLogger.info(`Running step each: ${iteration}`); + const iterationContext = { + ...context, + each: iteration, + }; + // re-render input with the modified context that includes each + actionInput = + (step.input && + this.render( + step.input, + { ...iterationContext, secrets: task.secrets ?? {} }, + renderTemplate, + )) ?? + {}; + } + await action.handler({ + input: actionInput, + secrets: task.secrets ?? {}, + logger: taskLogger, + logStream: streamLogger, + workspacePath, + createTemporaryDirectory: async () => { + const tmpDir = await fs.mkdtemp( + `${workspacePath}_step-${step.id}-`, + ); + tmpDirs.push(tmpDir); + return tmpDir; + }, + output(name: string, value: JsonValue) { + if (step.each) { + stepOutput[name] = stepOutput[name] || []; + (stepOutput[name] as JsonArray).push(value); + } else { + stepOutput[name] = value; + } + }, + templateInfo: task.spec.templateInfo, + user: task.spec.user, + isDryRun: task.isDryRun, + signal: task.cancelSignal, + }); + } // Remove all temporary directories that were created when executing the action for (const tmpDir of tmpDirs) { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 112276b9dd..4560f27c0b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -14,131 +14,94 @@ * limitations under the License. */ -import { JsonValue, JsonObject, Observable } from '@backstage/types'; +import { JsonValue, JsonObject } from '@backstage/types'; import { TaskSpec, TaskStep } from '@backstage/plugin-scaffolder-common'; import { TaskSecrets } from '@backstage/plugin-scaffolder-node'; -import { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + TemplateAction, + TaskStatus as _TaskStatus, + TaskCompletionState as _TaskCompletionState, + SerializedTask as _SerializedTask, + TaskEventType as _TaskEventType, + SerializedTaskEvent as _SerializedTaskEvent, + TaskBrokerDispatchResult as _TaskBrokerDispatchResult, + TaskBrokerDispatchOptions as _TaskBrokerDispatchOptions, + TaskContext as _TaskContext, + TaskBroker as _TaskBroker, +} from '@backstage/plugin-scaffolder-node'; /** * The status of each step of the Task * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type TaskStatus = - | 'cancelled' - | 'completed' - | 'failed' - | 'open' - | 'processing'; +export type TaskStatus = _TaskStatus; /** * The state of a completed task. * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type TaskCompletionState = 'failed' | 'completed'; +export type TaskCompletionState = _TaskCompletionState; /** * SerializedTask * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type SerializedTask = { - id: string; - spec: TaskSpec; - status: TaskStatus; - createdAt: string; - lastHeartbeatAt?: string; - createdBy?: string; - secrets?: TaskSecrets; -}; +export type SerializedTask = _SerializedTask; /** * TaskEventType * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type TaskEventType = 'completion' | 'log' | 'cancelled'; +export type TaskEventType = _TaskEventType; /** * SerializedTaskEvent * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type SerializedTaskEvent = { - id: number; - taskId: string; - body: JsonObject; - type: TaskEventType; - createdAt: string; -}; +export type SerializedTaskEvent = _SerializedTaskEvent; /** - * The result of {@link TaskBroker.dispatch} + * The result of `TaskBroker.dispatch`. * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type TaskBrokerDispatchResult = { - taskId: string; -}; +export type TaskBrokerDispatchResult = _TaskBrokerDispatchResult; /** - * The options passed to {@link TaskBroker.dispatch} + * The options passed to `TaskBroker.dispatch`. * Currently a spec and optional secrets * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export type TaskBrokerDispatchOptions = { - spec: TaskSpec; - secrets?: TaskSecrets; - createdBy?: string; -}; +export type TaskBrokerDispatchOptions = _TaskBrokerDispatchOptions; /** * Task * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export interface TaskContext { - cancelSignal: AbortSignal; - spec: TaskSpec; - secrets?: TaskSecrets; - createdBy?: string; - done: boolean; - isDryRun?: boolean; - - complete(result: TaskCompletionState, metadata?: JsonObject): Promise; - - emitLog(message: string, logMetadata?: JsonObject): Promise; - - getWorkspaceName(): Promise; -} +export type TaskContext = _TaskContext; /** * TaskBroker * * @public + * @deprecated Import from `@backstage/plugin-scaffolder-node` instead. */ -export interface TaskBroker { - cancel?(taskId: string): Promise; - - claim(): Promise; - - dispatch( - options: TaskBrokerDispatchOptions, - ): Promise; - - vacuumTasks(options: { timeoutS: number }): Promise; - - event$(options: { - taskId: string; - after: number | undefined; - }): Observable<{ events: SerializedTaskEvent[] }>; - - get(taskId: string): Promise; - - list?(options?: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; -} +export type TaskBroker = _TaskBroker; /** * TaskStoreEmitOptions diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 1771067367..15f7c11eba 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -231,13 +231,15 @@ export async function createRouter( catalogClient, actions, taskWorkers, - concurrentTasksLimit, scheduler, additionalTemplateFilters, additionalTemplateGlobals, permissions, permissionRules, } = options; + const concurrentTasksLimit = + options.concurrentTasksLimit ?? + options.config.getOptionalNumber('scaffolder.concurrentTasksLimit'); const logger = parentLogger.child({ plugin: 'scaffolder' }); @@ -275,19 +277,21 @@ export async function createRouter( const actionRegistry = new TemplateActionRegistry(); const workers = []; - for (let i = 0; i < (taskWorkers || 1); i++) { - const worker = await TaskWorker.create({ - taskBroker, - actionRegistry, - integrations, - logger, - workingDirectory, - additionalTemplateFilters, - additionalTemplateGlobals, - concurrentTasksLimit, - permissions, - }); - workers.push(worker); + if (concurrentTasksLimit !== 0) { + for (let i = 0; i < (taskWorkers || 1); i++) { + const worker = await TaskWorker.create({ + taskBroker, + actionRegistry, + integrations, + logger, + workingDirectory, + additionalTemplateFilters, + additionalTemplateGlobals, + concurrentTasksLimit, + permissions, + }); + workers.push(worker); + } } const actionsToRegister = Array.isArray(actions) diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 6fd5948a5c..25c18a270e 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-common +## 1.4.0 + +### Minor Changes + +- e514aac3eac0: Introduce `each` property on action steps, allowing them to be ran repeatedly. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + ## 1.3.2 ### Patch Changes diff --git a/plugins/scaffolder-common/api-report.md b/plugins/scaffolder-common/api-report.md index 7c40780b57..6113c72659 100644 --- a/plugins/scaffolder-common/api-report.md +++ b/plugins/scaffolder-common/api-report.md @@ -5,6 +5,7 @@ ```ts import { Entity } from '@backstage/catalog-model'; import type { EntityMeta } from '@backstage/catalog-model'; +import type { JsonArray } from '@backstage/types'; import { JsonObject } from '@backstage/types'; import type { JsonValue } from '@backstage/types'; import { KindValidator } from '@backstage/catalog-model'; @@ -36,6 +37,7 @@ export interface TaskSpecV1beta3 { // @public export interface TaskStep { action: string; + each?: string | JsonArray; id: string; if?: string | boolean; input?: JsonObject; diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index a0baa4470f..c281102109 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-common", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", - "version": "1.3.2", + "version": "1.4.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-common/src/TaskSpec.ts b/plugins/scaffolder-common/src/TaskSpec.ts index 7c06fe2b99..6b144b4f78 100644 --- a/plugins/scaffolder-common/src/TaskSpec.ts +++ b/plugins/scaffolder-common/src/TaskSpec.ts @@ -15,7 +15,7 @@ */ import type { EntityMeta, UserEntity } from '@backstage/catalog-model'; -import type { JsonObject, JsonValue } from '@backstage/types'; +import type { JsonArray, JsonObject, JsonValue } from '@backstage/types'; /** * Information about a template that is stored on a task specification. @@ -70,6 +70,10 @@ export interface TaskStep { * When this is false, or if the templated value string evaluates to something that is falsy the step will be skipped. */ if?: string | boolean; + /** + * Run step repeatedly + */ + each?: string | JsonArray; } /** diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index fb0a3f0823..ad964812f7 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-scaffolder-node +## 0.2.0 + +### Minor Changes + +- e514aac3eac0: Introduce `each` property on action steps, allowing them to be ran repeatedly. + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- e07a4914f621: Added several new types that were moved from `@backstage/plugin-scaffolder-backend`. +- 349611126ae2: Added two new alpha extension points, `scaffolderTaskBrokerExtensionPoint` and `scaffolderTemplatingExtensionPoint`. +- 0b1d775be05b: Export `TemplateExample` from the `createTemplateAction` type. +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.1.6-next.2 + +### Patch Changes + +- 0b1d775be05b: Export `TemplateExample` from the `createTemplateAction` type. +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.1.6-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- d3b31a791eb1: Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-scaffolder-common@1.3.2 + ## 0.1.6-next.0 ### Patch Changes diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index 46aba960d8..52ce3c8d13 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -7,9 +7,19 @@ import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; +import { JsonValue } from '@backstage/types'; import { Logger } from 'winston'; +import { Observable } from '@backstage/types'; import { Schema } from 'jsonschema'; +import { ScmIntegrations } from '@backstage/integration'; +import { SpawnOptionsWithoutStdio } from 'child_process'; +import { TaskBroker as TaskBroker_2 } from '@backstage/plugin-scaffolder-node'; +import { TaskSpec } from '@backstage/plugin-scaffolder-common'; +import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node'; +import { TemplateFilter as TemplateFilter_2 } from '@backstage/plugin-scaffolder-node'; +import { TemplateGlobal as TemplateGlobal_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateInfo } from '@backstage/plugin-scaffolder-common'; +import { UrlReader } from '@backstage/backend-common'; import { UserEntity } from '@backstage/catalog-model'; import { Writable } from 'stream'; import { z } from 'zod'; @@ -36,6 +46,7 @@ export type ActionContext< ref?: string; }; signal?: AbortSignal; + each?: JsonObject; }; // @public @@ -67,20 +78,163 @@ export const createTemplateAction: < >, ) => TemplateAction; +// @public +export function executeShellCommand( + options: ExecuteShellCommandOptions, +): Promise; + +// @public +export type ExecuteShellCommandOptions = { + command: string; + args: string[]; + options?: SpawnOptionsWithoutStdio; + logStream?: Writable; +}; + +// @public +export function fetchContents(options: { + reader: UrlReader; + integrations: ScmIntegrations; + baseUrl?: string; + fetchUrl?: string; + outputPath: string; +}): Promise; + +// @public +export function fetchFile(options: { + reader: UrlReader; + integrations: ScmIntegrations; + baseUrl?: string; + fetchUrl?: string; + outputPath: string; +}): Promise; + // @alpha export interface ScaffolderActionsExtensionPoint { // (undocumented) - addActions(...actions: TemplateAction[]): void; + addActions(...actions: TemplateAction_2[]): void; } // @alpha export const scaffolderActionsExtensionPoint: ExtensionPoint; +// @alpha +export interface ScaffolderTaskBrokerExtensionPoint { + // (undocumented) + setTaskBroker(taskBroker: TaskBroker_2): void; +} + +// @alpha +export const scaffolderTaskBrokerExtensionPoint: ExtensionPoint; + +// @alpha +export interface ScaffolderTemplatingExtensionPoint { + // (undocumented) + addTemplateFilters(filters: Record): void; + // (undocumented) + addTemplateGlobals(filters: Record): void; +} + +// @alpha +export const scaffolderTemplatingExtensionPoint: ExtensionPoint; + +// @public +export type SerializedTask = { + id: string; + spec: TaskSpec; + status: TaskStatus; + createdAt: string; + lastHeartbeatAt?: string; + createdBy?: string; + secrets?: TaskSecrets; +}; + +// @public +export type SerializedTaskEvent = { + id: number; + taskId: string; + body: JsonObject; + type: TaskEventType; + createdAt: string; +}; + +// @public +export interface TaskBroker { + // (undocumented) + cancel?(taskId: string): Promise; + // (undocumented) + claim(): Promise; + // (undocumented) + dispatch( + options: TaskBrokerDispatchOptions, + ): Promise; + // (undocumented) + event$(options: { taskId: string; after: number | undefined }): Observable<{ + events: SerializedTaskEvent[]; + }>; + // (undocumented) + get(taskId: string): Promise; + // (undocumented) + list?(options?: { createdBy?: string }): Promise<{ + tasks: SerializedTask[]; + }>; + // (undocumented) + vacuumTasks(options: { timeoutS: number }): Promise; +} + +// @public +export type TaskBrokerDispatchOptions = { + spec: TaskSpec; + secrets?: TaskSecrets; + createdBy?: string; +}; + +// @public +export type TaskBrokerDispatchResult = { + taskId: string; +}; + +// @public +export type TaskCompletionState = 'failed' | 'completed'; + +// @public +export interface TaskContext { + // (undocumented) + cancelSignal: AbortSignal; + // (undocumented) + complete(result: TaskCompletionState, metadata?: JsonObject): Promise; + // (undocumented) + createdBy?: string; + // (undocumented) + done: boolean; + // (undocumented) + emitLog(message: string, logMetadata?: JsonObject): Promise; + // (undocumented) + getWorkspaceName(): Promise; + // (undocumented) + isDryRun?: boolean; + // (undocumented) + secrets?: TaskSecrets; + // (undocumented) + spec: TaskSpec; +} + +// @public +export type TaskEventType = 'completion' | 'log' | 'cancelled'; + // @public export type TaskSecrets = Record & { backstageToken?: string; }; +// @public +export type TaskStatus = + | 'cancelled' + | 'completed' + | 'failed' + | 'open' + | 'processing'; + // @public (undocumented) export type TemplateAction< TActionInput extends JsonObject = JsonObject, @@ -109,10 +263,7 @@ export type TemplateActionOptions< > = { id: string; description?: string; - examples?: { - description: string; - example: string; - }[]; + examples?: TemplateExample[]; supportsDryRun?: boolean; schema?: { input?: TInputSchema; @@ -120,4 +271,18 @@ export type TemplateActionOptions< }; handler: (ctx: ActionContext) => Promise; }; + +// @public (undocumented) +export type TemplateExample = { + description: string; + example: string; +}; + +// @public (undocumented) +export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; + +// @public (undocumented) +export type TemplateGlobal = + | ((...args: JsonValue[]) => JsonValue | undefined) + | JsonValue; ``` diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 59e80f1c1c..ac22b7c2d6 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-node", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", - "version": "0.1.6-next.0", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,17 +30,22 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/types": "workspace:^", + "fs-extra": "10.1.0", "jsonschema": "^1.2.6", "winston": "^3.2.1", "zod": "^3.21.4", "zod-to-json-schema": "^3.20.4" }, "devDependencies": { - "@backstage/cli": "workspace:^" + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^" }, "files": [ "alpha", diff --git a/plugins/scaffolder-node/src/actions/createTemplateAction.ts b/plugins/scaffolder-node/src/actions/createTemplateAction.ts index a667f91744..38aba32778 100644 --- a/plugins/scaffolder-node/src/actions/createTemplateAction.ts +++ b/plugins/scaffolder-node/src/actions/createTemplateAction.ts @@ -20,6 +20,12 @@ import { Schema } from 'jsonschema'; import zodToJsonSchema from 'zod-to-json-schema'; import { JsonObject } from '@backstage/types'; +/** @public */ +export type TemplateExample = { + description: string; + example: string; +}; + /** @public */ export type TemplateActionOptions< TActionInput extends JsonObject = {}, @@ -29,7 +35,7 @@ export type TemplateActionOptions< > = { id: string; description?: string; - examples?: { description: string; example: string }[]; + examples?: TemplateExample[]; supportsDryRun?: boolean; schema?: { input?: TInputSchema; diff --git a/plugins/scaffolder-node/src/actions/executeShellCommand.ts b/plugins/scaffolder-node/src/actions/executeShellCommand.ts new file mode 100644 index 0000000000..ce54989589 --- /dev/null +++ b/plugins/scaffolder-node/src/actions/executeShellCommand.ts @@ -0,0 +1,75 @@ +/* + * 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 { spawn, SpawnOptionsWithoutStdio } from 'child_process'; +import { PassThrough, Writable } from 'stream'; + +/** + * Options for {@link executeShellCommand}. + * + * @public + */ +export type ExecuteShellCommandOptions = { + /** command to run */ + command: string; + /** arguments to pass the command */ + args: string[]; + /** options to pass to spawn */ + options?: SpawnOptionsWithoutStdio; + /** stream to capture stdout and stderr output */ + logStream?: Writable; +}; + +/** + * Run a command in a sub-process, normally a shell command. + * + * @public + */ +export async function executeShellCommand( + options: ExecuteShellCommandOptions, +): Promise { + const { + command, + args, + options: spawnOptions, + logStream = new PassThrough(), + } = options; + + await new Promise((resolve, reject) => { + const process = spawn(command, args, spawnOptions); + + process.stdout.on('data', stream => { + logStream.write(stream); + }); + + process.stderr.on('data', stream => { + logStream.write(stream); + }); + + process.on('error', error => { + return reject(error); + }); + + process.on('close', code => { + if (code !== 0) { + return reject( + new Error(`Command ${command} failed, exit code: ${code}`), + ); + } + return resolve(); + }); + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts b/plugins/scaffolder-node/src/actions/fetch.test.ts similarity index 98% rename from plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts rename to plugins/scaffolder-node/src/actions/fetch.test.ts index 3c4a677d4d..55b949fca7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts +++ b/plugins/scaffolder-node/src/actions/fetch.test.ts @@ -21,10 +21,10 @@ import { resolve as resolvePath } from 'path'; import { UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents, fetchFile } from './helpers'; +import { fetchContents, fetchFile } from './fetch'; import os from 'os'; -describe('fetchContent helper', () => { +describe('fetchContents helper', () => { beforeEach(() => { jest.clearAllMocks(); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts b/plugins/scaffolder-node/src/actions/fetch.ts similarity index 99% rename from plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts rename to plugins/scaffolder-node/src/actions/fetch.ts index 301c9991c4..66bbc0a842 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts +++ b/plugins/scaffolder-node/src/actions/fetch.ts @@ -54,6 +54,8 @@ export async function fetchContents(options: { /** * A helper function that reads the content of a single file from the given URL. * Can be used in your own actions, and also used behind `fetch:plain:file` + * + * @public */ export async function fetchFile(options: { reader: UrlReader; diff --git a/plugins/scaffolder-node/src/actions/index.ts b/plugins/scaffolder-node/src/actions/index.ts index e4af098356..47af320ccb 100644 --- a/plugins/scaffolder-node/src/actions/index.ts +++ b/plugins/scaffolder-node/src/actions/index.ts @@ -17,5 +17,11 @@ export { createTemplateAction, type TemplateActionOptions, + type TemplateExample, } from './createTemplateAction'; +export { + executeShellCommand, + type ExecuteShellCommandOptions, +} from './executeShellCommand'; +export { fetchContents, fetchFile } from './fetch'; export { type ActionContext, type TemplateAction } from './types'; diff --git a/plugins/scaffolder-node/src/actions/types.ts b/plugins/scaffolder-node/src/actions/types.ts index e35f55aa67..7e8c2b4f5f 100644 --- a/plugins/scaffolder-node/src/actions/types.ts +++ b/plugins/scaffolder-node/src/actions/types.ts @@ -71,6 +71,11 @@ export type ActionContext< * Implement the signal to make your custom step abortable https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal */ signal?: AbortSignal; + + /** + * Optional value of each invocation + */ + each?: JsonObject; }; /** @public */ diff --git a/plugins/scaffolder-node/src/extensions.ts b/plugins/scaffolder-node/src/extensions.ts index 1ae51ce2e6..b85da5b590 100644 --- a/plugins/scaffolder-node/src/extensions.ts +++ b/plugins/scaffolder-node/src/extensions.ts @@ -15,7 +15,12 @@ */ import { createExtensionPoint } from '@backstage/backend-plugin-api'; -import { TemplateAction } from './actions'; +import { + TemplateAction, + TemplateFilter, + TemplateGlobal, + TaskBroker, +} from '@backstage/plugin-scaffolder-node'; /** * Extension point for managing scaffolder actions. @@ -35,3 +40,42 @@ export const scaffolderActionsExtensionPoint = createExtensionPoint({ id: 'scaffolder.actions', }); + +/** + * Extension point for replacing the scaffolder task broker. + * + * @alpha + */ +export interface ScaffolderTaskBrokerExtensionPoint { + setTaskBroker(taskBroker: TaskBroker): void; +} + +/** + * Extension point for replacing the scaffolder task broker. + * + * @alpha + */ +export const scaffolderTaskBrokerExtensionPoint = + createExtensionPoint({ + id: 'scaffolder.taskBroker', + }); + +/** + * Extension point for adding template filters and globals. + * + * @alpha + */ +export interface ScaffolderTemplatingExtensionPoint { + addTemplateFilters(filters: Record): void; + addTemplateGlobals(filters: Record): void; +} + +/** + * Extension point for adding template filters and globals. + * + * @alpha + */ +export const scaffolderTemplatingExtensionPoint = + createExtensionPoint({ + id: 'scaffolder.templating', + }); diff --git a/plugins/scaffolder-node/src/index.ts b/plugins/scaffolder-node/src/index.ts index 2a7c06234e..dcce065108 100644 --- a/plugins/scaffolder-node/src/index.ts +++ b/plugins/scaffolder-node/src/index.ts @@ -22,7 +22,12 @@ export * from './actions'; export * from './tasks'; +export type { TemplateFilter, TemplateGlobal } from './types'; export { scaffolderActionsExtensionPoint, type ScaffolderActionsExtensionPoint, + scaffolderTaskBrokerExtensionPoint, + type ScaffolderTaskBrokerExtensionPoint, + scaffolderTemplatingExtensionPoint, + type ScaffolderTemplatingExtensionPoint, } from './extensions'; diff --git a/plugins/scaffolder-node/src/tasks/index.ts b/plugins/scaffolder-node/src/tasks/index.ts index 0c1d0d813e..930de95237 100644 --- a/plugins/scaffolder-node/src/tasks/index.ts +++ b/plugins/scaffolder-node/src/tasks/index.ts @@ -14,4 +14,15 @@ * limitations under the License. */ -export { type TaskSecrets } from './types'; +export type { + TaskSecrets, + SerializedTask, + SerializedTaskEvent, + TaskBroker, + TaskBrokerDispatchOptions, + TaskBrokerDispatchResult, + TaskCompletionState, + TaskContext, + TaskEventType, + TaskStatus, +} from './types'; diff --git a/plugins/scaffolder-node/src/tasks/types.ts b/plugins/scaffolder-node/src/tasks/types.ts index c17b3cbde3..8c9cc29e31 100644 --- a/plugins/scaffolder-node/src/tasks/types.ts +++ b/plugins/scaffolder-node/src/tasks/types.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +import { TaskSpec } from '@backstage/plugin-scaffolder-common'; +import { JsonObject, Observable } from '@backstage/types'; + /** * TaskSecrets * @@ -22,3 +25,124 @@ export type TaskSecrets = Record & { backstageToken?: string; }; + +/** + * The status of each step of the Task + * + * @public + */ +export type TaskStatus = + | 'cancelled' + | 'completed' + | 'failed' + | 'open' + | 'processing'; + +/** + * The state of a completed task. + * + * @public + */ +export type TaskCompletionState = 'failed' | 'completed'; + +/** + * SerializedTask + * + * @public + */ +export type SerializedTask = { + id: string; + spec: TaskSpec; + status: TaskStatus; + createdAt: string; + lastHeartbeatAt?: string; + createdBy?: string; + secrets?: TaskSecrets; +}; + +/** + * TaskEventType + * + * @public + */ +export type TaskEventType = 'completion' | 'log' | 'cancelled'; + +/** + * SerializedTaskEvent + * + * @public + */ +export type SerializedTaskEvent = { + id: number; + taskId: string; + body: JsonObject; + type: TaskEventType; + createdAt: string; +}; + +/** + * The result of {@link TaskBroker.dispatch} + * + * @public + */ +export type TaskBrokerDispatchResult = { + taskId: string; +}; + +/** + * The options passed to {@link TaskBroker.dispatch} + * Currently a spec and optional secrets + * + * @public + */ +export type TaskBrokerDispatchOptions = { + spec: TaskSpec; + secrets?: TaskSecrets; + createdBy?: string; +}; + +/** + * Task + * + * @public + */ +export interface TaskContext { + cancelSignal: AbortSignal; + spec: TaskSpec; + secrets?: TaskSecrets; + createdBy?: string; + done: boolean; + isDryRun?: boolean; + + complete(result: TaskCompletionState, metadata?: JsonObject): Promise; + + emitLog(message: string, logMetadata?: JsonObject): Promise; + + getWorkspaceName(): Promise; +} + +/** + * TaskBroker + * + * @public + */ +export interface TaskBroker { + cancel?(taskId: string): Promise; + + claim(): Promise; + + dispatch( + options: TaskBrokerDispatchOptions, + ): Promise; + + vacuumTasks(options: { timeoutS: number }): Promise; + + event$(options: { + taskId: string; + after: number | undefined; + }): Observable<{ events: SerializedTaskEvent[] }>; + + get(taskId: string): Promise; + + list?(options?: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; +} diff --git a/plugins/scaffolder-node/src/types.ts b/plugins/scaffolder-node/src/types.ts new file mode 100644 index 0000000000..bae106c84e --- /dev/null +++ b/plugins/scaffolder-node/src/types.ts @@ -0,0 +1,25 @@ +/* + * 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 { JsonValue } from '@backstage/types'; + +/** @public */ +export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; + +/** @public */ +export type TemplateGlobal = + | ((...args: JsonValue[]) => JsonValue | undefined) + | JsonValue; diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index e4762983c7..91cde8ccf2 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-scaffolder-react +## 1.5.2 + +### Patch Changes + +- ba9ee98a37bd: Fixed bug in Workflow component by passing a prop `templateName` down to Stepper component. +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + +## 1.5.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 1.5.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index ddf27cc83d..657a66e6e3 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-react", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", - "version": "1.5.2-next.0", + "version": "1.5.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 2d1a68c3ff..9098706875 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,58 @@ # @backstage/plugin-scaffolder +## 1.14.2 + +### Patch Changes + +- 8a0490fb669e: Fix the get entities query in the `MyGroupsPicker` to query the `kind=Group` entities. +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/plugin-scaffolder-common@1.4.0 + - @backstage/plugin-scaffolder-react@1.5.2 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-react@0.4.14 + +## 1.14.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/plugin-scaffolder-react@1.5.2-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 1.14.2-next.1 + +### Patch Changes + +- 8a0490fb669e: Fix the get entities query in the `MyGroupsPicker` to query the `kind=Group` entities. +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-permission-react@0.4.14 + - @backstage/plugin-scaffolder-common@1.3.2 + - @backstage/plugin-scaffolder-react@1.5.2-next.0 + ## 1.14.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 08a3004e08..f4dae39d96 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "1.14.2-next.0", + "version": "1.14.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx index 0bdfe5f153..099d6fa6ed 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx @@ -129,7 +129,7 @@ describe('', () => { expect(catalogApi.getEntities).toHaveBeenCalledWith({ filter: { - type: 'Group', + kind: 'Group', 'relations.hasMember': ['user:default/bob'], }, }); @@ -201,8 +201,8 @@ describe('', () => { // Simulate user input const inputField = getByRole('combobox'); - userEvent.click(inputField); - userEvent.type(inputField, 'group'); + await userEvent.click(inputField); + await userEvent.type(inputField, 'group'); // Wait for the dropdown elements to appear await waitFor(() => { @@ -257,8 +257,8 @@ describe('', () => { ); const inputField = getByRole('combobox'); - userEvent.click(inputField); - userEvent.type(inputField, 'group'); + await userEvent.click(inputField); + await userEvent.type(inputField, 'group'); await waitFor(() => { expect( @@ -267,7 +267,7 @@ describe('', () => { }); const option = getByRole('option', { name: 'My First Group' }); - userEvent.click(option); + await userEvent.click(option); await waitFor(() => { expect(onChange).toHaveBeenCalledTimes(1); diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx index 28bc2b93fa..fefdd4230c 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx @@ -62,7 +62,7 @@ export const MyGroupsPicker = (props: MyGroupsPickerProps) => { const { items } = await catalogApi.getEntities({ filter: { - type: 'Group', + kind: 'Group', ['relations.hasMember']: [userEntityRef], }, }); diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index a01a5a41a1..ce170b95dd 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,58 @@ # @backstage/plugin-search-backend-module-catalog +## 0.1.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- 12a8c94eda8d: Add package repository and homepage metadata +- ec1032129e47: Breaking change in the alpha export `searchModuleCatalogCollator`: Moved collator settings from module options into app-config. You are now expected to set up the catalog collator under the `search.collators.catalog` configuration key. There is also a new `catalogCollatorExtensionPoint` extension point for the module, wherein you can set custom transformers. +- d4f19a16bd52: Add User Entity email to the search index so that users can be found by their email. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 0.1.4-next.2 + +### Patch Changes + +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/README.md b/plugins/search-backend-module-catalog/README.md index b5960e2789..9deeccb910 100644 --- a/plugins/search-backend-module-catalog/README.md +++ b/plugins/search-backend-module-catalog/README.md @@ -2,11 +2,18 @@ > DISCLAIMER: The new backend system is in alpha, and so are the search backend module support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, you can find getting started guides below. -This package exports catalog backend modules responsible for extending search. +This package exports a module that extends the search backend to also indexing the entities of your catalog. -## Example +## Installation -### Use default schedule +Add the module package as a dependency: + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-search-backend-module-catalog +``` + +Add the collator to your backend instance, along with the search plugin itself: ```tsx // packages/backend-next/src/index.ts @@ -20,22 +27,50 @@ backend.add(searchModuleCatalogCollator()); backend.start(); ``` -### Use custom schedule +You may also want to add configuration parameters to your app-config, for example for controlling the scheduled indexing interval. These parameters should be placed under the `search.collators.catalog` key. See [the config definition file](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-catalog/config.d.ts) for more details. + +## Advanced Customizations + +This module also has an extension point, which lets you inject advanced customizations. Here's an example of how to leverage that extension point to tweak the transformer used for building the search indexer documents: ```tsx // packages/backend-next/src/index.ts import { createBackend } from '@backstage/backend-defaults'; +import { createBackendModule } from '@backstage/backend-plugin-api'; import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; -import { searchModuleCatalogCollator } from '@backstage/plugin-search-backend-module-catalog/alpha'; +import { CatalogCollatorEntityTransformer } from '@backstage/plugin-search-backend-module-catalog'; +import { + searchModuleCatalogCollator, + catalogCollatorExtensionPoint, +} from '@backstage/plugin-search-backend-module-catalog/alpha'; -const schedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, -}; +const customTransformer: CatalogCollatorEntityTransformer = entity => ({ + title: entity.metadata.title || entity.metadata.name, + text: entity.metadata.description || '', + componentType: entity.spec?.type?.toString() || 'other', + type: entity.spec?.type?.toString() || 'other', + namespace: entity.metadata.namespace || 'default', + kind: entity.kind, + lifecycle: (entity.spec?.lifecycle as string) || '', + owner: (entity.spec?.owner as string) || '', +}); const backend = createBackend(); backend.add(searchPlugin()); -backend.add(searchModuleCatalogCollator({ schedule })); +backend.add(searchModuleCatalogCollator()); +backend.add( + createBackendModule({ + pluginId: 'search', + moduleId: 'myCatalogCollatorOptions', + register(reg) { + reg.registerInit({ + deps: { collator: catalogCollatorExtensionPoint }, + init({ collator }) { + collator.setEntityTransformer(customTransformer); + }, + }); + }, + })(), +); backend.start(); ``` diff --git a/plugins/search-backend-module-catalog/alpha-api-report.md b/plugins/search-backend-module-catalog/alpha-api-report.md index 70fae9ef93..658c1aec15 100644 --- a/plugins/search-backend-module-catalog/alpha-api-report.md +++ b/plugins/search-backend-module-catalog/alpha-api-report.md @@ -4,21 +4,19 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { DefaultCatalogCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-catalog'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { CatalogCollatorEntityTransformer } from '@backstage/plugin-search-backend-module-catalog'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; // @alpha -export const searchModuleCatalogCollator: ( - options?: SearchModuleCatalogCollatorOptions | undefined, -) => BackendFeature; - -// @alpha -export type SearchModuleCatalogCollatorOptions = Omit< - DefaultCatalogCollatorFactoryOptions, - 'discovery' | 'tokenManager' -> & { - schedule?: TaskScheduleDefinition; +export type CatalogCollatorExtensionPoint = { + setEntityTransformer(transformer: CatalogCollatorEntityTransformer): void; }; +// @alpha +export const catalogCollatorExtensionPoint: ExtensionPoint; + +// @alpha +export const searchModuleCatalogCollator: () => BackendFeature; + // (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 b773860fe8..6f5813ca9e 100644 --- a/plugins/search-backend-module-catalog/api-report.md +++ b/plugins/search-backend-module-catalog/api-report.md @@ -24,11 +24,11 @@ export type CatalogCollatorEntityTransformer = ( // @public (undocumented) export const defaultCatalogCollatorEntityTransformer: CatalogCollatorEntityTransformer; -// @public (undocumented) +// @public export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { // (undocumented) static fromConfig( - _config: Config, + configRoot: Config, options: DefaultCatalogCollatorFactoryOptions, ): DefaultCatalogCollatorFactory; // (undocumented) diff --git a/plugins/search-backend-module-catalog/config.d.ts b/plugins/search-backend-module-catalog/config.d.ts new file mode 100644 index 0000000000..0105764afb --- /dev/null +++ b/plugins/search-backend-module-catalog/config.d.ts @@ -0,0 +1,55 @@ +/* + * 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 { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + +export interface Config { + search?: { + collators?: { + /** + * Configuration options for `@backstage/plugin-search-backend-module-catalog` + */ + catalog?: { + /** + * A templating string with placeholders, to form the final location of + * the entity. + * + * Defaults to '/catalog/:namespace/:kind/:name' + */ + locationTemplate?: string; + /** + * A filter expression passed to the catalog client, to select what + * entities to collate. + * + * Defaults to no filter, ie indexing all entities. + */ + filter?: object; + /** + * The number of entities to process at a time. Keep this at a + * reasonable number to avoid overloading either the catalog or the + * search backend. + * + * Defaults to 500 + */ + batchSize?: number; + /** + * The schedule for how often to run the collation job. + */ + schedule?: TaskScheduleDefinitionConfig; + }; + }; + }; +} diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index c1f46b7a3a..e3509aec8a 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-catalog", "description": "A module for the search backend that exports catalog modules", - "version": "0.1.4-next.0", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -48,7 +48,9 @@ "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^" @@ -60,6 +62,8 @@ "msw": "^1.0.0" }, "files": [ - "dist" - ] + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" } diff --git a/plugins/search-backend-module-catalog/src/alpha.test.ts b/plugins/search-backend-module-catalog/src/alpha.test.ts index 8a3082c6aa..33ac978d41 100644 --- a/plugins/search-backend-module-catalog/src/alpha.test.ts +++ b/plugins/search-backend-module-catalog/src/alpha.test.ts @@ -14,17 +14,11 @@ * limitations under the License. */ -import { startTestBackend } from '@backstage/backend-test-utils'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; import { searchModuleCatalogCollator } from './alpha'; describe('searchModuleCatalogCollator', () => { - const schedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, - }; - it('should register the catalog collator to the search index registry extension point with factory and schedule', async () => { const extensionPointMock = { addCollator: jest.fn(), @@ -34,7 +28,7 @@ describe('searchModuleCatalogCollator', () => { extensionPoints: [ [searchIndexRegistryExtensionPoint, extensionPointMock], ], - features: [searchModuleCatalogCollator({ schedule })], + features: [searchModuleCatalogCollator()], }); expect(extensionPointMock.addCollator).toHaveBeenCalledTimes(1); @@ -43,4 +37,37 @@ describe('searchModuleCatalogCollator', () => { schedule: expect.objectContaining({ run: expect.any(Function) }), }); }); + + it('refuses to start up with a broken schedule', async () => { + await expect( + startTestBackend({ + extensionPoints: [ + [ + searchIndexRegistryExtensionPoint, + { + addCollator: jest.fn(), + }, + ], + ], + features: [ + searchModuleCatalogCollator(), + mockServices.rootConfig.factory({ + data: { + search: { + collators: { + catalog: { + schedule: { + frequency: { minutes: 10 }, + timeout: { minutes: 'boo' }, + initialDelay: { seconds: 3 }, + }, + }, + }, + }, + }, + }), + ], + }), + ).rejects.toThrow(/Invalid schedule/); + }); }); diff --git a/plugins/search-backend-module-catalog/src/alpha.ts b/plugins/search-backend-module-catalog/src/alpha.ts index c633dc71b2..0edf90fc7f 100644 --- a/plugins/search-backend-module-catalog/src/alpha.ts +++ b/plugins/search-backend-module-catalog/src/alpha.ts @@ -22,68 +22,89 @@ import { coreServices, createBackendModule, + createExtensionPoint, } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; -import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; - +import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; import { + CatalogCollatorEntityTransformer, DefaultCatalogCollatorFactory, - DefaultCatalogCollatorFactoryOptions, } from '@backstage/plugin-search-backend-module-catalog'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; +import { readScheduleConfigOptions } from './collators/config'; /** + * Options for {@link catalogCollatorExtensionPoint}. + * * @alpha - * Options for {@link searchModuleCatalogCollator}. */ -export type SearchModuleCatalogCollatorOptions = Omit< - DefaultCatalogCollatorFactoryOptions, - 'discovery' | 'tokenManager' -> & { - schedule?: TaskScheduleDefinition; +export type CatalogCollatorExtensionPoint = { + /** + * Allows you to customize how entities are shaped into documents. + */ + setEntityTransformer(transformer: CatalogCollatorEntityTransformer): void; }; /** + * Extension point for customizing how catalog entities are shaped into + * documents for the search backend, when using + * {@link searchModuleCatalogCollator}. + * * @alpha - * Search backend module for the Catalog index. */ -export const searchModuleCatalogCollator = createBackendModule( - (options?: SearchModuleCatalogCollatorOptions) => ({ - moduleId: 'catalogCollator', - pluginId: 'search', - register(env) { - env.registerInit({ - deps: { - config: coreServices.config, - discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, - scheduler: coreServices.scheduler, - indexRegistry: searchIndexRegistryExtensionPoint, - }, - async init({ - config, - discovery, - tokenManager, - scheduler, - indexRegistry, - }) { - const defaultSchedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, - }; +export const catalogCollatorExtensionPoint = + createExtensionPoint({ + id: 'search.catalogCollator.extension', + }); - indexRegistry.addCollator({ - schedule: scheduler.createScheduledTaskRunner( - options?.schedule ?? defaultSchedule, - ), - factory: DefaultCatalogCollatorFactory.fromConfig(config, { - ...options, - discovery, - tokenManager, - }), - }); - }, - }); - }, - }), -); +/** + * Search backend module for the Catalog index. + * + * @alpha + */ +export const searchModuleCatalogCollator = createBackendModule({ + moduleId: 'catalogCollator', + pluginId: 'search', + register(env) { + let entityTransformer: CatalogCollatorEntityTransformer | undefined; + + env.registerExtensionPoint(catalogCollatorExtensionPoint, { + setEntityTransformer(transformer) { + if (entityTransformer) { + throw new Error('setEntityTransformer can only be called once'); + } + entityTransformer = transformer; + }, + }); + + env.registerInit({ + deps: { + config: coreServices.rootConfig, + discovery: coreServices.discovery, + tokenManager: coreServices.tokenManager, + scheduler: coreServices.scheduler, + indexRegistry: searchIndexRegistryExtensionPoint, + catalog: catalogServiceRef, + }, + async init({ + config, + discovery, + tokenManager, + scheduler, + indexRegistry, + catalog, + }) { + indexRegistry.addCollator({ + schedule: scheduler.createScheduledTaskRunner( + readScheduleConfigOptions(config), + ), + factory: DefaultCatalogCollatorFactory.fromConfig(config, { + 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 462e777dff..7af3a8e9a2 100644 --- a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts +++ b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { PluginEndpointDiscovery, TokenManager, diff --git a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts index ed81d39bbb..586346c385 100644 --- a/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts +++ b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; import { PluginEndpointDiscovery, TokenManager, @@ -24,27 +23,45 @@ import { CatalogClient, GetEntitiesRequest, } from '@backstage/catalog-client'; -import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; -import { catalogEntityReadPermission } from '@backstage/plugin-catalog-common/alpha'; +import { stringifyEntityRef } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; +import { catalogEntityReadPermission } from '@backstage/plugin-catalog-common/alpha'; import { Permission } from '@backstage/plugin-permission-common'; +import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { Readable } from 'stream'; import { CatalogCollatorEntityTransformer } from './CatalogCollatorEntityTransformer'; +import { readCollatorConfigOptions } from './config'; import { defaultCatalogCollatorEntityTransformer } from './defaultCatalogCollatorEntityTransformer'; -import { stringifyEntityRef } from '@backstage/catalog-model'; /** @public */ export type DefaultCatalogCollatorFactoryOptions = { discovery: PluginEndpointDiscovery; tokenManager: TokenManager; + /** + * @deprecated Use the config key `search.collators.catalog.locationTemplate` instead. + */ locationTemplate?: string; + /** + * @deprecated Use the config key `search.collators.catalog.filter` instead. + */ filter?: GetEntitiesRequest['filter']; + /** + * @deprecated Use the config key `search.collators.catalog.batchSize` instead. + */ batchSize?: number; catalogClient?: CatalogApi; + /** + * Allows you to customize how entities are shaped into documents. + */ entityTransformer?: CatalogCollatorEntityTransformer; }; -/** @public */ +/** + * Collates entities from the Catalog into documents for the search backend. + * + * @public + */ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { public readonly type = 'software-catalog'; public readonly visibilityPermission: Permission = @@ -58,13 +75,31 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { private entityTransformer: CatalogCollatorEntityTransformer; static fromConfig( - _config: Config, + configRoot: Config, options: DefaultCatalogCollatorFactoryOptions, ) { - return new DefaultCatalogCollatorFactory(options); + const configOptions = readCollatorConfigOptions(configRoot); + return new DefaultCatalogCollatorFactory({ + locationTemplate: + options.locationTemplate ?? configOptions.locationTemplate, + filter: options.filter ?? configOptions.filter, + batchSize: options.batchSize ?? configOptions.batchSize, + entityTransformer: options.entityTransformer, + discovery: options.discovery, + tokenManager: options.tokenManager, + catalogClient: options.catalogClient, + }); } - private constructor(options: DefaultCatalogCollatorFactoryOptions) { + private constructor(options: { + locationTemplate: string; + filter: GetEntitiesRequest['filter']; + batchSize: number; + entityTransformer?: CatalogCollatorEntityTransformer; + discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; + catalogClient?: CatalogApi; + }) { const { batchSize, discovery, @@ -75,10 +110,9 @@ export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { entityTransformer, } = options; - this.locationTemplate = - locationTemplate || '/catalog/:namespace/:kind/:name'; + this.locationTemplate = locationTemplate; this.filter = filter; - this.batchSize = batchSize || 500; + this.batchSize = batchSize; this.catalogClient = catalogClient || new CatalogClient({ discoveryApi: discovery }); this.tokenManager = tokenManager; diff --git a/plugins/search-backend-module-catalog/src/collators/config.test.ts b/plugins/search-backend-module-catalog/src/collators/config.test.ts new file mode 100644 index 0000000000..dc330f6df2 --- /dev/null +++ b/plugins/search-backend-module-catalog/src/collators/config.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { ConfigReader } from '@backstage/config'; +import { + defaults, + readCollatorConfigOptions, + readScheduleConfigOptions, +} from './config'; + +describe('config', () => { + describe('readScheduleConfigOptions', () => { + it('reads config', () => { + const result = readScheduleConfigOptions( + new ConfigReader({ + search: { + collators: { + catalog: { + schedule: { + frequency: { minutes: 1 }, + timeout: { seconds: 2 }, + initialDelay: { hours: 3 }, + }, + }, + }, + }, + }), + ); + expect(result).toEqual({ + frequency: { minutes: 1 }, + timeout: { seconds: 2 }, + initialDelay: { hours: 3 }, + }); + }); + + it('returns defaults', () => { + const result = readScheduleConfigOptions(new ConfigReader({})); + expect(result).toEqual(defaults.schedule); + }); + }); + + describe('readCollatorConfigOptions', () => { + it('reads config', () => { + const result = readCollatorConfigOptions( + new ConfigReader({ + search: { + collators: { + catalog: { + batchSize: 1, + locationTemplate: '/mock/:namespace/:kind/:name', + filter: { a: 1 }, + }, + }, + }, + }), + ); + expect(result).toEqual({ + batchSize: 1, + locationTemplate: '/mock/:namespace/:kind/:name', + filter: { a: 1 }, + }); + }); + + it('returns defaults', () => { + const result = readCollatorConfigOptions(new ConfigReader({})); + expect(result).toEqual(defaults.collatorOptions); + }); + }); +}); diff --git a/plugins/search-backend-module-catalog/src/collators/config.ts b/plugins/search-backend-module-catalog/src/collators/config.ts new file mode 100644 index 0000000000..d4ae9b5934 --- /dev/null +++ b/plugins/search-backend-module-catalog/src/collators/config.ts @@ -0,0 +1,81 @@ +/* + * 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 { + readTaskScheduleDefinitionFromConfig, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { EntityFilterQuery } from '@backstage/catalog-client'; +import { Config } from '@backstage/config'; +import { InputError } from '@backstage/errors'; + +const configKey = 'search.collators.catalog'; + +export const defaults = { + schedule: { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }, + collatorOptions: { + locationTemplate: '/catalog/:namespace/:kind/:name', + filter: undefined, + batchSize: 500, + }, +}; + +export function readScheduleConfigOptions( + configRoot: Config, +): TaskScheduleDefinition { + let schedule: TaskScheduleDefinition | undefined = undefined; + + const config = configRoot.getOptionalConfig(configKey); + if (config) { + const scheduleConfig = config.getOptionalConfig('schedule'); + if (scheduleConfig) { + try { + schedule = readTaskScheduleDefinitionFromConfig(scheduleConfig); + } catch (error) { + throw new InputError(`Invalid schedule at ${configKey}, ${error}`); + } + } + } + + return schedule ?? defaults.schedule; +} + +export function readCollatorConfigOptions(configRoot: Config): { + locationTemplate: string; + filter: EntityFilterQuery | undefined; + batchSize: number; +} { + const config = configRoot.getOptionalConfig(configKey); + if (!config) { + return defaults.collatorOptions; + } + + return { + locationTemplate: + config.getOptionalString('locationTemplate') ?? + defaults.collatorOptions.locationTemplate, + filter: + config.getOptionalConfig('filter')?.get() ?? + defaults.collatorOptions.filter, + batchSize: + config.getOptionalNumber('batchSize') ?? + defaults.collatorOptions.batchSize, + }; +} diff --git a/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.test.ts b/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.test.ts index 8206a58599..3fe6a9c3e5 100644 --- a/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.test.ts +++ b/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.test.ts @@ -42,6 +42,7 @@ const userEntity = { spec: { profile: { displayName: 'User 1', + email: 'test@test.com', }, }, }; @@ -94,7 +95,7 @@ describe('DefaultCatalogCollatorEntityTransformer', () => { expect(document).toMatchObject({ title: userEntity.metadata.name, - text: `${userEntity.metadata.description} : ${userEntity.spec.profile.displayName}`, + text: `${userEntity.metadata.description} : ${userEntity.spec.profile.displayName} : ${userEntity.spec.profile.email}`, namespace: 'default', componentType: 'other', lifecycle: '', diff --git a/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.ts b/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.ts index 2fc0c5d50d..db239b8669 100644 --- a/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.ts +++ b/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.ts @@ -27,6 +27,12 @@ const getDocumentText = (entity: Entity): string => { } } + if (isUserEntity(entity)) { + if (entity.spec?.profile?.email) { + documentTexts.push(entity.spec.profile.email); + } + } + return documentTexts.join(' : '); }; diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 268f563330..0d50bb342d 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.3.3 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 631eb3816b48: Index templates can now be configured through configuration under the `search.elasticsearch.indexTemplates`. In addition, the `ElasticSearchSearchEngine.fromConfig` now also accepts a `LoggerService` as the `logger` option as well as a new `translator` option. + + The alpha `searchModuleElasticsearchEngine` export no longer accepts options and a new alpha `elasticsearchTranslatorExtensionPoint` export has been added which lets you customize the query translator. + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/integration-aws-node@0.1.5 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## 1.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## 1.3.3-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + ## 1.3.3-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/alpha-api-report.md b/plugins/search-backend-module-elasticsearch/alpha-api-report.md index 91007c20de..9e5e7c2136 100644 --- a/plugins/search-backend-module-elasticsearch/alpha-api-report.md +++ b/plugins/search-backend-module-elasticsearch/alpha-api-report.md @@ -4,19 +4,20 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { ElasticSearchCustomIndexTemplate } from '@backstage/plugin-search-backend-module-elasticsearch'; import { ElasticSearchQueryTranslator } from '@backstage/plugin-search-backend-module-elasticsearch'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; + +// @alpha (undocumented) +export interface ElasticSearchQueryTranslatorExtensionPoint { + // (undocumented) + setTranslator(translator: ElasticSearchQueryTranslator): void; +} // @alpha -export const searchModuleElasticsearchEngine: ( - options?: SearchModuleElasticsearchEngineOptions | undefined, -) => BackendFeature; +export const elasticsearchTranslatorExtensionPoint: ExtensionPoint; // @alpha -export type SearchModuleElasticsearchEngineOptions = { - translator?: ElasticSearchQueryTranslator; - indexTemplate?: ElasticSearchCustomIndexTemplate; -}; +export const searchModuleElasticsearchEngine: () => BackendFeature; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/search-backend-module-elasticsearch/api-report.md b/plugins/search-backend-module-elasticsearch/api-report.md index b567414b22..408ec288d0 100644 --- a/plugins/search-backend-module-elasticsearch/api-report.md +++ b/plugins/search-backend-module-elasticsearch/api-report.md @@ -15,6 +15,7 @@ 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-common'; import { SearchQuery } from '@backstage/plugin-search-common'; @@ -299,10 +300,11 @@ export interface ElasticSearchNodeOptions { // @public export type ElasticSearchOptions = { - logger: Logger; + logger: Logger | LoggerService; config: Config; aliasPostfix?: string; indexPrefix?: string; + translator?: ElasticSearchQueryTranslator; }; // @public @@ -322,7 +324,7 @@ export class ElasticSearchSearchEngine implements SearchEngine { elasticSearchClientOptions: ElasticSearchClientOptions, aliasPostfix: string, indexPrefix: string, - logger: Logger, + logger: Logger | LoggerService, batchSize: number, highlightOptions?: ElasticSearchHighlightOptions, ); @@ -365,7 +367,7 @@ export type ElasticSearchSearchEngineIndexerOptions = { indexPrefix: string; indexSeparator: string; alias: string; - logger: Logger; + logger: Logger | LoggerService; elasticSearchClientWrapper: ElasticSearchClientWrapper; batchSize: number; }; diff --git a/plugins/search-backend-module-elasticsearch/config.d.ts b/plugins/search-backend-module-elasticsearch/config.d.ts index 16190e7ed0..d7fdd0cd1b 100644 --- a/plugins/search-backend-module-elasticsearch/config.d.ts +++ b/plugins/search-backend-module-elasticsearch/config.d.ts @@ -43,6 +43,33 @@ export interface Config { */ fragmentDelimiter?: string; }; + + /** Elasticsearch specific index template bodies */ + indexTemplates?: Array<{ + name: string; + body: { + /** + * Array of wildcard (*) expressions used to match the names of data streams and indices during creation. + */ + index_patterns: string[]; + + /** + * An ordered list of component template names. + * Component templates are merged in the order specified, + * meaning that the last component template specified has the highest precedence. + */ + composed_of?: string[]; + + /** + * See available properties of template + * https://www.elastic.co/guide/en/elasticsearch/reference/7.15/indices-put-template.html#put-index-template-api-request-body + */ + template?: { + [key: string]: unknown; + }; + }; + }>; + /** Miscellaneous options for the client */ clientOptions?: { ssl?: { @@ -128,7 +155,7 @@ export interface Config { }; /* TODO(kuangp): unsupported until @elastic/elasticsearch@7.14 is released | { - + /** * Bearer authentication token to connect to the cluster. * See: https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-service-token.html diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 47abe5e3b2..63e6868ad6 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", "description": "A module for the search backend that implements search using ElasticSearch", - "version": "1.3.3-next.0", + "version": "1.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-elasticsearch/src/alpha.ts b/plugins/search-backend-module-elasticsearch/src/alpha.ts index 538124f004..9801cf4643 100644 --- a/plugins/search-backend-module-elasticsearch/src/alpha.ts +++ b/plugins/search-backend-module-elasticsearch/src/alpha.ts @@ -16,59 +16,66 @@ import { coreServices, createBackendModule, + createExtensionPoint, } from '@backstage/backend-plugin-api'; -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; - import { - ElasticSearchCustomIndexTemplate, ElasticSearchQueryTranslator, ElasticSearchSearchEngine, } from '@backstage/plugin-search-backend-module-elasticsearch'; -/** - * @alpha - * Options for {@link searchModuleElasticsearchEngine}. - */ -export type SearchModuleElasticsearchEngineOptions = { - translator?: ElasticSearchQueryTranslator; - indexTemplate?: ElasticSearchCustomIndexTemplate; -}; +/** @alpha */ +export interface ElasticSearchQueryTranslatorExtensionPoint { + setTranslator(translator: ElasticSearchQueryTranslator): void; +} /** + * Extension point used to customize the ElasticSearch query translator. + * * @alpha + */ +export const elasticsearchTranslatorExtensionPoint = + createExtensionPoint({ + id: 'search.elasticsearchEngine.translator', + }); + +/** * Search backend module for the Elasticsearch engine. + * + * @alpha */ -export const searchModuleElasticsearchEngine = createBackendModule( - (options?: SearchModuleElasticsearchEngineOptions) => ({ - moduleId: 'elasticsearchEngine', - pluginId: 'search', - register(env) { - env.registerInit({ - deps: { - searchEngineRegistry: searchEngineRegistryExtensionPoint, - logger: coreServices.logger, - config: coreServices.config, - }, - async init({ searchEngineRegistry, logger, config }) { - const searchEngine = await ElasticSearchSearchEngine.fromConfig({ - logger: loggerToWinstonLogger(logger), - config: config, - }); +export const searchModuleElasticsearchEngine = createBackendModule({ + moduleId: 'elasticsearchEngine', + pluginId: 'search', + register(env) { + let translator: ElasticSearchQueryTranslator | undefined; - // set custom translator if available - if (options?.translator) { - searchEngine.setTranslator(options.translator); - } + env.registerExtensionPoint(elasticsearchTranslatorExtensionPoint, { + setTranslator(newTranslator) { + if (translator) { + throw new Error( + 'ElasticSearch query translator may only be set once', + ); + } + translator = newTranslator; + }, + }); - // set custom index template if available - if (options?.indexTemplate) { - searchEngine.setIndexTemplate(options.indexTemplate); - } - - searchEngineRegistry.setSearchEngine(searchEngine); - }, - }); - }, - }), -); + env.registerInit({ + deps: { + searchEngineRegistry: searchEngineRegistryExtensionPoint, + logger: coreServices.logger, + config: coreServices.rootConfig, + }, + async init({ searchEngineRegistry, logger, config }) { + searchEngineRegistry.setSearchEngine( + await ElasticSearchSearchEngine.fromConfig({ + logger, + config, + translator, + }), + ); + }, + }); + }, +}); diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index 90f6f407d9..9b085b52b0 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -38,6 +38,7 @@ import { AwsCredentialProvider, DefaultAwsCredentialsManager, } from '@backstage/integration-aws-node'; +import { LoggerService } from '@backstage/backend-plugin-api'; export type { ElasticSearchClientOptions }; @@ -73,10 +74,11 @@ export type ElasticSearchQueryTranslator = ( * @public */ export type ElasticSearchOptions = { - logger: Logger; + logger: Logger | LoggerService; config: Config; aliasPostfix?: string; indexPrefix?: string; + translator?: ElasticSearchQueryTranslator; }; /** @@ -126,7 +128,7 @@ export class ElasticSearchSearchEngine implements SearchEngine { private readonly elasticSearchClientOptions: ElasticSearchClientOptions, private readonly aliasPostfix: string, private readonly indexPrefix: string, - private readonly logger: Logger, + private readonly logger: Logger | LoggerService, private readonly batchSize: number, highlightOptions?: ElasticSearchHighlightOptions, ) { @@ -149,6 +151,7 @@ export class ElasticSearchSearchEngine implements SearchEngine { config, aliasPostfix = `search`, indexPrefix = ``, + translator, } = options; const credentialProvider = DefaultAwsCredentialsManager.fromConfig(config); const clientOptions = await this.createElasticSearchClientOptions( @@ -165,7 +168,7 @@ export class ElasticSearchSearchEngine implements SearchEngine { logger.info('Initializing ElasticSearch search engine.'); } - return new ElasticSearchSearchEngine( + const engine = new ElasticSearchSearchEngine( clientOptions, aliasPostfix, indexPrefix, @@ -176,6 +179,18 @@ export class ElasticSearchSearchEngine implements SearchEngine { 'search.elasticsearch.highlightOptions', ), ); + + for (const indexTemplate of this.readIndexTemplateConfig( + config.getConfig('search.elasticsearch'), + )) { + await engine.setIndexTemplate(indexTemplate); + } + + if (translator) { + await engine.setTranslator(translator); + } + + return engine; } /** @@ -221,11 +236,10 @@ export class ElasticSearchSearchEngine implements SearchEngine { .boolQuery() .should(value.map(it => esb.matchQuery(key, it.toString()))); } - this.logger.error( - 'Failed to query, unrecognized filter type', + this.logger.error('Failed to query, unrecognized filter type', { key, value, - ); + }); throw new Error( 'Failed to add filters to query. Unrecognized filter type', ); @@ -518,6 +532,24 @@ export class ElasticSearchSearchEngine implements SearchEngine { : {}), }; } + + private static readIndexTemplateConfig( + config: Config, + ): ElasticSearchCustomIndexTemplate[] { + return ( + config.getOptionalConfigArray('indexTemplates')?.map(templateConfig => { + const bodyConfig = templateConfig.getConfig('body'); + return { + name: templateConfig.getString('name'), + body: { + index_patterns: bodyConfig.getStringArray('index_patterns'), + composed_of: bodyConfig.getOptionalStringArray('composed_of'), + template: bodyConfig.getOptionalConfig('template')?.get(), + }, + }; + }) ?? [] + ); + } } /** diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts index a9d15c421e..b742216d52 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts @@ -19,6 +19,7 @@ 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'; /** * Options for instantiate ElasticSearchSearchEngineIndexer @@ -29,7 +30,7 @@ export type ElasticSearchSearchEngineIndexerOptions = { indexPrefix: string; indexSeparator: string; alias: string; - logger: Logger; + logger: Logger | LoggerService; elasticSearchClientWrapper: ElasticSearchClientWrapper; batchSize: number; }; @@ -55,7 +56,7 @@ export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer { private readonly indexSeparator: string; private readonly alias: string; private readonly removableAlias: string; - private readonly logger: Logger; + private readonly logger: Logger | LoggerService; private readonly sourceStream: Readable; private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper; private configuredBatchSize: number; @@ -186,7 +187,9 @@ export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer { // If any indices are removable, remove them. Do not bubble up this error, // as doing so would delete the now aliased index. Log instead. if (this.removableIndices.length) { - this.logger.info('Removing stale search indices', this.removableIndices); + this.logger.info('Removing stale search indices', { + removableIndices: this.removableIndices, + }); try { await this.elasticSearchClientWrapper.deleteIndex({ index: this.removableIndices, diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index a7c34b950e..05c17f7c59 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,48 @@ # @backstage/plugin-search-backend-module-explore +## 0.1.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- 12a8c94eda8d: Add package repository and homepage metadata +- 6694e79ab396: Breaking change for the alpha export moved `schedule` from module options into app-config for the new backend system. You can now pass in a `TaskScheduleDefinitionConfig` through the `search.collators.explore.schedule` configuration key. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + +## 0.1.4-next.2 + +### Patch Changes + +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-explore-common@0.0.1 + - @backstage/plugin-search-common@1.2.5 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-explore/README.md b/plugins/search-backend-module-explore/README.md index e775cf42b6..cf2a01b9f0 100644 --- a/plugins/search-backend-module-explore/README.md +++ b/plugins/search-backend-module-explore/README.md @@ -2,14 +2,21 @@ > DISCLAIMER: The new backend system is in alpha, and so are the search backend module support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, you can find getting started guides below. -This package exports explore backend modules responsible for extending search. +This package exports a module that extends the search backend to also indexing the tools exposed by the [`explore` service](https://github.com/backstage/backstage/tree/master/plugins/explore-backend). -## Example +## Installation -### Use default schedule +Add the module package as a dependency: + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-search-backend-module-explore +``` + +Add the collator to your backend instance, along with the search plugin itself: ```tsx -// packages/backend-next/src/index.ts +// packages/backend/src/index.ts import { createBackend } from '@backstage/backend-defaults'; import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; import { searchModuleExploreCollator } from '@backstage/plugin-search-backend-module-explore/alpha'; @@ -20,29 +27,11 @@ backend.add(searchModuleExploreCollator()); backend.start(); ``` -### Use custom schedule +You may also want to add configuration parameters to your app-config, for example for controlling the scheduled indexing interval. These parameters should be placed under the `search.collators.explore` key. See [the config definition file](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-explore/config.d.ts) for more details. -```tsx -// packages/backend-next/src/index.ts -import { createBackend } from '@backstage/backend-defaults'; -import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; -import { searchModuleExploreCollator } from '@backstage/plugin-search-backend-module-explore/alpha'; +## Using Auth Middleware -const schedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, -}; - -const backend = createBackend(); -backend.add(searchPlugin()); -backend.add(searchModuleExploreCollator({ schedule })); -backend.start(); -``` - -### Using Auth Middleware - -If your Backstage instance uses service-to-service authentication you can pass an optional `tokenManager` to the collator factory. This will ensure that the collator makes authenticated requests to the explore backend. +If your Backstage instance uses service-to-service authentication, the collator will need to have access to a `tokenManager`. This is automatically injected by the collator module when using the new backend system. But if you are using the old backend system, you will want to add it manually to ensure that the collator makes authenticated requests to the explore backend. ```tsx indexBuilder.addCollator({ diff --git a/plugins/search-backend-module-explore/alpha-api-report.md b/plugins/search-backend-module-explore/alpha-api-report.md index cf32b59dd3..a01bc999fb 100644 --- a/plugins/search-backend-module-explore/alpha-api-report.md +++ b/plugins/search-backend-module-explore/alpha-api-report.md @@ -4,21 +4,9 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; -import { ToolDocumentCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-explore'; // @alpha -export const searchModuleExploreCollator: ( - options?: SearchModuleExploreCollatorOptions | undefined, -) => BackendFeature; - -// @alpha -export type SearchModuleExploreCollatorOptions = Omit< - ToolDocumentCollatorFactoryOptions, - 'logger' | 'discovery' -> & { - schedule?: TaskScheduleDefinition; -}; +export const searchModuleExploreCollator: () => BackendFeature; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/search-backend-module-explore/config.d.ts b/plugins/search-backend-module-explore/config.d.ts new file mode 100644 index 0000000000..73ff015f20 --- /dev/null +++ b/plugins/search-backend-module-explore/config.d.ts @@ -0,0 +1,33 @@ +/* + * 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 { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + +export interface Config { + search?: { + collators?: { + /** + * Configuration options for `@backstage/plugin-search-backend-module-explore` + */ + explore?: { + /** + * The schedule for how often to run the collation job. + */ + schedule?: TaskScheduleDefinitionConfig; + }; + }; + }; +} diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 46c11a2e62..8e4781b081 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-explore", "description": "A module for the search backend that exports explore modules", - "version": "0.1.4-next.0", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -58,6 +58,8 @@ "msw": "^1.2.1" }, "files": [ - "dist" - ] + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" } diff --git a/plugins/search-backend-module-explore/src/alpha.test.ts b/plugins/search-backend-module-explore/src/alpha.test.ts index 9b133f0fd5..4505f86c5e 100644 --- a/plugins/search-backend-module-explore/src/alpha.test.ts +++ b/plugins/search-backend-module-explore/src/alpha.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { startTestBackend } from '@backstage/backend-test-utils'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; import { searchModuleExploreCollator } from './alpha'; @@ -34,7 +34,18 @@ describe('searchModuleExploreCollator', () => { extensionPoints: [ [searchIndexRegistryExtensionPoint, extensionPointMock], ], - features: [searchModuleExploreCollator({ schedule })], + features: [ + searchModuleExploreCollator(), + mockServices.rootConfig.factory({ + data: { + search: { + explore: { + schedule, + }, + }, + }, + }), + ], }); expect(extensionPointMock.addCollator).toHaveBeenCalledTimes(1); diff --git a/plugins/search-backend-module-explore/src/alpha.ts b/plugins/search-backend-module-explore/src/alpha.ts index f13c3c2406..403699e2cb 100644 --- a/plugins/search-backend-module-explore/src/alpha.ts +++ b/plugins/search-backend-module-explore/src/alpha.ts @@ -23,62 +23,59 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; import { loggerToWinstonLogger } from '@backstage/backend-common'; import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; -import { - ToolDocumentCollatorFactory, - ToolDocumentCollatorFactoryOptions, -} from '@backstage/plugin-search-backend-module-explore'; +import { ToolDocumentCollatorFactory } from '@backstage/plugin-search-backend-module-explore'; +import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; /** - * @alpha - * Options for {@link searchModuleExploreCollator}. - */ -export type SearchModuleExploreCollatorOptions = Omit< - ToolDocumentCollatorFactoryOptions, - 'logger' | 'discovery' -> & { - schedule?: TaskScheduleDefinition; -}; - -/** - * @alpha * Search backend module for the Explore index. + * + * @alpha */ -export const searchModuleExploreCollator = createBackendModule( - (options?: SearchModuleExploreCollatorOptions) => ({ - moduleId: 'exploreCollator', - pluginId: 'search', - register(env) { - env.registerInit({ - deps: { - config: coreServices.config, - logger: coreServices.logger, - discovery: coreServices.discovery, - scheduler: coreServices.scheduler, - indexRegistry: searchIndexRegistryExtensionPoint, - }, - async init({ config, logger, discovery, scheduler, indexRegistry }) { - const defaultSchedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, - }; +export const searchModuleExploreCollator = createBackendModule({ + moduleId: 'exploreCollator', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.logger, + discovery: coreServices.discovery, + scheduler: coreServices.scheduler, + tokenManager: coreServices.tokenManager, + indexRegistry: searchIndexRegistryExtensionPoint, + }, + async init({ + config, + logger, + discovery, + scheduler, + indexRegistry, + tokenManager, + }) { + const defaultSchedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; - indexRegistry.addCollator({ - schedule: scheduler.createScheduledTaskRunner( - options?.schedule ?? defaultSchedule, - ), - factory: ToolDocumentCollatorFactory.fromConfig(config, { - ...options, - discovery, - logger: loggerToWinstonLogger(logger), - }), - }); - }, - }); - }, - }), -); + const schedule = config.has('search.collators.explore.schedule') + ? readTaskScheduleDefinitionFromConfig( + config.getConfig('search.collators.explore.schedule'), + ) + : defaultSchedule; + + indexRegistry.addCollator({ + schedule: scheduler.createScheduledTaskRunner(schedule), + factory: ToolDocumentCollatorFactory.fromConfig(config, { + discovery, + logger: loggerToWinstonLogger(logger), + tokenManager, + }), + }); + }, + }); + }, +}); diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 25f4090f11..d85c7f3e91 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.9 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## 0.5.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + +## 0.5.9-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + ## 0.5.9-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 2f37f90681..de77f80e66 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-pg", "description": "A module for the search backend that implements search using PostgreSQL", - "version": "0.5.9-next.0", + "version": "0.5.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-pg/src/alpha.ts b/plugins/search-backend-module-pg/src/alpha.ts index e3e0087957..71d62e7927 100644 --- a/plugins/search-backend-module-pg/src/alpha.ts +++ b/plugins/search-backend-module-pg/src/alpha.ts @@ -32,7 +32,7 @@ export const searchModulePostgresEngine = createBackendModule({ deps: { searchEngineRegistry: searchEngineRegistryExtensionPoint, database: coreServices.database, - config: coreServices.config, + config: coreServices.rootConfig, }, async init({ searchEngineRegistry, database, config }) { searchEngineRegistry.setSearchEngine( diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 6dad3cc182..1434db16c4 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.1.4 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- 12a8c94eda8d: Add package repository and homepage metadata +- 6694e79ab396: **BREAKING:** Moved `schedule` & `collators` settings from module options into app-config for the new backend system. You can now pass in a `TaskScheduleDefinitionConfig` through the `search.collators.techdocs.schedule` configuration key & configure the `TechDocsCollatorFactory` with the key `search.collators.techdocs`. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 0.1.4-next.2 + +### Patch Changes + +- 29f77f923c71: Ensure that all services are dependency injected into the module instead of taken from options +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/README.md b/plugins/search-backend-module-techdocs/README.md index 5de310466b..9ffdcb7f55 100644 --- a/plugins/search-backend-module-techdocs/README.md +++ b/plugins/search-backend-module-techdocs/README.md @@ -2,14 +2,21 @@ > DISCLAIMER: The new backend system is in alpha, and so are the search backend module support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, you can find getting started guides below. -This package exports techdocs backend modules responsible for extending search. +This package exports a module that extends the search backend to also index techdocs. -## Example +## Installation -### Use default schedule +Add the module package as a dependency: + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-search-backend-module-techdocs +``` + +Add the collator to your backend instance, along with the search plugin itself: ```tsx -// packages/backend-next/src/index.ts +// packages/backend/src/index.ts import { createBackend } from '@backstage/backend-defaults'; import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; import { searchModuleTechDocsCollator } from '@backstage/plugin-search-backend-module-techdocs/alpha'; @@ -20,22 +27,4 @@ backend.add(searchModuleTechDocsCollator()); backend.start(); ``` -### Use custom schedule - -```tsx -// packages/backend-next/src/index.ts -import { createBackend } from '@backstage/backend-defaults'; -import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; -import { searchModuleTechDocsCollator } from '@backstage/plugin-search-backend-module-techdocs/alpha'; - -const schedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, -}; - -const backend = createBackend(); -backend.add(searchPlugin()); -backend.add(searchModuleTechDocsCollator({ schedule })); -backend.start(); -``` +You may also want to add configuration parameters to your app-config, for example for controlling the scheduled indexing interval. These parameters should be placed under the `search.collators.techdocs` key. See [the config definition file](https://github.com/backstage/backstage/blob/master/plugins/search-backend-module-techdocs/config.d.ts) for more details. diff --git a/plugins/search-backend-module-techdocs/alpha-api-report.md b/plugins/search-backend-module-techdocs/alpha-api-report.md index 71ce09b744..6e36a0020a 100644 --- a/plugins/search-backend-module-techdocs/alpha-api-report.md +++ b/plugins/search-backend-module-techdocs/alpha-api-report.md @@ -4,21 +4,9 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; -import { TechDocsCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-techdocs'; // @alpha -export const searchModuleTechDocsCollator: ( - options?: SearchModuleTechDocsCollatorOptions | undefined, -) => BackendFeature; - -// @alpha -export type SearchModuleTechDocsCollatorOptions = Omit< - TechDocsCollatorFactoryOptions, - 'logger' | 'discovery' | 'tokenManager' -> & { - schedule?: TaskScheduleDefinition; -}; +export const searchModuleTechDocsCollator: () => BackendFeature; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/search-backend-module-techdocs/config.d.ts b/plugins/search-backend-module-techdocs/config.d.ts new file mode 100644 index 0000000000..630bfc3692 --- /dev/null +++ b/plugins/search-backend-module-techdocs/config.d.ts @@ -0,0 +1,53 @@ +/* + * 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 { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + +export interface Config { + search?: { + collators?: { + /** + * Configuration options for `@backstage/plugin-search-backend-module-techdocs` + */ + techdocs?: { + /** + * The schedule for how often to run the collation job. + */ + schedule?: TaskScheduleDefinitionConfig; + /** + * A templating string with placeholders, to form the final location of + * the entity. + * + * Defaults to `'/docs/:namespace/:kind/:name/:path'`. + */ + locationTemplate?: string; + /** + * An abstract value that controls the concurrency level of the + * collation process. Increasing this value will both increase the + * number of entities fetched at a time from the catalog, as well as how + * many things are being processed concurrently. + * + * Defaults to `10`. + */ + parallelismLimit?: number; + /** + * Defaults to `false`. + */ + legacyPathCasing?: boolean; + }; + }; + }; +} diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 5a5e933523..51e59626f3 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", "description": "A module for the search backend that exports techdocs modules", - "version": "0.1.4-next.0", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -49,6 +49,7 @@ "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^", @@ -64,6 +65,8 @@ "msw": "^1.0.0" }, "files": [ - "dist" - ] + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" } diff --git a/plugins/search-backend-module-techdocs/src/alpha.test.ts b/plugins/search-backend-module-techdocs/src/alpha.test.ts index 011b00b12c..d65a77a765 100644 --- a/plugins/search-backend-module-techdocs/src/alpha.test.ts +++ b/plugins/search-backend-module-techdocs/src/alpha.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { startTestBackend } from '@backstage/backend-test-utils'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; import { searchModuleTechDocsCollator } from './alpha'; @@ -34,7 +34,18 @@ describe('searchModuleTechDocsCollator', () => { extensionPoints: [ [searchIndexRegistryExtensionPoint, extensionPointMock], ], - features: [searchModuleTechDocsCollator({ schedule })], + features: [ + searchModuleTechDocsCollator(), + mockServices.rootConfig.factory({ + data: { + search: { + techdocs: { + schedule, + }, + }, + }, + }), + ], }); expect(extensionPointMock.addCollator).toHaveBeenCalledTimes(1); diff --git a/plugins/search-backend-module-techdocs/src/alpha.ts b/plugins/search-backend-module-techdocs/src/alpha.ts index 1432cc88fb..bb084ddfd9 100644 --- a/plugins/search-backend-module-techdocs/src/alpha.ts +++ b/plugins/search-backend-module-techdocs/src/alpha.ts @@ -19,75 +19,65 @@ * A module for the search backend that exports TechDocs modules. */ +import { loggerToWinstonLogger } from '@backstage/backend-common'; import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; -import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; +import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs'; import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; -import { - DefaultTechDocsCollatorFactory, - TechDocsCollatorFactoryOptions, -} from '@backstage/plugin-search-backend-module-techdocs'; - -/** - * @alpha - * Options for {@link searchModuleTechDocsCollator}. - */ -export type SearchModuleTechDocsCollatorOptions = Omit< - TechDocsCollatorFactoryOptions, - 'logger' | 'discovery' | 'tokenManager' -> & { - schedule?: TaskScheduleDefinition; -}; - /** * @alpha * Search backend module for the TechDocs index. */ -export const searchModuleTechDocsCollator = createBackendModule( - (options?: SearchModuleTechDocsCollatorOptions) => ({ - moduleId: 'techDocsCollator', - pluginId: 'search', - register(env) { - env.registerInit({ - deps: { - config: coreServices.config, - logger: coreServices.logger, - discovery: coreServices.discovery, - tokenManager: coreServices.tokenManager, - scheduler: coreServices.scheduler, - indexRegistry: searchIndexRegistryExtensionPoint, - }, - async init({ - config, - logger, - discovery, - tokenManager, - scheduler, - indexRegistry, - }) { - const defaultSchedule = { - frequency: { minutes: 10 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 3 }, - }; +export const searchModuleTechDocsCollator = createBackendModule({ + moduleId: 'techDocsCollator', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.logger, + discovery: coreServices.discovery, + tokenManager: coreServices.tokenManager, + scheduler: coreServices.scheduler, + catalog: catalogServiceRef, + indexRegistry: searchIndexRegistryExtensionPoint, + }, + async init({ + config, + logger, + discovery, + tokenManager, + scheduler, + catalog, + indexRegistry, + }) { + const defaultSchedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; - indexRegistry.addCollator({ - schedule: scheduler.createScheduledTaskRunner( - options?.schedule ?? defaultSchedule, - ), - factory: DefaultTechDocsCollatorFactory.fromConfig(config, { - ...options, - discovery, - tokenManager, - logger: loggerToWinstonLogger(logger), - }), - }); - }, - }); - }, - }), -); + const schedule = config.has('search.collators.techdocs.schedule') + ? readTaskScheduleDefinitionFromConfig( + config.getConfig('search.collators.techdocs.schedule'), + ) + : defaultSchedule; + + indexRegistry.addCollator({ + schedule: scheduler.createScheduledTaskRunner(schedule), + factory: DefaultTechDocsCollatorFactory.fromConfig(config, { + discovery, + tokenManager, + logger: loggerToWinstonLogger(logger), + catalogClient: catalog, + }), + }); + }, + }); + }, +}); 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 d9329151c6..319b8fb446 100644 --- a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts +++ b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts @@ -174,10 +174,15 @@ describe('DefaultTechDocsCollatorFactory', () => { it('maps a returned entity with a custom locationTemplate', async () => { // Provide an alternate location template. - factory = DefaultTechDocsCollatorFactory.fromConfig(config, { + const _config = new ConfigReader({ + ...config.get(), + search: { + collators: { techdocs: { locationTemplate: '/software/:name' } }, + }, + }); + factory = DefaultTechDocsCollatorFactory.fromConfig(_config, { discovery: mockDiscoveryApi, tokenManager: mockTokenManager, - locationTemplate: '/software/:name', logger, }); collator = await factory.getCollator(); @@ -194,10 +199,17 @@ describe('DefaultTechDocsCollatorFactory', () => { // A parallelismLimit of 1 is a catalog limit of 50 per request. Code // above in the /entities handler ensures valid entities are only // returned on the second page. - factory = DefaultTechDocsCollatorFactory.fromConfig(config, { - ...options, - parallelismLimit: 1, + const _config = new ConfigReader({ + ...config.get(), + search: { + collators: { + techdocs: { + parallelismLimit: 1, + }, + }, + }, }); + factory = DefaultTechDocsCollatorFactory.fromConfig(_config, options); collator = await factory.getCollator(); const pipeline = TestPipeline.fromCollator(collator); @@ -205,6 +217,9 @@ describe('DefaultTechDocsCollatorFactory', () => { // Only 1 entity with TechDocs configured multiplied by 3 pages. expect(documents).toHaveLength(3); + expect(_config.get('search.collators.techdocs.parallelismLimit')).toEqual( + 1, + ); }); describe('with legacyPathCasing configuration', () => { diff --git a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts index e2c6f5913c..051dc93189 100644 --- a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts +++ b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts @@ -104,7 +104,18 @@ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { config.getOptionalBoolean( 'techdocs.legacyUseCaseSensitiveTripletPaths', ) || false; - return new DefaultTechDocsCollatorFactory({ ...options, legacyPathCasing }); + const locationTemplate = config.getOptionalString( + 'search.collators.techdocs.locationTemplate', + ); + const parallelismLimit = config.getOptionalNumber( + 'search.collators.techdocs.parallelismLimit', + ); + return new DefaultTechDocsCollatorFactory({ + ...options, + locationTemplate, + parallelismLimit, + legacyPathCasing, + }); } async getCollator(): Promise { diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index ad3d44f026..9d82992c80 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-search-backend-node +## 1.2.4 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- e3e9bc10298b: Fix Lunr search engine highlight by ignoring invalid metadata positions. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 1.2.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 1.2.4-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + ## 1.2.4-next.0 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index b799d07d62..19faf1f0b4 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-node", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", - "version": "1.2.4-next.0", + "version": "1.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 26bf5db1f2..8538ffc08f 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,60 @@ # @backstage/plugin-search-backend +## 1.4.0 + +### Minor Changes + +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 951ab6c9db58: Add missing `configSchema` to package.json +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-openapi-utils@0.0.3 + - @backstage/plugin-search-backend-node@1.2.4 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/plugin-permission-node@0.7.11 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 1.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-permission-node@0.7.11-next.2 + - @backstage/plugin-search-backend-node@1.2.4-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 1.4.0-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- 12a8c94eda8d: Add package repository and homepage metadata +- 951ab6c9db58: Add missing `configSchema` to package.json +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-openapi-utils@0.0.3-next.1 + - @backstage/plugin-search-backend-node@1.2.4-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-permission-node@0.7.11-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + ## 1.4.0-next.0 ### Minor Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 5342b412f9..ba36bea2a9 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend", "description": "The Backstage backend plugin that provides your backstage app with search", - "version": "1.4.0-next.0", + "version": "1.4.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend/src/alpha.ts b/plugins/search-backend/src/alpha.ts index 0c14852875..59394ab7a6 100644 --- a/plugins/search-backend/src/alpha.ts +++ b/plugins/search-backend/src/alpha.ts @@ -93,7 +93,7 @@ export const searchPlugin = createBackendPlugin({ env.registerInit({ deps: { logger: coreServices.logger, - config: coreServices.config, + config: coreServices.rootConfig, permissions: coreServices.permissions, http: coreServices.httpRouter, searchIndexService: searchIndexServiceRef, diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index 136a7ad4a4..e926722cbb 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-react +## 1.6.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + - @backstage/plugin-search-common@1.2.5 + ## 1.6.4-next.0 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 787d24bc2a..550f7d5969 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.6.4-next.0", + "version": "1.6.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index cbc0187d23..1e6c8d45de 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-search +## 1.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/version-bridge@1.0.4 + - @backstage/plugin-search-common@1.2.5 + +## 1.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 1.3.4-next.0 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 7a8d7dffab..3bca411e3e 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search", "description": "The Backstage plugin that provides your backstage app with search", - "version": "1.3.4-next.0", + "version": "1.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index f4bac35016..4fed635b84 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-sentry +## 0.5.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.5.6-next.0 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index c0bc4c1246..72e5bec26c 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sentry", "description": "A Backstage plugin that integrates towards Sentry", - "version": "0.5.6-next.0", + "version": "0.5.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/shortcuts/CHANGELOG.md b/plugins/shortcuts/CHANGELOG.md index 1082d7d53e..d26eddc2d3 100644 --- a/plugins/shortcuts/CHANGELOG.md +++ b/plugins/shortcuts/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-shortcuts +## 0.3.13 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- ee52a48b63d5: Limit the use of the duplicate shortcut name when adding a shortcut +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## 0.3.13-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + ## 0.3.13-next.0 ### Patch Changes diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 1c1699c67c..e08dfc089a 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-shortcuts", "description": "A Backstage plugin that provides a shortcuts feature to the sidebar", - "version": "0.3.13-next.0", + "version": "0.3.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/shortcuts/src/AddShortcut.test.tsx b/plugins/shortcuts/src/AddShortcut.test.tsx index f8786e2b24..35f37bc265 100644 --- a/plugins/shortcuts/src/AddShortcut.test.tsx +++ b/plugins/shortcuts/src/AddShortcut.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { AddShortcut } from './AddShortcut'; -import { DefaultShortcutsApi } from './api'; +import { DefaultShortcutsApi, shortcutsApiRef } from './api'; import { MockAnalyticsApi, MockStorageApi, @@ -42,13 +42,29 @@ describe('AddShortcut', () => { }); it('displays the title', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); expect(screen.getByText('Add Shortcut')).toBeInTheDocument(); }); it('closes the popup', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); fireEvent.click(screen.getByText('Cancel')); expect(props.onClose).toHaveBeenCalledTimes(1); @@ -57,7 +73,15 @@ describe('AddShortcut', () => { it('saves the input', async () => { const spy = jest.spyOn(api, 'add'); - await renderInTestApp(); + await renderInTestApp( + + + , + ); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -78,7 +102,12 @@ describe('AddShortcut', () => { const spy = jest.spyOn(api, 'add'); await renderInTestApp( - + , ); @@ -106,9 +135,18 @@ describe('AddShortcut', () => { it('pastes the values', async () => { const spy = jest.spyOn(api, 'add'); - await renderInTestApp(, { - routeEntries: ['/some-initial-url'], - }); + await renderInTestApp( + + , + , + { + routeEntries: ['/some-initial-url'], + }, + ); fireEvent.click(screen.getByText('Use current page')); fireEvent.click(screen.getByText('Save')); @@ -125,8 +163,14 @@ describe('AddShortcut', () => { await renderInTestApp( <> - - + + + + , ); diff --git a/plugins/shortcuts/src/EditShortcut.test.tsx b/plugins/shortcuts/src/EditShortcut.test.tsx index 72a89c13b6..3077168193 100644 --- a/plugins/shortcuts/src/EditShortcut.test.tsx +++ b/plugins/shortcuts/src/EditShortcut.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { EditShortcut } from './EditShortcut'; import { Shortcut } from './types'; -import { DefaultShortcutsApi } from './api'; +import { DefaultShortcutsApi, shortcutsApiRef } from './api'; import { MockAnalyticsApi, MockStorageApi, @@ -48,13 +48,29 @@ describe('EditShortcut', () => { }); it('displays the title', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); expect(screen.getByText('Edit Shortcut')).toBeInTheDocument(); }); it('closes the popup', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); fireEvent.click(screen.getByText('Cancel')); expect(props.onClose).toHaveBeenCalledTimes(1); @@ -63,7 +79,15 @@ describe('EditShortcut', () => { it('updates the shortcut', async () => { const spy = jest.spyOn(api, 'update'); - await renderInTestApp(); + await renderInTestApp( + + + , + ); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -86,7 +110,12 @@ describe('EditShortcut', () => { const spy = jest.spyOn(api, 'update'); await renderInTestApp( - + , ); @@ -115,7 +144,15 @@ describe('EditShortcut', () => { it('removes the shortcut', async () => { const spy = jest.spyOn(api, 'remove'); - await renderInTestApp(); + await renderInTestApp( + + + , + ); fireEvent.click(screen.getByText('Remove')); expect(spy).toHaveBeenCalledWith('id'); @@ -132,8 +169,14 @@ describe('EditShortcut', () => { await renderInTestApp( <> - - + + + + , ); diff --git a/plugins/shortcuts/src/ShortcutForm.test.tsx b/plugins/shortcuts/src/ShortcutForm.test.tsx index 3a52356b5d..388c072725 100644 --- a/plugins/shortcuts/src/ShortcutForm.test.tsx +++ b/plugins/shortcuts/src/ShortcutForm.test.tsx @@ -16,7 +16,12 @@ import React from 'react'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { ShortcutForm } from './ShortcutForm'; -import { renderInTestApp } from '@backstage/test-utils'; +import { DefaultShortcutsApi, shortcutsApiRef } from './api'; +import { + renderInTestApp, + TestApiProvider, + MockStorageApi, +} from '@backstage/test-utils'; describe('ShortcutForm', () => { const props = { @@ -25,7 +30,15 @@ describe('ShortcutForm', () => { }; it('displays validation messages', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -43,8 +56,47 @@ describe('ShortcutForm', () => { }); }); + it('displays duplicate validation messages for title and URL', async () => { + const mockShortcutApi = new DefaultShortcutsApi(MockStorageApi.create()); + mockShortcutApi.add({ title: 'Existing Title', url: '/existing-url' }); + + await renderInTestApp( + + + , + ); + + const urlInput = screen.getByPlaceholderText('Enter a URL'); + const titleInput = screen.getByPlaceholderText('Enter a display name'); + + fireEvent.change(urlInput, { target: { value: '/existing-url' } }); + fireEvent.change(titleInput, { target: { value: 'Existing Title' } }); + + fireEvent.click(screen.getByText('Save')); + + await waitFor(() => { + expect( + screen.getByText('A shortcut with this title already exists'), + ).toBeInTheDocument(); + expect( + screen.getByText('A shortcut with this url already exists'), + ).toBeInTheDocument(); + }); + + expect(props.onSave).not.toHaveBeenCalled(); + expect(props.onClose).not.toHaveBeenCalled(); + }); + it('allows external links', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -66,7 +118,15 @@ describe('ShortcutForm', () => { }); it('allows relative links when external links are enabled', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); const urlInput = screen.getByPlaceholderText('Enter a URL'); const titleInput = screen.getByPlaceholderText('Enter a display name'); @@ -89,10 +149,17 @@ describe('ShortcutForm', () => { it('calls the save handler', async () => { await renderInTestApp( - , + + + , + , ); fireEvent.click(screen.getByText('Save')); @@ -105,7 +172,15 @@ describe('ShortcutForm', () => { }); it('calls the close handler', async () => { - await renderInTestApp(); + await renderInTestApp( + + + , + ); fireEvent.click(screen.getByText('Cancel')); await waitFor(() => { diff --git a/plugins/shortcuts/src/ShortcutForm.tsx b/plugins/shortcuts/src/ShortcutForm.tsx index 99aa0d29b7..e8cd8144d0 100644 --- a/plugins/shortcuts/src/ShortcutForm.tsx +++ b/plugins/shortcuts/src/ShortcutForm.tsx @@ -15,6 +15,7 @@ */ import React, { useEffect } from 'react'; +import useObservable from 'react-use/lib/useObservable'; import { useForm, SubmitHandler, Controller } from 'react-hook-form'; import { Button, @@ -24,6 +25,8 @@ import { TextField, } from '@material-ui/core'; import { FormValues } from './types'; +import { shortcutsApiRef } from './api'; +import { useApi } from '@backstage/core-plugin-api'; const useStyles = makeStyles(theme => ({ field: { @@ -50,6 +53,11 @@ export const ShortcutForm = ({ allowExternalLinks, }: Props) => { const classes = useStyles(); + const shortcutApi = useApi(shortcutsApiRef); + const shortcutData = useObservable( + shortcutApi.shortcut$(), + shortcutApi.get(), + ); const { handleSubmit, reset, @@ -63,6 +71,18 @@ export const ShortcutForm = ({ }, }); + const titleIsUnique = (title: string) => { + if (shortcutData.some(shortcutTitle => shortcutTitle.title === title)) + return 'A shortcut with this title already exists'; + return true; + }; + + const urlIsUnique = (url: string) => { + if (shortcutData.some(shortcutUrl => shortcutUrl.url === url)) + return 'A shortcut with this url already exists'; + return true; + }; + useEffect(() => { reset(formValues); }, [reset, formValues]); @@ -75,6 +95,7 @@ export const ShortcutForm = ({ control={control} rules={{ required: true, + validate: urlIsUnique, ...(allowExternalLinks ? { pattern: { @@ -112,6 +133,7 @@ export const ShortcutForm = ({ control={control} rules={{ required: true, + validate: titleIsUnique, minLength: { value: 2, message: 'Must be at least 2 characters', diff --git a/plugins/sonarqube-backend/CHANGELOG.md b/plugins/sonarqube-backend/CHANGELOG.md index 23010b6a79..509e7ca551 100644 --- a/plugins/sonarqube-backend/CHANGELOG.md +++ b/plugins/sonarqube-backend/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-sonarqube-backend +## 0.2.2 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- a24d9df8757c: Added optional `externalBaseUrl` config for setting a different frontend URL +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.2-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/sonarqube-backend/README.md b/plugins/sonarqube-backend/README.md index 4d16dce025..b417fea7c4 100644 --- a/plugins/sonarqube-backend/README.md +++ b/plugins/sonarqube-backend/README.md @@ -138,6 +138,34 @@ sonarqube: apiKey: abcdef0123456789abcedf0123456789ab ``` +#### Example - Different frontend and backend URLs + +In some instances, you might want to use one URL for the backend and another for the frontend. +This can be achieved by using the optional `externalBaseUrl` property in the config. + +##### Single instance config + +```yaml +sonarqube: + baseUrl: https://sonarqube-internal.example.com + externalBaseUrl: https://sonarqube.example.com + apiKey: 123456789abcdef0123456789abcedf012 +``` + +##### Multiple instance config + +```yaml +sonarqube: + instances: + - name: default + baseUrl: https://default-sonarqube-internal.example.com + externalBaseUrl: https://default-sonarqube.example.com + apiKey: 123456789abcdef0123456789abcedf012 + - name: specialProject + baseUrl: https://special-project-sonarqube.example.com + apiKey: abcdef0123456789abcedf0123456789ab +``` + ## Links - [Sonarqube Frontend](../sonarqube/README.md) diff --git a/plugins/sonarqube-backend/api-report.md b/plugins/sonarqube-backend/api-report.md index 0fbd463cf3..535db37185 100644 --- a/plugins/sonarqube-backend/api-report.md +++ b/plugins/sonarqube-backend/api-report.md @@ -15,6 +15,7 @@ export class DefaultSonarqubeInfoProvider implements SonarqubeInfoProvider { static fromConfig(config: Config): DefaultSonarqubeInfoProvider; getBaseUrl(options?: { instanceName?: string }): { baseUrl: string; + externalBaseUrl?: string; }; getFindings(options: { componentKey: string; @@ -49,6 +50,7 @@ export interface SonarqubeFindings { export interface SonarqubeInfoProvider { getBaseUrl(options?: { instanceName?: string }): { baseUrl: string; + externalBaseUrl?: string; }; getFindings(options: { componentKey: string; @@ -60,6 +62,7 @@ export interface SonarqubeInfoProvider { export interface SonarqubeInstanceConfig { apiKey: string; baseUrl: string; + externalBaseUrl?: string; name: string; } diff --git a/plugins/sonarqube-backend/config.d.ts b/plugins/sonarqube-backend/config.d.ts index 7e93f61eb4..6e03a53f0c 100644 --- a/plugins/sonarqube-backend/config.d.ts +++ b/plugins/sonarqube-backend/config.d.ts @@ -23,6 +23,13 @@ export interface Config { */ baseUrl?: string; + /** + * The external url of the sonarqube installation. + * Use this if you want to use a different url for the frontend than the backend. + * @visibility frontend + */ + externalBaseUrl?: string; + /** * The api key to access the sonarqube instance under baseUrl. * @visibility secret @@ -46,6 +53,13 @@ export interface Config { */ baseUrl: string; + /** + * The external url of the sonarqube instance. + * Use this if you want to use a different url for the frontend than the backend. + * @visibility frontend + */ + externalBaseUrl?: string; + /** * The api key to access the sonarqube instance. * @visibility secret diff --git a/plugins/sonarqube-backend/package.json b/plugins/sonarqube-backend/package.json index 41aec6ecc9..c2d8375460 100644 --- a/plugins/sonarqube-backend/package.json +++ b/plugins/sonarqube-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-backend", - "version": "0.2.2-next.0", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-backend/src/service/router.test.ts b/plugins/sonarqube-backend/src/service/router.test.ts index 7045cdf0d2..b40e2d1528 100644 --- a/plugins/sonarqube-backend/src/service/router.test.ts +++ b/plugins/sonarqube-backend/src/service/router.test.ts @@ -24,7 +24,7 @@ import { SonarqubeFindings } from './sonarqubeInfoProvider'; describe('createRouter', () => { let app: express.Express; const getBaseUrlMock: jest.Mock< - { baseUrl: string }, + { baseUrl: string; externalBaseUrl?: string }, [{ instanceName: string }] > = jest.fn(); const getFindingsMock: jest.Mock< @@ -55,6 +55,7 @@ describe('createRouter', () => { describe('GET /findings', () => { const DUMMY_COMPONENT_KEY = 'my:component'; const DUMMY_INSTANCE_KEY = 'myInstance'; + it('returns ok', async () => { const measures = { analysisDate: '2022-01-01T00:00:00Z', @@ -77,6 +78,7 @@ describe('createRouter', () => { expect(response.status).toEqual(200); expect(response.body).toEqual(measures); }); + it('returns an error when component key is not defined', async () => { const response = await request(app) .get('/findings') @@ -112,9 +114,12 @@ describe('createRouter', () => { expect(response.body).toEqual(measures); }); }); + describe('GET /instanceUrl', () => { const DUMMY_INSTANCE_KEY = 'myInstance'; - const DUMMY_INSTANCE_URL = 'http://sonarqube.example.com'; + const DUMMY_INSTANCE_URL = 'http://sonarqube-internal.example.com'; + const DUMMY_INSTANCE_EXTERNAL_URL = 'http://sonarqube.example.com'; + it('returns ok', async () => { getBaseUrlMock.mockReturnValue({ baseUrl: DUMMY_INSTANCE_URL }); const response = await request(app) @@ -141,5 +146,17 @@ describe('createRouter', () => { expect(response.status).toEqual(200); expect(response.body).toEqual({ instanceUrl: DUMMY_INSTANCE_URL }); }); + + it('returns the external base url when provided', async () => { + getBaseUrlMock.mockReturnValue({ + baseUrl: DUMMY_INSTANCE_URL, + externalBaseUrl: DUMMY_INSTANCE_EXTERNAL_URL, + }); + const response = await request(app).get('/instanceUrl').send(); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + instanceUrl: DUMMY_INSTANCE_EXTERNAL_URL, + }); + }); }); }); diff --git a/plugins/sonarqube-backend/src/service/router.ts b/plugins/sonarqube-backend/src/service/router.ts index f244ae5ce3..5a993b5d59 100644 --- a/plugins/sonarqube-backend/src/service/router.ts +++ b/plugins/sonarqube-backend/src/service/router.ts @@ -81,11 +81,11 @@ export async function createRouter( ? `Retrieving sonarqube instance URL for key ${instanceKey}` : `Retrieving default sonarqube instance URL as instanceKey is not provided`, ); - const { baseUrl } = sonarqubeInfoProvider.getBaseUrl({ + const { baseUrl, externalBaseUrl } = sonarqubeInfoProvider.getBaseUrl({ instanceName: instanceKey, }); response.json({ - instanceUrl: baseUrl, + instanceUrl: externalBaseUrl || baseUrl, }); }); diff --git a/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts b/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts index 72ab2ecee2..468020c1b3 100644 --- a/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts +++ b/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts @@ -27,6 +27,7 @@ describe('SonarqubeConfig', () => { const SONARQUBE_DEFAULT_INSTANCE_NAME = 'default'; const DUMMY_SONAR_URL = 'https://sonarqube.example.com'; const DUMMY_SONAR_APIKEY = '123456789abcdef0123456789abcedf012'; + const DUMMY_SIMPLE_OBJECT_FOR_DEFAULT_SONARQUBE_CONFIG = { name: SONARQUBE_DEFAULT_INSTANCE_NAME, baseUrl: DUMMY_SONAR_URL, @@ -112,6 +113,7 @@ describe('SonarqubeConfig', () => { }, ]); }); + it('Throw an error if both a named default config and top level config', async () => { expect(() => SonarqubeConfig.fromConfig( @@ -299,6 +301,46 @@ describe('DefaultSonarqubeInfoProvider', () => { baseUrl: 'https://sonarqube-other.example.com', }); }); + + it('Provide external base url for simple config', async () => { + const provider = configureProvider({ + sonarqube: { + baseUrl: 'https://sonarqube-internal.example.com', + externalBaseUrl: 'https://sonarqube.example.com', + apiKey: '123456789abcdef0123456789abcedf012', + }, + }); + + expect(provider.getBaseUrl()).toEqual({ + baseUrl: 'https://sonarqube-internal.example.com', + externalBaseUrl: 'https://sonarqube.example.com', + }); + }); + + it('Provide external base url for named config', async () => { + const provider = configureProvider({ + sonarqube: { + instances: [ + { + name: 'default', + baseUrl: 'https://sonarqube.example.com', + apiKey: '123456789abcdef0123456789abcedf012', + }, + { + name: 'other', + baseUrl: 'https://sonarqube-other-internal.example.com', + externalBaseUrl: 'https://sonarqube-other.example.com', + apiKey: '123456789abcdef0123456789abcedf012', + }, + ], + }, + }); + + expect(provider.getBaseUrl({ instanceName: 'other' })).toEqual({ + baseUrl: 'https://sonarqube-other-internal.example.com', + externalBaseUrl: 'https://sonarqube-other.example.com', + }); + }); }); describe('getFindings', () => { @@ -385,6 +427,7 @@ describe('DefaultSonarqubeInfoProvider', () => { apiKey: DUMMY_API_KEY, }, }; + it('Provide findings when everything is ok', async () => { setupHandlers(); const provider = configureProvider(DUMMY_SIMPLE_CONFIG_FOR_PROVIDER); diff --git a/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts b/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts index 2b96e6764e..a5c0bf985a 100644 --- a/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts +++ b/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts @@ -30,7 +30,10 @@ export interface SonarqubeInfoProvider { * @param instanceName - Name of the sonarqube instance to get the info from * @returns the url of the instance */ - getBaseUrl(options?: { instanceName?: string }): { baseUrl: string }; + getBaseUrl(options?: { instanceName?: string }): { + baseUrl: string; + externalBaseUrl?: string; + }; /** * Query the sonarqube instance corresponding to the instanceName to get all @@ -96,6 +99,10 @@ export interface SonarqubeInstanceConfig { * Base url to access the instance */ baseUrl: string; + /** + * External url to access the instance from the frontend + */ + externalBaseUrl?: string; /** * Access token to access the sonarqube instance as generated in user profile. */ @@ -132,6 +139,7 @@ export class SonarqubeConfig { sonarqubeConfig.getOptionalConfigArray('instances')?.map(c => ({ name: c.getString('name'), baseUrl: c.getString('baseUrl'), + externalBaseUrl: c.getOptionalString('externalBaseUrl'), apiKey: c.getString('apiKey'), })) || []; @@ -142,9 +150,11 @@ export class SonarqubeConfig { // Get these as optional strings and check to give a better error message const baseUrl = sonarqubeConfig.getOptionalString('baseUrl'); + const externalBaseUrl = + sonarqubeConfig.getOptionalString('externalBaseUrl'); const apiKey = sonarqubeConfig.getOptionalString('apiKey'); - if (hasNamedDefault && (baseUrl || apiKey)) { + if (hasNamedDefault && (baseUrl || externalBaseUrl || apiKey)) { throw new Error( `Found both a named sonarqube instance with name ${DEFAULT_SONARQUBE_NAME} and top level baseUrl or apiKey config. Use only one style of config.`, ); @@ -160,10 +170,11 @@ export class SonarqubeConfig { if (unnamedAllPresent) { const unnamedInstanceConfig = [ - { name: DEFAULT_SONARQUBE_NAME, baseUrl, apiKey }, + { name: DEFAULT_SONARQUBE_NAME, baseUrl, externalBaseUrl, apiKey }, ] as { name: string; baseUrl: string; + externalBaseUrl?: string; apiKey: string; }[]; @@ -303,11 +314,15 @@ export class DefaultSonarqubeInfoProvider implements SonarqubeInfoProvider { */ getBaseUrl(options: { instanceName?: string } = {}): { baseUrl: string; + externalBaseUrl?: string; } { const instanceConfig = this.config.getInstanceConfig({ sonarqubeName: options.instanceName, }); - return { baseUrl: instanceConfig.baseUrl }; + return { + baseUrl: instanceConfig.baseUrl, + externalBaseUrl: instanceConfig.externalBaseUrl, + }; } /** diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 51128ed9f5..adda9640a9 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-sonarqube +## 0.7.2 + +### Patch Changes + +- b2ccddefbdc6: Remove sonarQube card disable class +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-sonarqube-react@0.1.7 + +## 0.7.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.7.2-next.1 + +### Patch Changes + +- b2ccddefbdc6: Remove sonarQube card disable class +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-sonarqube-react@0.1.7 + ## 0.7.2-next.0 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index dda7ad59ea..b03f09581a 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sonarqube", "description": "", - "version": "0.7.2-next.0", + "version": "0.7.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 3d6ec3088f..24931ad86b 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-splunk-on-call +## 0.4.10 + +### Patch Changes + +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/theme@0.4.1 + +## 0.4.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.4.10-next.0 ### Patch Changes diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index fb39c0da1a..7f7429e4c1 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-splunk-on-call", "description": "A Backstage plugin that integrates towards Splunk On-Call", - "version": "0.4.10-next.0", + "version": "0.4.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow-backend/CHANGELOG.md b/plugins/stack-overflow-backend/CHANGELOG.md index 3e3716580e..9e1bcb63c2 100644 --- a/plugins/stack-overflow-backend/CHANGELOG.md +++ b/plugins/stack-overflow-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-stack-overflow-backend +## 0.2.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + +## 0.2.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.5 + ## 0.2.4-next.0 ### Patch Changes diff --git a/plugins/stack-overflow-backend/package.json b/plugins/stack-overflow-backend/package.json index 3ad42aa525..513e52e4cb 100644 --- a/plugins/stack-overflow-backend/package.json +++ b/plugins/stack-overflow-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-stack-overflow-backend", - "version": "0.2.4-next.0", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow/CHANGELOG.md b/plugins/stack-overflow/CHANGELOG.md index 0a45b1eb07..ddda86777e 100644 --- a/plugins/stack-overflow/CHANGELOG.md +++ b/plugins/stack-overflow/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-stack-overflow +## 0.1.19 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/plugin-home-react@0.1.2 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/config@1.0.8 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.5 + +## 0.1.19-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-home-react@0.1.2-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index 030255ec62..61df0a4ace 100644 --- a/plugins/stack-overflow/package.json +++ b/plugins/stack-overflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-stack-overflow", - "version": "0.1.19-next.0", + "version": "0.1.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stackstorm/CHANGELOG.md b/plugins/stackstorm/CHANGELOG.md index 52e6615586..ac690b8086 100644 --- a/plugins/stackstorm/CHANGELOG.md +++ b/plugins/stackstorm/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-stackstorm +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/stackstorm/package.json b/plugins/stackstorm/package.json index 800fbc5ffe..9453fc70c6 100644 --- a/plugins/stackstorm/package.json +++ b/plugins/stackstorm/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stackstorm", "description": "A Backstage plugin that integrates towards StackStorm", - "version": "0.1.5-next.0", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md index 4b07de0d02..ec6986d399 100644 --- a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md +++ b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-tech-insights-backend-module-jsonfc +## 0.1.32 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-tech-insights-node@0.4.6 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-tech-insights-common@0.2.11 + +## 0.1.32-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-tech-insights-node@0.4.6-next.2 + +## 0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-tech-insights-node@0.4.6-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-tech-insights-common@0.2.11 + ## 0.1.32-next.0 ### Patch Changes diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 3784d3d1f5..046517b75e 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend-module-jsonfc", - "version": "0.1.32-next.0", + "version": "0.1.32", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md index 4d2b8b5630..dd5f5ce33b 100644 --- a/plugins/tech-insights-backend/CHANGELOG.md +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-tech-insights-backend +## 0.5.14 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-tasks@0.5.5 + - @backstage/plugin-tech-insights-node@0.4.6 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## 0.5.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-tech-insights-node@0.4.6-next.2 + +## 0.5.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/plugin-tech-insights-node@0.4.6-next.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + ## 0.5.14-next.0 ### Patch Changes diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 944d1f2cc5..c3654b7967 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend", - "version": "0.5.14-next.0", + "version": "0.5.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-node/CHANGELOG.md b/plugins/tech-insights-node/CHANGELOG.md index 8eb1287bd5..8e42f4113d 100644 --- a/plugins/tech-insights-node/CHANGELOG.md +++ b/plugins/tech-insights-node/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-tech-insights-node +## 0.4.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## 0.4.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + ## 0.4.6-next.0 ### Patch Changes diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index 87d8ebb6ee..42b2b83f92 100644 --- a/plugins/tech-insights-node/package.json +++ b/plugins/tech-insights-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-node", - "version": "0.4.6-next.0", + "version": "0.4.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights/CHANGELOG.md b/plugins/tech-insights/CHANGELOG.md index 079e53c767..16bb11c539 100644 --- a/plugins/tech-insights/CHANGELOG.md +++ b/plugins/tech-insights/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-tech-insights +## 0.3.13 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-tech-insights-common@0.2.11 + +## 0.3.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + +## 0.3.13-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/catalog-model@1.4.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-tech-insights-common@0.2.11 + ## 0.3.13-next.0 ### Patch Changes diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index 67df31cf47..804bea606a 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights", - "version": "0.3.13-next.0", + "version": "0.3.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index b14e7c34f7..c52c2f2423 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-tech-radar +## 0.6.7 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + ## 0.6.7-next.0 ### Patch Changes diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index e376c05baf..789f3a3e16 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-tech-radar", "description": "A Backstage plugin that lets you display a Tech Radar for your organization", - "version": "0.6.7-next.0", + "version": "0.6.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 4998c21869..51e9d7885d 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.17 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/integration-react@1.1.16 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog@1.12.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2 + - @backstage/plugin-search-react@1.6.4 + - @backstage/plugin-techdocs@1.6.6 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9 + +## 1.0.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.12.1-next.2 + - @backstage/plugin-techdocs@1.6.6-next.2 + - @backstage/integration-react@1.1.16-next.1 + +## 1.0.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/plugin-catalog@1.12.1-next.1 + - @backstage/plugin-techdocs@1.6.6-next.1 + - @backstage/core-app-api@1.9.1-next.0 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/test-utils@1.4.2-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + ## 1.0.17-next.0 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index ff14fb2187..898ba2392b 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-addons-test-utils", - "version": "1.0.17-next.0", + "version": "1.0.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 24c9a12c2b..376fb47d2d 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-techdocs-backend +## 1.6.5 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4 + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/integration@1.6.0 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + +## 1.6.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.2 + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## 1.6.5-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.4-next.1 + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-common@1.0.15 + - @backstage/plugin-permission-common@0.7.7 + - @backstage/plugin-search-common@1.2.5 + ## 1.6.5-next.0 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index f3718e7a7d..9ab72b47bd 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-backend", "description": "The Backstage backend plugin that renders technical documentation for your components", - "version": "1.6.5-next.0", + "version": "1.6.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-backend/src/plugin.ts b/plugins/techdocs-backend/src/plugin.ts index 134de2bba6..745370b67a 100644 --- a/plugins/techdocs-backend/src/plugin.ts +++ b/plugins/techdocs-backend/src/plugin.ts @@ -41,7 +41,7 @@ export const techdocsPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, urlReader: coreServices.urlReader, http: coreServices.httpRouter, diff --git a/plugins/techdocs-backend/src/search/index.ts b/plugins/techdocs-backend/src/search/index.ts index fc2b48375e..904f998042 100644 --- a/plugins/techdocs-backend/src/search/index.ts +++ b/plugins/techdocs-backend/src/search/index.ts @@ -25,12 +25,12 @@ import type { TechDocsCollatorFactoryOptions as _TechDocsCollatorFactoryOptions /** * @public - * @deprecated import from `@backstage/search-backend-module-techdocs` instead + * @deprecated import from `@backstage/plugin-search-backend-module-techdocs` instead */ export type TechDocsCollatorFactoryOptions = _TechDocsCollatorFactoryOptions; /** * @public - * @deprecated import from `@backstage/search-backend-module-techdocs` instead + * @deprecated import from `@backstage/plugin-search-backend-module-techdocs` instead */ export const DefaultTechDocsCollatorFactory = _DefaultTechDocsCollatorFactory; diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index b04e012a5f..46360b59ca 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.0.16 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9 + +## 1.0.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/theme@0.4.1 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + ## 1.0.16-next.0 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 79ac81c021..458738ec7f 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", "description": "Plugin module for contributed TechDocs Addons", - "version": "1.0.16-next.0", + "version": "1.0.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 7cafbe692c..2ec3167604 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-techdocs-node +## 1.7.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/integration@1.6.0 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-search-common@1.2.5 + +## 1.7.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + +## 1.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/integration@1.5.1 + - @backstage/integration-aws-node@0.1.5 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/plugin-search-common@1.2.5 + ## 1.7.4-next.0 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 6efbc99547..04539f46c3 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-node", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "1.7.4-next.0", + "version": "1.7.4", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 8aa94ebbe5..09c2ffc465 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-react +## 1.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/version-bridge@1.0.4 + ## 1.1.9-next.0 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 42df70d181..e146fa1bf7 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-react", "description": "Shared frontend utilities for TechDocs and Addons", - "version": "1.1.9-next.0", + "version": "1.1.9", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index fbf8b4cbdc..c77450431a 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-techdocs +## 1.6.6 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-techdocs-react@1.1.9 + +## 1.6.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 1.6.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + ## 1.6.6-next.0 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 2892e2d983..54bf661d99 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "1.6.6-next.0", + "version": "1.6.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index 3cdec47720..f4935afdd4 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-todo-backend +## 0.2.0 + +### Minor Changes + +- ebeb77586975: Now performs request validation based on OpenAPI schema through `@backstage/backend-openapi-utils`. Error responses for invalid input, like `"a"` instead of a number, may have changed. + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- ad3fb5401729: Fixed a bug where filter queries from the frontend would always fail validation. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/backend-openapi-utils@0.0.3 + - @backstage/plugin-catalog-node@1.4.1 + - @backstage/integration@1.6.0 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-catalog-node@1.4.1-next.2 + +## 0.2.0-next.1 + +### Patch Changes + +- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config` +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-openapi-utils@0.0.3-next.1 + - @backstage/plugin-catalog-node@1.4.1-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-client@1.4.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 1257ce5303..c2ba1ea8d1 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo-backend", "description": "A Backstage backend plugin that lets you browse TODO comments in your source code", - "version": "0.2.0-next.0", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts index 276d176efb..2ce1771ecb 100644 --- a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts +++ b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts @@ -183,7 +183,7 @@ export const todoReaderServiceRef = createServiceRef({ service, deps: { reader: coreServices.urlReader, - config: coreServices.config, + config: coreServices.rootConfig, logger: coreServices.logger, }, factory: async ({ reader, config, logger }) => { diff --git a/plugins/todo-backend/src/schema/openapi.generated.ts b/plugins/todo-backend/src/schema/openapi.generated.ts index a58a96ddb8..effe851790 100644 --- a/plugins/todo-backend/src/schema/openapi.generated.ts +++ b/plugins/todo-backend/src/schema/openapi.generated.ts @@ -173,6 +173,7 @@ export const spec = { name: 'filter', in: 'query', required: false, + allowReserved: true, schema: { type: 'array', description: diff --git a/plugins/todo-backend/src/schema/openapi.yaml b/plugins/todo-backend/src/schema/openapi.yaml index 0e6203e002..8023144f48 100644 --- a/plugins/todo-backend/src/schema/openapi.yaml +++ b/plugins/todo-backend/src/schema/openapi.yaml @@ -111,6 +111,7 @@ paths: - name: filter in: query required: false + allowReserved: true schema: type: array description: A list of filters used to narrow down the listed TODO items diff --git a/plugins/todo-backend/src/service/router.test.ts b/plugins/todo-backend/src/service/router.test.ts index 0bf591316c..64f894a147 100644 --- a/plugins/todo-backend/src/service/router.test.ts +++ b/plugins/todo-backend/src/service/router.test.ts @@ -131,6 +131,23 @@ describe('createRouter', () => { ); }); + it('forwards filter query', async () => { + mockService.listTodos.mockResolvedValueOnce(mockListBody); + + const response = await request(app).get('/v1/todos?filter=text=*borked*'); + expect(response.status).toEqual(200); + expect(response.body).toEqual(mockListBody); + expect(mockService.listTodos).toHaveBeenCalledWith( + { + entity: undefined, + offset: undefined, + limit: undefined, + filters: [{ field: 'text', value: '*borked*' }], + }, + { token: undefined }, + ); + }); + it('rejects invalid queries', async () => { request(app) .get('/v1/todos?entity=k:n&entity=k:n') diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index 9a7758da72..0b99db3d3a 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-todo +## 0.2.23 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.2.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.2.23-next.0 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index fa18bf94bb..67dea39fa4 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo", "description": "A Backstage plugin that lets you browse TODO comments in your source code", - "version": "0.2.23-next.0", + "version": "0.2.23", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index c54bb63337..886a14e061 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-user-settings-backend +## 0.1.12 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-plugin-api@0.6.0 + - @backstage/plugin-auth-node@0.2.17 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 0.1.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.0-next.2 + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-auth-node@0.2.17-next.2 + +## 0.1.12-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-auth-node@0.2.17-next.1 + - @backstage/backend-plugin-api@0.6.0-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + ## 0.1.12-next.0 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index caaa31faa8..05912f1f55 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings-backend", "description": "The Backstage backend plugin to manage user settings", - "version": "0.1.12-next.0", + "version": "0.1.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 026ea410f9..ab04a4af40 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-user-settings +## 0.7.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.9.1 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/types@1.1.0 + +## 0.7.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.7.6-next.0 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 57a6d89be0..a832ceccf2 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings", "description": "A Backstage plugin that provides a settings page", - "version": "0.7.6-next.0", + "version": "0.7.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault-backend/CHANGELOG.md b/plugins/vault-backend/CHANGELOG.md index 682d521970..84b61db8ed 100644 --- a/plugins/vault-backend/CHANGELOG.md +++ b/plugins/vault-backend/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-vault-backend +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/backend-tasks@0.5.5 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + +## 0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.5-next.2 + - @backstage/backend-common@0.19.2-next.2 + +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/backend-tasks@0.5.5-next.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + ## 0.3.4-next.0 ### Patch Changes diff --git a/plugins/vault-backend/package.json b/plugins/vault-backend/package.json index 36cfcbee1d..e5ea735974 100644 --- a/plugins/vault-backend/package.json +++ b/plugins/vault-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault-backend", "description": "A Backstage backend plugin that integrates towards Vault", - "version": "0.3.4-next.0", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault/CHANGELOG.md b/plugins/vault/CHANGELOG.md index d6ba597c0d..dcf84ffc9c 100644 --- a/plugins/vault/CHANGELOG.md +++ b/plugins/vault/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-vault +## 0.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/vault/package.json b/plugins/vault/package.json index 7d77495527..0466c81c3f 100644 --- a/plugins/vault/package.json +++ b/plugins/vault/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault", "description": "A Backstage plugin that integrates towards Vault", - "version": "0.1.15-next.0", + "version": "0.1.15", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/xcmetrics/CHANGELOG.md b/plugins/xcmetrics/CHANGELOG.md index 501758e40c..c85c22f0fa 100644 --- a/plugins/xcmetrics/CHANGELOG.md +++ b/plugins/xcmetrics/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-xcmetrics +## 0.2.41 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- 3d86be999fdf: Prefer simple `theme.spacing` without string interpolation +- Updated dependencies + - @backstage/core-components@0.13.4 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + +## 0.2.41-next.1 + +### Patch Changes + +- 12a8c94eda8d: Add package repository and homepage metadata +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + ## 0.2.41-next.0 ### Patch Changes diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 0a3ca50ca8..87bed48d61 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-xcmetrics", "description": "A Backstage plugin that shows XCode build metrics for your components", - "version": "0.2.41-next.0", + "version": "0.2.41", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/yarn.lock b/yarn.lock index 86528ba0de..8a220323d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -102,16 +102,16 @@ __metadata: linkType: hard "@apollo/client@npm:^3.0.0": - version: 3.7.17 - resolution: "@apollo/client@npm:3.7.17" + version: 3.8.1 + resolution: "@apollo/client@npm:3.8.1" dependencies: "@graphql-typed-document-node/core": ^3.1.1 - "@wry/context": ^0.7.0 - "@wry/equality": ^0.5.0 - "@wry/trie": ^0.4.0 + "@wry/context": ^0.7.3 + "@wry/equality": ^0.5.6 + "@wry/trie": ^0.4.3 graphql-tag: ^2.12.6 hoist-non-react-statics: ^3.3.2 - optimism: ^0.16.2 + optimism: ^0.17.5 prop-types: ^15.7.2 response-iterator: ^0.2.6 symbol-observable: ^4.0.0 @@ -133,7 +133,7 @@ __metadata: optional: true subscriptions-transport-ws: optional: true - checksum: d25e955f3c409885185405ca5876cb74538becafa202257a27483c07d7faa9ce83b34d4cab4b7fd9ad217f80d768e651881292c406dbbca221c91013917890d7 + checksum: 3a1748359a7c0f339764e7764dc6c7426be1d522eda963416d3a693733edbce8408cb8f78f9c98b036d34621af663e3dd3446703dfd29037c78a77eacd3c70bb languageName: node linkType: hard @@ -199,8 +199,8 @@ __metadata: linkType: hard "@apollo/server@npm:^4.0.0": - version: 4.8.1 - resolution: "@apollo/server@npm:4.8.1" + version: 4.9.1 + resolution: "@apollo/server@npm:4.9.1" dependencies: "@apollo/cache-control-types": ^1.0.3 "@apollo/server-gateway-interface": ^1.1.1 @@ -230,7 +230,7 @@ __metadata: whatwg-mimetype: ^3.0.0 peerDependencies: graphql: ^16.6.0 - checksum: b59c60b23b90aed9e2f74de1d16f4ac7582f16e2f98d830c85e1339525867786ab17a9295aadf53cdcfdfd0540f81273706e013aae9148ebc3e55e86c820b21c + checksum: d5299acfc8f0519adb5aed59e067880bdfd2528353711be07c935d0038ef95e06eecfc58af473011d215c92659f1858ae06c26fe9f70f865383d8e5457e17be5 languageName: node linkType: hard @@ -569,517 +569,457 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/chunked-blob-reader-native@npm:3.310.0": - version: 3.310.0 - resolution: "@aws-sdk/chunked-blob-reader-native@npm:3.310.0" - dependencies: - "@aws-sdk/util-base64": 3.310.0 - tslib: ^2.5.0 - checksum: 8b9b88edcaa28b58277a06e95c1b01f1eb2bfba3d994031664957340ae2bc857f1575ec999e8dbd085242b52719228ebff05af1cc0228b1f9296646f4b2095cb - languageName: node - linkType: hard - -"@aws-sdk/chunked-blob-reader@npm:3.310.0": - version: 3.310.0 - resolution: "@aws-sdk/chunked-blob-reader@npm:3.310.0" - dependencies: - tslib: ^2.5.0 - checksum: 4969fe05c6cea38d0a8dc3ec8e37cbd82a0a5b6f8c32ad6c7d02f0800bc3641e96356f47981c88b645b4dc2bdcb73d03d7ec67ac38d277dde8337b61688f815b - languageName: node - linkType: hard - -"@aws-sdk/client-cognito-identity@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.370.0" +"@aws-sdk/client-cognito-identity@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.387.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.370.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/client-sts": 3.387.0 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 + "@smithy/config-resolver": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: e13eb18f70ce89ea1d2a111ad74892bbf5ca57a926d97a8787e6fa71e502eed94660cc2e364223151a4c6d019a4d30fd7f21da093f39893e6719ee481fdaf47a + checksum: 88be8e754b0f65bef93cf00f87336aa66b6e7d52d78f6bef132716f528f908318628f87316d0feae2d4a8fcf0ba2d2362ecae4bdea193eb8f14ed43eaa2deee1 languageName: node linkType: hard "@aws-sdk/client-eks@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/client-eks@npm:3.370.0" + version: 3.387.0 + resolution: "@aws-sdk/client-eks@npm:3.387.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.370.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 - "@smithy/util-waiter": ^1.0.1 + "@aws-sdk/client-sts": 3.387.0 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 + "@smithy/config-resolver": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 + "@smithy/util-waiter": ^2.0.2 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 4b34fe5155161da1b0102e49d62720e8ad067effe188af8aeb9a755d05eae55f52edb8279c19ad18851513d526050f08c239d4a609ea482c5352223b71785e54 + checksum: f4a8a64d7c7bfef23c4fb610156a4b80c14728870bf67346ee052082f136f02776aba89600adc79dae803d1e96c351f16a0dc4a0ddbe9df09a198644966d4fc3 languageName: node linkType: hard "@aws-sdk/client-organizations@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/client-organizations@npm:3.370.0" + version: 3.387.0 + resolution: "@aws-sdk/client-organizations@npm:3.387.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.370.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/client-sts": 3.387.0 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 + "@smithy/config-resolver": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 738b259771e83c52abdcc2b2511521f91777262b7e14b1aceb78d8dae7179e2a17648733fe7a40358ad6040ee03f1f251a977a54197b09d4d9f0b6b40052e8d7 + checksum: 095de70f230113d1b035a25c93074e6953a06b99deb0bf39928c661e05710c7678defbdbb58dbb6919e6f4633fbc9a26a684b07821ba69e244e14733ba978eaa languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/client-s3@npm:3.370.0" + version: 3.387.0 + resolution: "@aws-sdk/client-s3@npm:3.387.0" dependencies: "@aws-crypto/sha1-browser": 3.0.0 "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.370.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/hash-blob-browser": 3.370.0 - "@aws-sdk/hash-stream-node": 3.370.0 - "@aws-sdk/md5-js": 3.370.0 - "@aws-sdk/middleware-bucket-endpoint": 3.370.0 - "@aws-sdk/middleware-expect-continue": 3.370.0 - "@aws-sdk/middleware-flexible-checksums": 3.370.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-location-constraint": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-sdk-s3": 3.370.0 - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/middleware-ssec": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/signature-v4-multi-region": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 + "@aws-sdk/client-sts": 3.387.0 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/middleware-bucket-endpoint": 3.387.0 + "@aws-sdk/middleware-expect-continue": 3.387.0 + "@aws-sdk/middleware-flexible-checksums": 3.387.0 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-location-constraint": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-sdk-s3": 3.387.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/middleware-ssec": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/signature-v4-multi-region": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 "@aws-sdk/xml-builder": 3.310.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/eventstream-serde-browser": ^1.0.1 - "@smithy/eventstream-serde-config-resolver": ^1.0.1 - "@smithy/eventstream-serde-node": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-stream": ^1.0.1 - "@smithy/util-utf8": ^1.0.1 - "@smithy/util-waiter": ^1.0.1 + "@smithy/config-resolver": ^2.0.2 + "@smithy/eventstream-serde-browser": ^2.0.2 + "@smithy/eventstream-serde-config-resolver": ^2.0.2 + "@smithy/eventstream-serde-node": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-blob-browser": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/hash-stream-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/md5-js": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-stream": ^2.0.2 + "@smithy/util-utf8": ^2.0.0 + "@smithy/util-waiter": ^2.0.2 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 6e395a519a46c1351597b7104e510c8a6f17b7d531eb9a1bf0a706ba69c6c383a9f02b2309d9eb712ead8454e6c1a3906ce65202b7cf353c0385879162875c7b + checksum: cc0600dfc971b203bb737850613ab986811b36014f4f42e8cebca71d05baf7ef3d618687ae43c50694b1341f339a44671813187dff6910d24130418d755dd265 languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/client-sqs@npm:3.370.0" + version: 3.387.0 + resolution: "@aws-sdk/client-sqs@npm:3.387.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.370.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-sdk-sqs": 3.370.0 - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/md5-js": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/client-sts": 3.387.0 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-sdk-sqs": 3.387.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 + "@smithy/config-resolver": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/md5-js": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 3029a13464fcf3d7a913e9bbe5e98733f733489a1a7d69256b3189e07ce016cbf350b06368636e75426f10f60b8e14aac29678fed6ca77e378954f3cd0c24b4a + checksum: 3382ad67ec9679b73a571e0389db1ae80f21fa28beb3a13e3107ce72021705f37c3aacb06b3a3cfb4bc751de0453a443e30f315d5b2cb319abcd3454158c3518 languageName: node linkType: hard -"@aws-sdk/client-sso-oidc@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/client-sso-oidc@npm:3.370.0" +"@aws-sdk/client-sso@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/client-sso@npm:3.387.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 + "@smithy/config-resolver": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: a94d58fdef83615fa63ecde638a3ece82c2eaf05d7309a4ab336e0c6bebf8501aad123efbbf0714db4710208897594e13bfe1e450a657aa4bd82fbea338c0946 + checksum: ca4750675bc39e7201b4b611cd642cb5f37413138a09670499c00ea637509aab0bd54a7b96e6b4a2bec810f12c9c87d0519fc6cad0a7f8a86ecad4bf8047a7ac languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/client-sso@npm:3.370.0" +"@aws-sdk/client-sts@npm:3.387.0, @aws-sdk/client-sts@npm:^3.350.0": + version: 3.387.0 + resolution: "@aws-sdk/client-sts@npm:3.387.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 - tslib: ^2.5.0 - checksum: e6797cac371b7da2b01885f0a4bcb81033af2246bb45ca6454d76b223ab23a824667479e9e7a77fe3d99164fb009a970ff6498d9d13ef57587ef1652f0f7f036 - languageName: node - linkType: hard - -"@aws-sdk/client-sts@npm:3.370.0, @aws-sdk/client-sts@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/client-sts@npm:3.370.0" - dependencies: - "@aws-crypto/sha256-browser": 3.0.0 - "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/middleware-host-header": 3.370.0 - "@aws-sdk/middleware-logger": 3.370.0 - "@aws-sdk/middleware-recursion-detection": 3.370.0 - "@aws-sdk/middleware-sdk-sts": 3.370.0 - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/middleware-user-agent": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@aws-sdk/util-user-agent-browser": 3.370.0 - "@aws-sdk/util-user-agent-node": 3.370.0 - "@smithy/config-resolver": ^1.0.1 - "@smithy/fetch-http-handler": ^1.0.1 - "@smithy/hash-node": ^1.0.1 - "@smithy/invalid-dependency": ^1.0.1 - "@smithy/middleware-content-length": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/middleware-retry": ^1.0.3 - "@smithy/middleware-serde": ^1.0.1 - "@smithy/middleware-stack": ^1.0.1 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/node-http-handler": ^1.0.2 - "@smithy/protocol-http": ^1.1.0 - "@smithy/smithy-client": ^1.0.3 - "@smithy/types": ^1.1.0 - "@smithy/url-parser": ^1.0.1 - "@smithy/util-base64": ^1.0.1 - "@smithy/util-body-length-browser": ^1.0.1 - "@smithy/util-body-length-node": ^1.0.1 - "@smithy/util-defaults-mode-browser": ^1.0.1 - "@smithy/util-defaults-mode-node": ^1.0.1 - "@smithy/util-retry": ^1.0.3 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/middleware-host-header": 3.387.0 + "@aws-sdk/middleware-logger": 3.387.0 + "@aws-sdk/middleware-recursion-detection": 3.387.0 + "@aws-sdk/middleware-sdk-sts": 3.387.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/middleware-user-agent": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@aws-sdk/util-user-agent-browser": 3.387.0 + "@aws-sdk/util-user-agent-node": 3.387.0 + "@smithy/config-resolver": ^2.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/hash-node": ^2.0.2 + "@smithy/invalid-dependency": ^2.0.2 + "@smithy/middleware-content-length": ^2.0.2 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/middleware-retry": ^2.0.2 + "@smithy/middleware-serde": ^2.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-body-length-browser": ^2.0.0 + "@smithy/util-body-length-node": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.2 + "@smithy/util-defaults-mode-node": ^2.0.2 + "@smithy/util-retry": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 55ce8a7a8a8a44cffdbfca10ac2e4d389a4a13e81a4558ef401c3174727296c47375cec5898023e082285aaf5804d62f5d887da53e343f24aec8d8b5bc4ea29b + checksum: 6f3a0a36c7d2881b438e64a2b6cb11f809c4bc406af27fb692d5d3a7cebb93fc5b26f235ee5a4f3eea620b0c07d015ce11e2d8f32c79b3ea01221fe8548c2421 languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.370.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.387.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/client-cognito-identity": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 3ee862b115b3549bebeff65b6d02e26154f22dc2f9efde8e805c7305537a9cd4b9af7f3a8cb3f7f325c2ae211cb687ca4a9e345990c13319e4e3fad0b33f0503 + checksum: 126a72f6b6515237aa128c22fe6b0fbdfefbfb39950da8686187f567fdbaa6cb18d41437b67e80bd66e4f5d2eae0018587585700e0950c2a639e04092f31445e languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.370.0" +"@aws-sdk/credential-provider-env@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 0295278ca333b8548c417d164569df737a5c587d7d39c35e9d0c9b55022a3d8caa3de04dd242135069cb3570cd9f52a2c1012fcaef4a13f49e91cec6aa0ed9b6 + checksum: 7cfa4c87224a8632741d8fd1c25384a59413581b94eb79e1547a4f9b3542c2b27559c097a09f747f4b1617a3c382795f17aebc290b5e35a8a5fae1f93736449f languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.370.0" +"@aws-sdk/credential-provider-ini@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.387.0" dependencies: - "@aws-sdk/credential-provider-env": 3.370.0 - "@aws-sdk/credential-provider-process": 3.370.0 - "@aws-sdk/credential-provider-sso": 3.370.0 - "@aws-sdk/credential-provider-web-identity": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/credential-provider-imds": ^1.0.1 - "@smithy/property-provider": ^1.0.1 - "@smithy/shared-ini-file-loader": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/credential-provider-env": 3.387.0 + "@aws-sdk/credential-provider-process": 3.387.0 + "@aws-sdk/credential-provider-sso": 3.387.0 + "@aws-sdk/credential-provider-web-identity": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 8d8d0d386eb521047ac515fe8bb99faad0b5613dfd1e1824ebc0b427f68d420a68c97ba5b1b5e64a16e3edda3633d76d2f632acdf2c8b37ff39148c8e50c7200 + checksum: 40e69ca6081d24016b0a56fab6cca9b6cd2d5ca2cf17ab2e3e99a99e351d78d86d38f5e424a6c26355d099c6e060e24c56ff3064b6b1fbc2cad9c89b1dd655c4 languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.370.0, @aws-sdk/credential-provider-node@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.370.0" +"@aws-sdk/credential-provider-node@npm:3.387.0, @aws-sdk/credential-provider-node@npm:^3.350.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.387.0" dependencies: - "@aws-sdk/credential-provider-env": 3.370.0 - "@aws-sdk/credential-provider-ini": 3.370.0 - "@aws-sdk/credential-provider-process": 3.370.0 - "@aws-sdk/credential-provider-sso": 3.370.0 - "@aws-sdk/credential-provider-web-identity": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/credential-provider-imds": ^1.0.1 - "@smithy/property-provider": ^1.0.1 - "@smithy/shared-ini-file-loader": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/credential-provider-env": 3.387.0 + "@aws-sdk/credential-provider-ini": 3.387.0 + "@aws-sdk/credential-provider-process": 3.387.0 + "@aws-sdk/credential-provider-sso": 3.387.0 + "@aws-sdk/credential-provider-web-identity": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 564422e77d791fcc307f2954ebf2e7a17273dff63812ed8528196f3f38163db726a71cc1c22d2e6de287727d3506764dab8c9c99de7fe3426d6cec0bf4d4f0ab + checksum: cdc3f29c6be304e39cb42f8b53227f607e8bb10d0fae542614d76b322e1a75718d70a4726d97cb24d6e3da51245b837dcb07cfaf34eb8600d290267c8987a06a languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.370.0" +"@aws-sdk/credential-provider-process@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/shared-ini-file-loader": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 286080cfdb0a433a619cafb29316cc4259022de5369a5a2eefd638a8b025626eb730a6d676bdff4ef0fb905c44fe4a1d4c856f62318553f62b425eea406229de + checksum: 90b5c902b659dd6d39b6e81503fa4587ed5bb7c4b6a7ecb648561e8ca7a82c621b0b65496855cba3e68d63e1c432e26d40c89c0194e5952f31b52cbd20bca8ae languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.370.0" +"@aws-sdk/credential-provider-sso@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.387.0" dependencies: - "@aws-sdk/client-sso": 3.370.0 - "@aws-sdk/token-providers": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/shared-ini-file-loader": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/client-sso": 3.387.0 + "@aws-sdk/token-providers": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: a5ef52fd213f567ba7aaa243bf4494a4df272522de4db952f0f9a0b1c148920fc49aff43321ec11b08ddd62e34c2686b0d50ae3a20c229e680d392b3c194b0f5 + checksum: 740d16079be50a43a23b59c10d44fabca3da67ee251f2b6975d7b58909554c66e0c8a40f6e9457d85f2997a63cf435bcbf82e53d9d2f7c7005f243dbe5305fff languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.370.0" +"@aws-sdk/credential-provider-web-identity@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 3e9ce1c7749e60302f966a64635a11dece3da47ece8da6e4eae0c784bdf481bee48a0b1085014b11b812ee608fe99fb20548203bfa8d2f43f709b39b1a484f9b + checksum: 47cf13e4275ee948750c78c147fe70a64698197ed86bc38c29269e745001775cf4261e763e6dd37235ed50adc5638add08b3c04d6bb9af1cf1ef7c79f0cd336b languageName: node linkType: hard "@aws-sdk/credential-providers@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/credential-providers@npm:3.370.0" + version: 3.387.0 + resolution: "@aws-sdk/credential-providers@npm:3.387.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.370.0 - "@aws-sdk/client-sso": 3.370.0 - "@aws-sdk/client-sts": 3.370.0 - "@aws-sdk/credential-provider-cognito-identity": 3.370.0 - "@aws-sdk/credential-provider-env": 3.370.0 - "@aws-sdk/credential-provider-ini": 3.370.0 - "@aws-sdk/credential-provider-node": 3.370.0 - "@aws-sdk/credential-provider-process": 3.370.0 - "@aws-sdk/credential-provider-sso": 3.370.0 - "@aws-sdk/credential-provider-web-identity": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/credential-provider-imds": ^1.0.1 - "@smithy/property-provider": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/client-cognito-identity": 3.387.0 + "@aws-sdk/client-sso": 3.387.0 + "@aws-sdk/client-sts": 3.387.0 + "@aws-sdk/credential-provider-cognito-identity": 3.387.0 + "@aws-sdk/credential-provider-env": 3.387.0 + "@aws-sdk/credential-provider-ini": 3.387.0 + "@aws-sdk/credential-provider-node": 3.387.0 + "@aws-sdk/credential-provider-process": 3.387.0 + "@aws-sdk/credential-provider-sso": 3.387.0 + "@aws-sdk/credential-provider-web-identity": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@smithy/credential-provider-imds": ^2.0.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 7eb60bda6bb4fe22c487aa19469858ba593c49fdad9cc8c2447307cdfdd883fd87743a398007f62ddf7e354eff8dfa716ef614adaaee2712f1a9866f478c32de + checksum: 761260e185f932cddc523d3e1491497cf3ff147496af354aed2ddd43da5a86218dbc2eccec00a71c166070dd24261795c2eb227ab986761478bd138ccb3fa9af languageName: node linkType: hard @@ -1095,29 +1035,6 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/hash-blob-browser@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/hash-blob-browser@npm:3.370.0" - dependencies: - "@aws-sdk/chunked-blob-reader": 3.310.0 - "@aws-sdk/chunked-blob-reader-native": 3.310.0 - "@aws-sdk/types": 3.370.0 - tslib: ^2.5.0 - checksum: 51d24225d299c238032aa6e29cf2c13b876231cf8fef40dcf9ef1ea05b1fb873cf7de76eba13ce63b2324e156c85e2795ba0e66abc2570745b9ec92586185f4b - languageName: node - linkType: hard - -"@aws-sdk/hash-stream-node@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/hash-stream-node@npm:3.370.0" - dependencies: - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-utf8": 3.310.0 - tslib: ^2.5.0 - checksum: 97add2caadb15f5b6cbc551801dc672ba6e3664667c9be53c794cca9a1be827df932ac40148bae0e7586b6f7bebb2483257501d3c9e946dd1ba7fbb31a29de33 - languageName: node - linkType: hard - "@aws-sdk/is-array-buffer@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/is-array-buffer@npm:3.310.0" @@ -1128,44 +1045,33 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.350.0": - version: 3.370.0 - resolution: "@aws-sdk/lib-storage@npm:3.370.0" + version: 3.387.0 + resolution: "@aws-sdk/lib-storage@npm:3.387.0" dependencies: - "@smithy/abort-controller": ^1.0.1 - "@smithy/middleware-endpoint": ^1.0.2 - "@smithy/smithy-client": ^1.0.3 + "@smithy/abort-controller": ^2.0.1 + "@smithy/middleware-endpoint": ^2.0.2 + "@smithy/smithy-client": ^2.0.2 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: ^2.5.0 peerDependencies: "@aws-sdk/client-s3": ^3.0.0 - checksum: a45fc04b33ad462b8d9ca8f0233c10b49eb91ace24cf8a53a359d96b6266a4eef2bc713bbbacb4799703c339756a9d39cb17bfaa44f607aa99df22c688c0748b + checksum: aa753c2e6f9db051fe893ab2352a3c7c84ca53089ba837b972e1ae5911af71bb978002ea07ed59f673b3773fe3d1fbd2ce2fff251b671f672655cfbf9b48626e languageName: node linkType: hard -"@aws-sdk/md5-js@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/md5-js@npm:3.370.0" +"@aws-sdk/middleware-bucket-endpoint@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-utf8": 3.310.0 - tslib: ^2.5.0 - checksum: 1a0ae6590d3b860ea380e1299dc6debc533ab39a055aa204c1be08cbdead0e5eaac8ac8deb1b9332f00d7177d874b73ba03de5dcf012b2b4cd5ad96ae36bd7b5 - languageName: node - linkType: hard - -"@aws-sdk/middleware-bucket-endpoint@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.370.0" - dependencies: - "@aws-sdk/types": 3.370.0 + "@aws-sdk/types": 3.387.0 "@aws-sdk/util-arn-parser": 3.310.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 - "@smithy/util-config-provider": ^1.0.1 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-config-provider": ^2.0.0 tslib: ^2.5.0 - checksum: b8ed893397dec1dc5373b9920d70ddb30ba8a3651aa45805b9a47cafacff98d65927dfe754e9f5fcdc0c8d8d4ef7f171bc9fa4968d248d71429d61c2d822e493 + checksum: 61b134bb6ba13d8007f1e8bd9a9c4ab4041c66fa184deaf7a0796297c218f14eb175f957038577909ac94e16fdf97d115f698a2d629ce1d4122593fba86f192e languageName: node linkType: hard @@ -1182,115 +1088,115 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-expect-continue@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-expect-continue@npm:3.370.0" +"@aws-sdk/middleware-expect-continue@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 5524c43c62a371875eff73304600213dbf296ad18879f5d9462b7199e4cd01cdbb87b8d2cf0c4f412ef3cfd95a51cc722c9b87f58ff91fd6daeb5ffc1d0082e4 + checksum: 48644c0809702b9b82c9d43bad3a3919868295914550d5c0ce72dbefa39dd24daf4aa02b7a431e1856e7bff3c3bff93ea1d06936f7a20213c1ddbea14cf6b1ad languageName: node linkType: hard -"@aws-sdk/middleware-flexible-checksums@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.370.0" +"@aws-sdk/middleware-flexible-checksums@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.387.0" dependencies: "@aws-crypto/crc32": 3.0.0 "@aws-crypto/crc32c": 3.0.0 - "@aws-sdk/types": 3.370.0 - "@smithy/is-array-buffer": ^1.0.1 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/types": 3.387.0 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: b8af3c7beda3baa8290735f98f97476108304fb96d522fb27a0d449d4e82262c0cc66d59f36688067ad073714653cf88050a73acfa5d6ba5f3e90f645778bced + checksum: 056a4558750f3c6bf676154778de813bbed2897d5a79efbe142ea6f99643fdf98b4367f602bb61a3cecd5e22ea2b680b842a0cfc5cf8f828d936626d8d4126ea languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.370.0" +"@aws-sdk/middleware-host-header@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 2a7d3f3a0ab75d3c09a9cecc3e545706df2ba561851cde0992a41d00bad2695e5d5f29ff388037a11fb52770b3b9b2a98166cab5c4d1a2fa438ecea32e14604b + checksum: 91fdffa0051d1c426533170b1770c174abffc822b6e8e0cf0b850536a32c9fdea3241279494e2511232a46260b2320546bb9a59b2d4ee05a72f3cedabe6fa1a1 languageName: node linkType: hard -"@aws-sdk/middleware-location-constraint@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-location-constraint@npm:3.370.0" +"@aws-sdk/middleware-location-constraint@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: d3156144dd2bdba620a164d139e6466f79261ec2ad93c7014200d1e7fea21d0aedfcec83e69cf31ce806c42aca4ae07155245901e71d8273ec02f1fd6b75032a + checksum: 4027555e3c0ba089711a93ce0ca0ab4527052ff67fe7d390b59d305f238109b9f4aec63ac1e51b6e5a88026c2417967dc7091178061d096fbb3ee23ca2947555 languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-logger@npm:3.370.0" +"@aws-sdk/middleware-logger@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-logger@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: f3c4062247d2a0064f82e412f027c2cd950d512d9e4a14d603a925ae97cd527e304439e854fb0b2de1f84b429ace317edaa09a8ee9319efec8df47aa3abbb65b + checksum: 75812390856559eaf9e8259f3d335c97268783d2e175dbd4568271e8f321a7a34754fcfcff0f70726a751c0418f6787ddbd7f1ebb3b644530e743edb5da63c2b languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.370.0" +"@aws-sdk/middleware-recursion-detection@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: ee259519548171ef3381f9bd48568d8052a7980cfe1a8070d72b44af0522a55baa0e440db5c1460123dc48fb217251a31aea236beda7220af60ebbe2375c96a0 + checksum: dd6ed9eb8969181f3adf5820f66db38ab544fda03f44bec87e6efdb194f01184094dc2d3ce150e9f045a75decb99e37a9a1dc8b37a78f8722caae8367c9db8d9 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-s3@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-sdk-s3@npm:3.370.0" +"@aws-sdk/middleware-sdk-s3@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 + "@aws-sdk/types": 3.387.0 "@aws-sdk/util-arn-parser": 3.310.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 23da26ca2df37b33c8201aec5cfa86f888abe9b14a23a6815d648d964efa5d99e38b036ecf8adac818ad1db8097da2f345c78d7c7fe6dcc323e5043e38d8cd24 + checksum: 674ebd73ed89f8375b5a7f15135a65e61736b8bc58f076504a050e9cb679da867ba256e6dc8c46b2a403c3a2fbed0667e40bb3b40bf79bc54a669650f09d97ac languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.370.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/types": ^1.1.0 - "@smithy/util-hex-encoding": ^1.0.1 - "@smithy/util-utf8": ^1.0.1 + "@aws-sdk/types": 3.387.0 + "@smithy/types": ^2.1.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 980c38781355f4481251bf88692926d1c819c4cf59cf76bca8ea6d4dbea4677034c8c6c84f0435f218ae5c289a2450aa048cc6861b4552a2a0e0ae1f81f806bc + checksum: fa734414df1c597c8fbcdff5131cfb240682a99d6d2302096040a05ff64742383f6812e8e41a7b1d32421b5556a138fd9241bced7eb35a29bcbca5376bb793b1 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sts@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-sdk-sts@npm:3.370.0" +"@aws-sdk/middleware-sdk-sts@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.387.0" dependencies: - "@aws-sdk/middleware-signing": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/middleware-signing": 3.387.0 + "@aws-sdk/types": 3.387.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 14877966d56518895d55b30af45ff695b835ced56e46986b3aea12b3f8df953741a18ad24fcd567eae7a4b0c4c501fe4c0a20c01d6604d0e67c2be6435b15037 + checksum: b9d5e4af2e10f980ef19add7eccd7f04eb5e42987c238acb17093b1d4239ae3430fb25e1c34ef915cd3420a2e93d59cf6570c1f9275bf5345733b1b030876cb5 languageName: node linkType: hard @@ -1304,42 +1210,42 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-signing@npm:3.370.0" +"@aws-sdk/middleware-signing@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-signing@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/protocol-http": ^1.1.0 - "@smithy/signature-v4": ^1.0.1 - "@smithy/types": ^1.1.0 - "@smithy/util-middleware": ^1.0.1 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.1.0 + "@smithy/util-middleware": ^2.0.0 tslib: ^2.5.0 - checksum: f8581ad377a8eea2345b2881adfe0c4fc35a9825ca0b55facbac5f89e088acd8e231d87ae632dd249df71f8b0113046b7cdd8a0b82c69195faf799813da14e91 + checksum: 3553748d92a64232df572f2b56d122943db9520d31b224f263ecce5711368de9d8bddc51facde9bf8a7d1075c3c381a96e65d4ad8d7f14f097d8f7e484c712e4 languageName: node linkType: hard -"@aws-sdk/middleware-ssec@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-ssec@npm:3.370.0" +"@aws-sdk/middleware-ssec@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: a5714a7b1d0d54976dc507a30f872a50d7eb0dfa11fc8f37dec16f4e69ea8ae53d4eb9722a05cfb5b8a34c6f5ac26c4e30555349122a60f67ac2d4f95b711aea + checksum: 362b2c5faf5a606734d72104071a6a5aaaab7ecb8eb36507f1db32e22f842898fa589916b59e239f8cbbccdbf4743c978ad2fab2e434d14f3397f71086df31f4 languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.370.0" +"@aws-sdk/middleware-user-agent@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@aws-sdk/util-endpoints": 3.370.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@aws-sdk/util-endpoints": 3.387.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: c4366db10a7eece54c9f5429352329e7ea31c91a2c05b5b484304fb2e69f07d297e04c387c22ddc11759945e2adc97e6541302391ad8faa5a3aae8e6a1605589 + checksum: 54cb1ddbd743bf70a28c1eebd6d1bc0adfad79b2bff0553737d238de01a83e11c9130c38526d4236457fa124d6024fd0c09607d796808dfe43821c75387d8fba languageName: node linkType: hard @@ -1387,21 +1293,21 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/signature-v4-multi-region@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/signature-v4-multi-region@npm:3.370.0" +"@aws-sdk/signature-v4-multi-region@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/protocol-http": ^1.1.0 - "@smithy/signature-v4": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/protocol-http": ^2.0.2 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 peerDependencies: "@aws-sdk/signature-v4-crt": ^3.118.0 peerDependenciesMeta: "@aws-sdk/signature-v4-crt": optional: true - checksum: 0c8ce6a70edbe8fac38e613b8e2df84baf7bfa17c9f585b9434cbdad1d7132e538a4127ea27be5071ccec7ca2ad056962e346f390e58cf349271087fa960939c + checksum: a520eb8a564edd12169addef6c449c236e6c86a657f7e1cda0331eff8adfa925e1ff4a201f4adbedd6f078fe5ceb89f933a268e5c4b61c92ff60ae47132ff5ae languageName: node linkType: hard @@ -1421,21 +1327,20 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/token-providers@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/token-providers@npm:3.370.0" +"@aws-sdk/token-providers@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/token-providers@npm:3.387.0" dependencies: - "@aws-sdk/client-sso-oidc": 3.370.0 - "@aws-sdk/types": 3.370.0 - "@smithy/property-provider": ^1.0.1 - "@smithy/shared-ini-file-loader": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/property-provider": ^2.0.0 + "@smithy/shared-ini-file-loader": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 7126c5bdc86d8a0cefbf92649927747928fd1af76ca943ec8e9c558d99c7550535b694149601c16b096ff429178e7bea51ac2ffc121a24637ddc8b03976759ed + checksum: dffbaa98da3bd30fdedab88093d3ae94b4fdad1dd12b30fd5dbf7cfb01d1e219e3f5abe403ca71514bfa1f92818dde97a6d9c9d2cda35f2cbd43882750017bc2 languageName: node linkType: hard -"@aws-sdk/types@npm:3.370.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": +"@aws-sdk/types@npm:3.370.0": version: 3.370.0 resolution: "@aws-sdk/types@npm:3.370.0" dependencies: @@ -1445,6 +1350,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/types@npm:3.387.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": + version: 3.387.0 + resolution: "@aws-sdk/types@npm:3.387.0" + dependencies: + "@smithy/types": ^2.1.0 + tslib: ^2.5.0 + checksum: 39c5c3eea4cd8705c0c9dafa187ac6e14585a1bb6d162bbda8dc3ea5522020302ccd3ff7c8b425225c625d2b83ae6e6f6b71f621da790830a8a55ed5643197ec + languageName: node + linkType: hard + "@aws-sdk/url-parser@npm:3.370.0": version: 3.370.0 resolution: "@aws-sdk/url-parser@npm:3.370.0" @@ -1465,16 +1380,6 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-base64@npm:3.310.0": - version: 3.310.0 - resolution: "@aws-sdk/util-base64@npm:3.310.0" - dependencies: - "@aws-sdk/util-buffer-from": 3.310.0 - tslib: ^2.5.0 - checksum: 3c9f7c818401fe8332d2ce438c0660cc9be7db9a5eef68d7fafa30ddcc44b0af3ba9ea58092f0e2b2537a18ec0942ce3c8f12090d3e3b9568b6a94a0713e9de7 - languageName: node - linkType: hard - "@aws-sdk/util-buffer-from@npm:3.310.0": version: 3.310.0 resolution: "@aws-sdk/util-buffer-from@npm:3.310.0" @@ -1485,13 +1390,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-endpoints@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/util-endpoints@npm:3.370.0" +"@aws-sdk/util-endpoints@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/util-endpoints@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 + "@aws-sdk/types": 3.387.0 tslib: ^2.5.0 - checksum: d351ad2fdc92bec16d0d925dbcfc3f38baed12a7984d70bf02ee2dfe4c3fd1a85f53e515f9751f31d1345fe0bdebd62be7cd08e405a31354db92312f0cc6282c + checksum: 15c3250f096ca4ed7a832294cc3b609e62004f8888781cbd5ee907bd325bb5b999867aa7c8fdd5b806e92424f2561f5dd8ae3ebbb876044289d36163180fa2a5 languageName: node linkType: hard @@ -1543,32 +1448,32 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.370.0" +"@aws-sdk/util-user-agent-browser@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/types": ^2.1.0 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: 3a549b1337490aaeacc21c3bff0e7459f51e764c6c9a69aafeb7950f54e118d2eeb64b652afb97e2f0aa6777df1e65fced2b96bfcdd98aacc753acfce7847b59 + checksum: a7ab9c2d98c4e00f7ac8aab8f08e8f0472d026573c0790aa6036557de0dae5c6e89a3989e00937725badbefcfdfd4fa5e5a996e5e11a043a3d594b5956cfae2c languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.370.0": - version: 3.370.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.370.0" +"@aws-sdk/util-user-agent-node@npm:3.387.0": + version: 3.387.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.387.0" dependencies: - "@aws-sdk/types": 3.370.0 - "@smithy/node-config-provider": ^1.0.1 - "@smithy/types": ^1.1.0 + "@aws-sdk/types": 3.387.0 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 peerDependencies: aws-crt: ">=1.0.0" peerDependenciesMeta: aws-crt: optional: true - checksum: 83b1f2c7f25f44b6bd3aed8e7b9fe3a68e077f3b8b3832648a38eb5985b95d0e5ac469242e1d21aa37e29a6c8b4f9cb2a1d60ea1b6e4a76c32225e58f98da504 + checksum: abe3cf2d058ff0a907780ee2100b0d5805d7878c434115cda645d2fe0a57226dd7cf86c2d795ef75d8062914d89c8d71752789b355317586417b92482e05c98f languageName: node linkType: hard @@ -1752,8 +1657,8 @@ __metadata: linkType: hard "@azure/identity@npm:^3.2.1": - version: 3.2.3 - resolution: "@azure/identity@npm:3.2.3" + version: 3.2.4 + resolution: "@azure/identity@npm:3.2.4" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-auth": ^1.3.0 @@ -1771,7 +1676,7 @@ __metadata: stoppable: ^1.1.0 tslib: ^2.2.0 uuid: ^8.3.0 - checksum: 8f74831a358f41490056fb1978d9214542f5e7a788cc607ded3c475e1e61784da2cb6409434cb736c66291c24b755dbab42ec7b93b218ca535dce3363183c64b + checksum: 71e33542742113c0c77f948fac3c1554cc8418cf0063f3d209bc357c021b49a42a5e0fef1e8b946fbf250feefa705727275b936ac7d8efad24f79c01abddb740 languageName: node linkType: hard @@ -1840,8 +1745,8 @@ __metadata: linkType: hard "@azure/storage-blob@npm:^12.5.0": - version: 12.14.0 - resolution: "@azure/storage-blob@npm:12.14.0" + version: 12.15.0 + resolution: "@azure/storage-blob@npm:12.15.0" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-http": ^3.0.0 @@ -1851,7 +1756,7 @@ __metadata: "@azure/logger": ^1.0.0 events: ^3.0.0 tslib: ^2.2.0 - checksum: d087afd09a68342f7dcc655bf8861ffbcf84caddbfeee9ad226491a5b890fe0721a51d7221d0fc514b205a50ac153982803d89958640d2237a431b5f6b47dd6c + checksum: fe5399e7107685f1e81bd782fbd10e11c6aec01141c63f24f138a985aa709da96e83fc5dd3295408b0609981b2fb71d2304935056692e4cf3833635157799769 languageName: node linkType: hard @@ -3252,13 +3157,13 @@ __metadata: languageName: node linkType: hard -"@babel/runtime-corejs3@npm:^7.20.13, @babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/runtime-corejs3@npm:7.22.5" +"@babel/runtime-corejs3@npm:^7.20.13, @babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.6": + version: 7.22.10 + resolution: "@babel/runtime-corejs3@npm:7.22.10" dependencies: core-js-pure: ^3.30.2 - regenerator-runtime: ^0.13.11 - checksum: cdeabaa6858cedb0ec47c1245195a09a8fd2de06f4545614acb574d150a81d0e27eb9c08d69787b2c1ad4a1fc57919a3f0599f60d14914227c200563cd595503 + regenerator-runtime: ^0.14.0 + checksum: 00c50bf21fa7d0db1af7944f4fd56569da0dddedd68dbbc18587eedbd0e04aa4efea7010ff4a44ba590ea7ff3c91f0edf87136020c930de14dc4cdec511b6d22 languageName: node linkType: hard @@ -3345,6 +3250,7 @@ __metadata: "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" + "@backstage/cli-node": "workspace:^" "@backstage/config": "workspace:^" "@backstage/config-loader": "workspace:^" "@backstage/errors": "workspace:^" @@ -3372,6 +3278,7 @@ __metadata: logform: ^2.3.2 minimatch: ^5.0.0 minimist: ^1.2.5 + mock-fs: ^5.2.0 morgan: ^1.10.0 node-forge: ^1.3.1 selfsigned: ^2.0.0 @@ -3580,7 +3487,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-client@^1.4.3, @backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": +"@backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": version: 0.0.0-use.local resolution: "@backstage/catalog-client@workspace:packages/catalog-client" dependencies: @@ -3592,7 +3499,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-model@^1.1.5, @backstage/catalog-model@^1.4.0, @backstage/catalog-model@^1.4.1, @backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": +"@backstage/catalog-model@^1.1.5, @backstage/catalog-model@^1.4.1, @backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": version: 0.0.0-use.local resolution: "@backstage/catalog-model@workspace:packages/catalog-model" dependencies: @@ -3713,7 +3620,7 @@ __metadata: css-loader: ^6.5.1 del: ^7.0.0 diff: ^5.0.0 - esbuild: ^0.18.0 + esbuild: ^0.19.0 esbuild-loader: ^2.18.0 eslint: ^8.6.0 eslint-config-prettier: ^8.3.0 @@ -3746,7 +3653,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.6.7 node-libs-browser: ^2.2.1 - nodemon: ^2.0.2 + nodemon: ^3.0.1 npm-packlist: ^5.0.0 ora: ^5.3.0 postcss: ^8.1.0 @@ -3838,7 +3745,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/config@^1.0.6, @backstage/config@^1.0.7, @backstage/config@^1.0.8, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": +"@backstage/config@^1.0.6, @backstage/config@^1.0.7, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": version: 0.0.0-use.local resolution: "@backstage/config@workspace:packages/config" dependencies: @@ -3886,110 +3793,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@npm:^0.12.3": - version: 0.12.5 - resolution: "@backstage/core-components@npm:0.12.5" - dependencies: - "@backstage/config": ^1.0.7 - "@backstage/core-plugin-api": ^1.5.0 - "@backstage/errors": ^1.1.5 - "@backstage/theme": ^0.2.18 - "@backstage/version-bridge": ^1.0.3 - "@material-table/core": ^3.1.0 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.57 - "@react-hookz/web": ^20.0.0 - "@types/react-sparklines": ^1.7.0 - "@types/react-text-truncate": ^0.14.0 - ansi-regex: ^6.0.1 - classnames: ^2.2.6 - d3-selection: ^3.0.0 - d3-shape: ^3.0.0 - d3-zoom: ^3.0.0 - dagre: ^0.8.5 - history: ^5.0.0 - immer: ^9.0.1 - lodash: ^4.17.21 - pluralize: ^8.0.0 - prop-types: ^15.7.2 - qs: ^6.9.4 - rc-progress: 3.4.1 - react-helmet: 6.1.0 - react-hook-form: ^7.12.2 - react-markdown: ^8.0.0 - react-sparklines: ^1.7.0 - react-syntax-highlighter: ^15.4.5 - react-text-truncate: ^0.19.0 - react-use: ^17.3.2 - react-virtualized-auto-sizer: ^1.0.6 - react-window: ^1.8.6 - remark-gfm: ^3.0.1 - zen-observable: ^0.10.0 - zod: ~3.18.0 - peerDependencies: - "@types/react": ^16.13.1 || ^17.0.0 - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 8308c1b90247911f6cf22963b530cef169287f508ff29d159d0c83758134dd43bd5acd2113350690a46a02246bc05dca975bcc38325000aefb1c93c80e2f19c7 - languageName: node - linkType: hard - -"@backstage/core-components@npm:^0.13.2, @backstage/core-components@npm:^0.13.3": - version: 0.13.3 - resolution: "@backstage/core-components@npm:0.13.3" - dependencies: - "@backstage/config": ^1.0.8 - "@backstage/core-plugin-api": ^1.5.3 - "@backstage/errors": ^1.2.1 - "@backstage/theme": ^0.4.1 - "@backstage/version-bridge": ^1.0.4 - "@date-io/core": ^1.3.13 - "@material-table/core": ^3.1.0 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 - "@types/react": ^16.13.1 || ^17.0.0 - "@types/react-sparklines": ^1.7.0 - "@types/react-text-truncate": ^0.14.0 - ansi-regex: ^6.0.1 - classnames: ^2.2.6 - d3-selection: ^3.0.0 - d3-shape: ^3.0.0 - d3-zoom: ^3.0.0 - dagre: ^0.8.5 - history: ^5.0.0 - immer: ^9.0.1 - linkify-react: 4.1.1 - linkifyjs: 4.1.1 - lodash: ^4.17.21 - pluralize: ^8.0.0 - prop-types: ^15.7.2 - qs: ^6.9.4 - rc-progress: 3.4.2 - react-helmet: 6.1.0 - react-hook-form: ^7.12.2 - react-markdown: ^8.0.0 - react-sparklines: ^1.7.0 - react-syntax-highlighter: ^15.4.5 - react-text-truncate: ^0.19.0 - react-use: ^17.3.2 - react-virtualized-auto-sizer: ^1.0.11 - react-window: ^1.8.6 - remark-gfm: ^3.0.1 - zen-observable: ^0.10.0 - zod: ^3.21.4 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: ef1010b048697c051654d52a88f0e22d2a82bad33bb14c0ee5e11e86930ccfca71dc0cf0946440cc92e439d4b3a97281fbe6e1c1f78553dbd03f1118e2c2ad6a - languageName: node - linkType: hard - -"@backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": +"@backstage/core-components@^0.13.3, @backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": version: 0.0.0-use.local resolution: "@backstage/core-components@workspace:packages/core-components" dependencies: @@ -4063,7 +3867,57 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-plugin-api@^1.3.0, @backstage/core-plugin-api@^1.5.0, @backstage/core-plugin-api@^1.5.2, @backstage/core-plugin-api@^1.5.3, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": +"@backstage/core-components@npm:^0.12.3": + version: 0.12.5 + resolution: "@backstage/core-components@npm:0.12.5" + dependencies: + "@backstage/config": ^1.0.7 + "@backstage/core-plugin-api": ^1.5.0 + "@backstage/errors": ^1.1.5 + "@backstage/theme": ^0.2.18 + "@backstage/version-bridge": ^1.0.3 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.57 + "@react-hookz/web": ^20.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + history: ^5.0.0 + immer: ^9.0.1 + lodash: ^4.17.21 + pluralize: ^8.0.0 + prop-types: ^15.7.2 + qs: ^6.9.4 + rc-progress: 3.4.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.6 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ~3.18.0 + peerDependencies: + "@types/react": ^16.13.1 || ^17.0.0 + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 8308c1b90247911f6cf22963b530cef169287f508ff29d159d0c83758134dd43bd5acd2113350690a46a02246bc05dca975bcc38325000aefb1c93c80e2f19c7 + languageName: node + linkType: hard + +"@backstage/core-plugin-api@^1.3.0, @backstage/core-plugin-api@^1.5.0, @backstage/core-plugin-api@^1.5.3, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": version: 0.0.0-use.local resolution: "@backstage/core-plugin-api@workspace:packages/core-plugin-api" dependencies: @@ -4110,7 +3964,7 @@ __metadata: handlebars: ^4.7.3 inquirer: ^8.2.0 mock-fs: ^5.1.1 - nodemon: ^2.0.2 + nodemon: ^3.0.1 ora: ^5.3.0 recursive-readdir: ^2.2.2 ts-node: ^10.0.0 @@ -4150,7 +4004,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/errors@^1.1.5, @backstage/errors@^1.2.1, @backstage/errors@workspace:^, @backstage/errors@workspace:packages/errors": +"@backstage/errors@^1.1.5, @backstage/errors@workspace:^, @backstage/errors@workspace:packages/errors": version: 0.0.0-use.local resolution: "@backstage/errors@workspace:packages/errors" dependencies: @@ -4191,28 +4045,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@npm:^1.1.15": - version: 1.1.15 - resolution: "@backstage/integration-react@npm:1.1.15" - dependencies: - "@backstage/config": ^1.0.8 - "@backstage/core-components": ^0.13.3 - "@backstage/core-plugin-api": ^1.5.3 - "@backstage/integration": ^1.5.1 - "@backstage/theme": ^0.4.1 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - react-use: ^17.2.4 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 3fed97a63e3bc4c5faf128373def1b31d62952f088ae19feb7ee1c68aa5092215ab047b66cb4b38caa022a26370eac244fb2b6ddb47ba896e2c3fce06873f575 - languageName: node - linkType: hard - -"@backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": +"@backstage/integration-react@^1.1.15, @backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": version: 0.0.0-use.local resolution: "@backstage/integration-react@workspace:packages/integration-react" dependencies: @@ -4242,7 +4075,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration@^1.5.1, @backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": +"@backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": version: 0.0.0-use.local resolution: "@backstage/integration@workspace:packages/integration" dependencies: @@ -4637,6 +4470,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/config-loader": "workspace:^" + "@backstage/plugin-app-node": "workspace:^" "@backstage/types": "workspace:^" "@types/express": ^4.17.6 "@types/supertest": ^2.0.8 @@ -4657,17 +4491,64 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-app-node@workspace:^, @backstage/plugin-app-node@workspace:plugins/app-node": + version: 0.0.0-use.local + resolution: "@backstage/plugin-app-node@workspace:plugins/app-node" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/cli": "workspace:^" + "@types/express": ^4.17.6 + express: ^4.17.1 + languageName: unknown + linkType: soft + +"@backstage/plugin-auth-backend-module-gcp-iap-provider@workspace:^, @backstage/plugin-auth-backend-module-gcp-iap-provider@workspace:plugins/auth-backend-module-gcp-iap-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-gcp-iap-provider@workspace:plugins/auth-backend-module-gcp-iap-provider" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + "@backstage/types": "workspace:^" + express: ^4.18.2 + google-auth-library: ^8.0.0 + msw: ^1.0.0 + supertest: ^6.1.3 + languageName: unknown + linkType: soft + +"@backstage/plugin-auth-backend-module-google-provider@workspace:^, @backstage/plugin-auth-backend-module-google-provider@workspace:plugins/auth-backend-module-google-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-google-provider@workspace:plugins/auth-backend-module-google-provider" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + "@types/passport-google-oauth20": ^2.0.3 + google-auth-library: ^8.0.0 + msw: ^1.0.0 + passport-google-oauth20: ^2.0.0 + supertest: ^6.1.3 + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend@workspace:^, @backstage/plugin-auth-backend@workspace:plugins/auth-backend": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend@workspace:plugins/auth-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/plugin-auth-backend-module-gcp-iap-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-google-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/types": "workspace:^" "@davidzemon/passport-okta-oauth": ^0.0.5 @@ -4727,18 +4608,29 @@ __metadata: resolution: "@backstage/plugin-auth-node@workspace:plugins/auth-node" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-client": "workspace:^" + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/types": "workspace:^" "@types/express": "*" + "@types/passport": ^1.0.3 + cookie-parser: ^1.4.6 express: ^4.17.1 + express-promise-router: ^4.1.1 jose: ^4.6.0 lodash: ^4.17.21 msw: ^1.0.0 node-fetch: ^2.6.7 + passport: ^0.6.0 + supertest: ^6.1.3 uuid: ^8.0.0 winston: ^3.2.1 + zod: ^3.21.4 + zod-to-json-schema: ^3.21.4 languageName: unknown linkType: soft @@ -5457,7 +5349,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-common@^1.0.10, @backstage/plugin-catalog-common@^1.0.15, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": +"@backstage/plugin-catalog-common@^1.0.10, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-common@workspace:plugins/catalog-common" dependencies: @@ -5594,44 +5486,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-react@npm:^1.2.4, @backstage/plugin-catalog-react@npm:^1.7.0, @backstage/plugin-catalog-react@npm:^1.8.0": - version: 1.8.0 - resolution: "@backstage/plugin-catalog-react@npm:1.8.0" - dependencies: - "@backstage/catalog-client": ^1.4.3 - "@backstage/catalog-model": ^1.4.1 - "@backstage/core-components": ^0.13.3 - "@backstage/core-plugin-api": ^1.5.3 - "@backstage/errors": ^1.2.1 - "@backstage/integration": ^1.5.1 - "@backstage/plugin-catalog-common": ^1.0.15 - "@backstage/plugin-permission-common": ^0.7.7 - "@backstage/plugin-permission-react": ^0.4.14 - "@backstage/theme": ^0.4.1 - "@backstage/types": ^1.1.0 - "@backstage/version-bridge": ^1.0.4 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^23.0.0 - "@types/react": ^16.13.1 || ^17.0.0 - classnames: ^2.2.6 - jwt-decode: ^3.1.0 - lodash: ^4.17.21 - material-ui-popup-state: ^1.9.3 - qs: ^6.9.4 - react-use: ^17.2.4 - yaml: ^2.0.0 - zen-observable: ^0.10.0 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 01dff91076bcc7829b10866975599a05991dd12b9c8f5978383a58dbbb2a4a0ef3b4558b6f8cc468260dc9eef594f8ebdba3cf9cbf776529ca1fd24b6460bb8e - languageName: node - linkType: hard - -"@backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": +"@backstage/plugin-catalog-react@^1.2.4, @backstage/plugin-catalog-react@^1.8.0, @backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-react@workspace:plugins/catalog-react" dependencies: @@ -6069,7 +5924,7 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/node": ^16.11.26 - "@types/pluralize": ^0.0.29 + "@types/pluralize": ^0.0.30 "@types/react": ^16.13.1 || ^17.0.0 "@types/recharts": ^1.8.14 "@types/regression": ^2.0.0 @@ -7015,23 +6870,6 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home-react@npm:^0.1.1": - version: 0.1.1 - resolution: "@backstage/plugin-home-react@npm:0.1.1" - dependencies: - "@backstage/core-components": ^0.13.3 - "@backstage/core-plugin-api": ^1.5.3 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@rjsf/utils": 5.7.3 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: cc9741b1a3b3e1943d849d5437d0254b3fbfebf3a700ba1c282ccb158aa1f23761ba021330318c79fe0fd153c2ca088e04d8355a2fd4bdaa8731e667496dd33c - languageName: node - linkType: hard - "@backstage/plugin-home-react@workspace:^, @backstage/plugin-home-react@workspace:plugins/home-react": version: 0.0.0-use.local resolution: "@backstage/plugin-home-react@workspace:plugins/home-react" @@ -7061,39 +6899,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home@npm:^0.5.3": - version: 0.5.4 - resolution: "@backstage/plugin-home@npm:0.5.4" - dependencies: - "@backstage/catalog-model": ^1.4.1 - "@backstage/config": ^1.0.8 - "@backstage/core-components": ^0.13.3 - "@backstage/core-plugin-api": ^1.5.3 - "@backstage/plugin-catalog-react": ^1.8.0 - "@backstage/plugin-home-react": ^0.1.1 - "@backstage/theme": ^0.4.1 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@rjsf/core-v5": "npm:@rjsf/core@5.7.3" - "@rjsf/material-ui-v5": "npm:@rjsf/material-ui@5.7.3" - "@rjsf/utils": 5.7.3 - "@rjsf/validator-ajv8": 5.7.3 - "@types/react": ^16.13.1 || ^17.0.0 - lodash: ^4.17.21 - react-grid-layout: ^1.3.4 - react-resizable: ^3.0.4 - react-use: ^17.2.4 - zod: ~3.21.4 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: cf27862ba80f166691880b46c942726515695537ecb61eb4996109fdf32d2797ae47e25a0342f4cf1e4f1403262456dc5b6e190cd0ef28c80f188fd34aaa7196 - languageName: node - linkType: hard - -"@backstage/plugin-home@workspace:^, @backstage/plugin-home@workspace:plugins/home": +"@backstage/plugin-home@^0.5.4, @backstage/plugin-home@workspace:^, @backstage/plugin-home@workspace:plugins/home": version: 0.0.0-use.local resolution: "@backstage/plugin-home@workspace:plugins/home" dependencies: @@ -7483,6 +7289,7 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-linguist-common": "workspace:^" "@backstage/types": "workspace:^" "@types/express": "*" @@ -7490,6 +7297,7 @@ __metadata: express: ^4.18.1 express-promise-router: ^4.1.0 fs-extra: ^10.0.0 + js-yaml: ^4.1.0 knex: ^2.0.0 linguist-js: ^2.5.3 luxon: ^2.0.2 @@ -7609,6 +7417,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-newrelic@workspace:plugins/newrelic" dependencies: + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" @@ -7624,9 +7433,11 @@ __metadata: "@testing-library/react": ^12.1.3 "@testing-library/user-event": ^14.0.0 "@types/node": ^16.11.26 + "@types/parse-link-header": ^2.0.1 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 - msw: ^1.0.0 + msw: ^1.2.3 + parse-link-header: ^2.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 @@ -7718,6 +7529,39 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-opencost@workspace:plugins/opencost": + version: 0.0.0-use.local + resolution: "@backstage/plugin-opencost@workspace:plugins/opencost" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@date-io/luxon": 1.x + "@material-ui/core": ^4.9.13 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": ^4.0.0-alpha.60 + "@material-ui/pickers": ^3.3.10 + "@material-ui/styles": ^4.11.5 + "@testing-library/jest-dom": ^5.10.1 + "@testing-library/react": ^12.1.3 + "@testing-library/user-event": ^14.0.0 + axios: ^1.4.0 + cross-fetch: ^3.1.5 + date-fns: ^2.30.0 + lodash: ^4.17.21 + msw: ^1.0.0 + react-use: ^17.2.4 + recharts: ^2.5.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-router-dom: "*" + languageName: unknown + linkType: soft + "@backstage/plugin-org-react@workspace:plugins/org-react": version: 0.0.0-use.local resolution: "@backstage/plugin-org-react@workspace:plugins/org-react" @@ -7905,7 +7749,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-common@^0.7.7, @backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": +"@backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-common@workspace:plugins/permission-common" dependencies: @@ -7943,7 +7787,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-react@^0.4.14, @backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": +"@backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-react@workspace:plugins/permission-react" dependencies: @@ -8053,8 +7897,10 @@ __metadata: dependencies: "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/config-loader": "workspace:^" "@types/express": ^4.17.6 "@types/http-proxy-middleware": ^0.19.3 "@types/supertest": ^2.0.8 @@ -8175,7 +8021,6 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/types": "workspace:^" fs-extra: 10.1.0 @@ -8197,7 +8042,6 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/types": "workspace:^" "@types/command-exists": ^1.2.0 @@ -8237,7 +8081,6 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/types": "workspace:^" "@types/command-exists": ^1.2.0 @@ -8316,7 +8159,7 @@ __metadata: command-exists: ^1.2.9 compression: ^1.7.4 cors: ^2.8.5 - esbuild: ^0.18.0 + esbuild: ^0.19.0 express: ^4.17.1 express-promise-router: ^4.1.0 fs-extra: 10.1.0 @@ -8366,11 +8209,16 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-node@workspace:plugins/scaffolder-node" dependencies: + "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/types": "workspace:^" + fs-extra: 10.1.0 jsonschema: ^1.2.6 winston: ^3.2.1 zod: ^3.21.4 @@ -8516,7 +8364,9 @@ __metadata: "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" @@ -8597,6 +8447,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" @@ -9607,14 +9458,15 @@ __metadata: dependencies: "@apidevtools/swagger-parser": ^10.1.0 "@apisyouwonthate/style-guide": ^1.4.0 + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" "@backstage/cli-node": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/types": "workspace:^" "@manypkg/get-packages": ^1.1.3 - "@microsoft/api-documenter": ^7.19.27 - "@microsoft/api-extractor": ^7.33.7 + "@microsoft/api-documenter": ^7.22.33 + "@microsoft/api-extractor": ^7.36.4 "@stoplight/spectral-core": ^1.18.0 "@stoplight/spectral-formatters": ^1.1.0 "@stoplight/spectral-functions": ^1.7.2 @@ -9626,6 +9478,7 @@ __metadata: "@types/mock-fs": ^4.13.0 "@types/node": ^16.11.26 chalk: ^4.0.0 + codeowners-utils: ^1.0.2 commander: ^9.1.0 fs-extra: 10.1.0 glob: ^8.0.3 @@ -9636,6 +9489,7 @@ __metadata: mock-fs: ^5.1.0 p-limit: ^3.0.2 ts-node: ^10.0.0 + yaml-diff-patch: ^2.0.0 peerDependencies: "@microsoft/api-extractor-model": "*" "@microsoft/tsdoc": "*" @@ -9709,7 +9563,7 @@ __metadata: languageName: node linkType: hard -"@backstage/types@^1.0.2, @backstage/types@^1.1.0, @backstage/types@workspace:^, @backstage/types@workspace:packages/types": +"@backstage/types@^1.0.2, @backstage/types@workspace:^, @backstage/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@backstage/types@workspace:packages/types" dependencies: @@ -9720,7 +9574,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/version-bridge@^1.0.3, @backstage/version-bridge@^1.0.4, @backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": +"@backstage/version-bridge@^1.0.3, @backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": version: 0.0.0-use.local resolution: "@backstage/version-bridge@workspace:packages/version-bridge" dependencies: @@ -10087,13 +9941,13 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0": - version: 6.15.3 - resolution: "@codemirror/view@npm:6.15.3" + version: 6.16.0 + resolution: "@codemirror/view@npm:6.16.0" dependencies: "@codemirror/state": ^6.1.4 style-mod: ^4.0.0 w3c-keyname: ^2.2.4 - checksum: 048949b1b493a962904a7f77661a939f7c1893a7381022756a135f5dd8daf667f498be1b81da9c37c0e8de85b078ad987c2f75318385c520ed83c95da6313e95 + checksum: 54d412b5159716c8a1a9c46fa04ff083e68a663cb887e6e2a4ca86fe9c3930d5255200fe84c65620e0a442f62dc2c13df277bcd1d4eef2e11e3c4e124fcf9d38 languageName: node linkType: hard @@ -10434,9 +10288,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-arm64@npm:0.18.17" +"@esbuild/android-arm64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/android-arm64@npm:0.19.2" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -10455,9 +10309,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-arm@npm:0.18.17" +"@esbuild/android-arm@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/android-arm@npm:0.19.2" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -10469,9 +10323,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-x64@npm:0.18.17" +"@esbuild/android-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/android-x64@npm:0.19.2" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -10483,9 +10337,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/darwin-arm64@npm:0.18.17" +"@esbuild/darwin-arm64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/darwin-arm64@npm:0.19.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -10497,9 +10351,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/darwin-x64@npm:0.18.17" +"@esbuild/darwin-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/darwin-x64@npm:0.19.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -10511,9 +10365,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/freebsd-arm64@npm:0.18.17" +"@esbuild/freebsd-arm64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/freebsd-arm64@npm:0.19.2" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -10525,9 +10379,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/freebsd-x64@npm:0.18.17" +"@esbuild/freebsd-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/freebsd-x64@npm:0.19.2" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -10539,9 +10393,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-arm64@npm:0.18.17" +"@esbuild/linux-arm64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-arm64@npm:0.19.2" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -10553,9 +10407,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-arm@npm:0.18.17" +"@esbuild/linux-arm@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-arm@npm:0.19.2" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -10567,9 +10421,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-ia32@npm:0.18.17" +"@esbuild/linux-ia32@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-ia32@npm:0.19.2" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -10588,9 +10442,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-loong64@npm:0.18.17" +"@esbuild/linux-loong64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-loong64@npm:0.19.2" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -10602,9 +10456,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-mips64el@npm:0.18.17" +"@esbuild/linux-mips64el@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-mips64el@npm:0.19.2" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -10616,9 +10470,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-ppc64@npm:0.18.17" +"@esbuild/linux-ppc64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-ppc64@npm:0.19.2" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -10630,9 +10484,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-riscv64@npm:0.18.17" +"@esbuild/linux-riscv64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-riscv64@npm:0.19.2" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -10644,9 +10498,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-s390x@npm:0.18.17" +"@esbuild/linux-s390x@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-s390x@npm:0.19.2" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -10658,9 +10512,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-x64@npm:0.18.17" +"@esbuild/linux-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/linux-x64@npm:0.19.2" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -10672,9 +10526,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/netbsd-x64@npm:0.18.17" +"@esbuild/netbsd-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/netbsd-x64@npm:0.19.2" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -10686,9 +10540,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/openbsd-x64@npm:0.18.17" +"@esbuild/openbsd-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/openbsd-x64@npm:0.19.2" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -10700,9 +10554,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/sunos-x64@npm:0.18.17" +"@esbuild/sunos-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/sunos-x64@npm:0.19.2" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -10714,9 +10568,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-arm64@npm:0.18.17" +"@esbuild/win32-arm64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/win32-arm64@npm:0.19.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -10728,9 +10582,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-ia32@npm:0.18.17" +"@esbuild/win32-ia32@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/win32-ia32@npm:0.19.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -10742,9 +10596,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-x64@npm:0.18.17" +"@esbuild/win32-x64@npm:0.19.2": + version: 0.19.2 + resolution: "@esbuild/win32-x64@npm:0.19.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -10760,16 +10614,16 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.4.0": - version: 4.5.0 - resolution: "@eslint-community/regexpp@npm:4.5.0" - checksum: 99c01335947dbd7f2129e954413067e217ccaa4e219fe0917b7d2bd96135789384b8fedbfb8eb09584d5130b27a7b876a7150ab7376f51b3a0c377d5ce026a10 +"@eslint-community/regexpp@npm:^4.6.1": + version: 4.6.2 + resolution: "@eslint-community/regexpp@npm:4.6.2" + checksum: a3c341377b46b54fa228f455771b901d1a2717f95d47dcdf40199df30abc000ba020f747f114f08560d119e979d882a94cf46cfc51744544d54b00319c0f2724 languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.0": - version: 2.1.0 - resolution: "@eslint/eslintrc@npm:2.1.0" +"@eslint/eslintrc@npm:^2.1.1": + version: 2.1.1 + resolution: "@eslint/eslintrc@npm:2.1.1" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -10780,14 +10634,14 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: d5ed0adbe23f6571d8c9bb0ca6edf7618dc6aed4046aa56df7139f65ae7b578874e0d9c796df784c25bda648ceb754b6320277d828c8b004876d7443b8dc018c + checksum: bf909ea183d27238c257a82d4ffdec38ca94b906b4b8dfae02ecbe7ecc9e5a8182ef5e469c808bb8cb4fea4750f43ac4ca7c4b4a167b6cd7e3aaacd386b2bd25 languageName: node linkType: hard -"@eslint/js@npm:8.44.0": - version: 8.44.0 - resolution: "@eslint/js@npm:8.44.0" - checksum: fc539583226a28f5677356e9f00d2789c34253f076643d2e32888250e509a4e13aafe0880cb2425139051de0f3a48d25bfc5afa96b7304f203b706c17340e3cf +"@eslint/js@npm:^8.46.0": + version: 8.46.0 + resolution: "@eslint/js@npm:8.46.0" + checksum: 7aed479832302882faf5bec37e9d068f270f84c19b3fb529646a7c1b031e73a312f730569c78806492bc09cfce3d7651dfab4ce09a56cbb06bc6469449e56377 languageName: node linkType: hard @@ -10932,23 +10786,23 @@ __metadata: linkType: hard "@google-cloud/container@npm:^4.0.0, @google-cloud/container@npm:^4.15.0": - version: 4.15.0 - resolution: "@google-cloud/container@npm:4.15.0" + version: 4.16.0 + resolution: "@google-cloud/container@npm:4.16.0" dependencies: google-gax: ^3.5.8 - checksum: e81f1b708ab44c55b8e998d34c442baed10a113828ef51b4a84b806a2b04a10a0dc40ee0aa202386ac6158b34b9d3f03467d7008da9e4f709fdb1f021e725b8c + checksum: 324d31a45e21ce8e4734a861d5abc5a3ed8f5d0a4825db37ea4a8d6ae6658b395f5e70a29b02fe8964cbf9bd6a00387652b55a670113b941394a9dc54d34ec52 languageName: node linkType: hard "@google-cloud/firestore@npm:^6.0.0": - version: 6.6.1 - resolution: "@google-cloud/firestore@npm:6.6.1" + version: 6.7.0 + resolution: "@google-cloud/firestore@npm:6.7.0" dependencies: fast-deep-equal: ^3.1.1 functional-red-black-tree: ^1.0.1 google-gax: ^3.5.7 protobufjs: ^7.0.0 - checksum: 9f3838e07d74da34ba4cf322efd3f82e3b8f5b330ae01c0e51d717aacafe2a84caa2869989bbff87fe020086f5b8591baaaf8c643dc93c20fd3f6f2adfd67a24 + checksum: 8464d4d866adcbd80cd528230408f7161a402df7b74d4036b2f81c1f9b83051057364e1c8264477b7a46ce03c2b8fa0d28799f6ccd77d905274a71ca93b2593f languageName: node linkType: hard @@ -10977,8 +10831,8 @@ __metadata: linkType: hard "@google-cloud/storage@npm:^6.0.0": - version: 6.11.0 - resolution: "@google-cloud/storage@npm:6.11.0" + version: 6.12.0 + resolution: "@google-cloud/storage@npm:6.12.0" dependencies: "@google-cloud/paginator": ^3.0.7 "@google-cloud/projectify": ^3.0.0 @@ -10989,6 +10843,7 @@ __metadata: duplexify: ^4.0.0 ent: ^2.2.0 extend: ^3.0.2 + fast-xml-parser: ^4.2.2 gaxios: ^5.0.0 google-auth-library: ^8.0.1 mime: ^3.0.0 @@ -10997,7 +10852,7 @@ __metadata: retry-request: ^5.0.0 teeny-request: ^8.0.0 uuid: ^8.0.0 - checksum: 4183c0ad3b271e0c22b3aa01e9942590ae64b984efa142a787b96379baaf4c05a2ebdbb6015c8ea189951df251d57f4f35c6a28b920922828cc89d3e4147d1a1 + checksum: cfe44e3f4d1bacd8eeefa7885d261f421c4ff84e82abe50200b5b77e28322baf9cb67497872b9868b25b43b14197b1a155d5eb7b70afb39d3476fa4bdead3338 languageName: node linkType: hard @@ -11222,17 +11077,17 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/batch-execute@npm:^8.5.18": - version: 8.5.18 - resolution: "@graphql-tools/batch-execute@npm:8.5.18" +"@graphql-tools/batch-execute@npm:^9.0.1": + version: 9.0.1 + resolution: "@graphql-tools/batch-execute@npm:9.0.1" dependencies: - "@graphql-tools/utils": 9.2.1 - dataloader: 2.2.2 + "@graphql-tools/utils": ^10.0.5 + dataloader: ^2.2.2 tslib: ^2.4.0 - value-or-promise: 1.0.12 + value-or-promise: ^1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 4a5b5dec502a91bd657a4ddad74965d050d3994c89f036225fac58657b5a275bb374224b20484e3ba773525e75a5552c4a5af7b6d52859ce3a3b32aec2412b6e + checksum: c2bcf9dbcd3d08b2e04708fddad7f587677b62e189b0357b8a5495693e17b257b3022f5983d503966baaf32cbdc92a0a00dc95be7f5b6039357a31ffdde29a19 languageName: node linkType: hard @@ -11268,20 +11123,19 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/delegate@npm:9.0.28": - version: 9.0.28 - resolution: "@graphql-tools/delegate@npm:9.0.28" +"@graphql-tools/delegate@npm:^10.0.0": + version: 10.0.2 + resolution: "@graphql-tools/delegate@npm:10.0.2" dependencies: - "@graphql-tools/batch-execute": ^8.5.18 - "@graphql-tools/executor": ^0.0.15 - "@graphql-tools/schema": ^9.0.16 - "@graphql-tools/utils": ^9.2.1 + "@graphql-tools/batch-execute": ^9.0.1 + "@graphql-tools/executor": ^1.0.0 + "@graphql-tools/schema": ^10.0.0 + "@graphql-tools/utils": ^10.0.5 dataloader: ^2.2.2 tslib: ^2.5.0 - value-or-promise: ^1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: e24c757ea15a2b654268194bbf76188fca6692486d1b019b0ad7d48c264f92b7b681a43c8a8355ace48a29f9dd566c20e8707b6cef281331a813f23ee1c94375 + checksum: 3641dd03b241234cc0bc15221e1bd7e82f003f0bdb59a04cfaba23b5dc43d3aed1f068cc79d526dd731f6e4acfd90c9e20287feb565b9c881ed441c5008e32db languageName: node linkType: hard @@ -11350,18 +11204,18 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/executor@npm:^0.0.15": - version: 0.0.15 - resolution: "@graphql-tools/executor@npm:0.0.15" +"@graphql-tools/executor@npm:^1.0.0": + version: 1.2.0 + resolution: "@graphql-tools/executor@npm:1.2.0" dependencies: - "@graphql-tools/utils": 9.2.1 - "@graphql-typed-document-node/core": 3.1.2 - "@repeaterjs/repeater": 3.0.4 + "@graphql-tools/utils": ^10.0.0 + "@graphql-typed-document-node/core": 3.2.0 + "@repeaterjs/repeater": ^3.0.4 tslib: ^2.4.0 - value-or-promise: 1.0.12 + value-or-promise: ^1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 7a963d6fcd00bd9c2a42f35a33bc9178576a1eeac755ce69981aa1e2c61238953b7ab9c11ffcfc86e49fdcf4fc057698c6f7169d6be580298e286076276b1295 + checksum: a6fe10636e433155a8fb76f1be76f4f55e7e54c7473a14c546a7b558819e92b3658f33f1cb646b9523a22d692b95f2cbd5a8e628f4f263fa48f229ddf98a6850 languageName: node linkType: hard @@ -11508,18 +11362,6 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/merge@npm:8.4.0": - version: 8.4.0 - resolution: "@graphql-tools/merge@npm:8.4.0" - dependencies: - "@graphql-tools/utils": 9.2.1 - tslib: ^2.4.0 - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 32265749833615ac2cb3d958318f5c46b7bd5ec858acfbad7136d379594ec3c98ba67ba5f04f4061187e5dfd52bb277155cd98fdeb2b4c5535c16bdb4f117ae0 - languageName: node - linkType: hard - "@graphql-tools/merge@npm:^8.2.6, @graphql-tools/merge@npm:^8.4.1": version: 8.4.1 resolution: "@graphql-tools/merge@npm:8.4.1" @@ -11532,6 +11374,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/merge@npm:^9.0.0": + version: 9.0.0 + resolution: "@graphql-tools/merge@npm:9.0.0" + dependencies: + "@graphql-tools/utils": ^10.0.0 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: e93794260faa477979bdc22f05da26c0eff02bd82c11565a1197152b70b5ab1c73dc6f8a05caf06737bcb18cb244bd19ee99d62bfc41af3bffb3c17eddb70edf + languageName: node + linkType: hard + "@graphql-tools/optimize@npm:^1.3.0": version: 1.3.0 resolution: "@graphql-tools/optimize@npm:1.3.0" @@ -11613,21 +11467,21 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/schema@npm:9.0.17": - version: 9.0.17 - resolution: "@graphql-tools/schema@npm:9.0.17" +"@graphql-tools/schema@npm:^10.0.0": + version: 10.0.0 + resolution: "@graphql-tools/schema@npm:10.0.0" dependencies: - "@graphql-tools/merge": 8.4.0 - "@graphql-tools/utils": 9.2.1 + "@graphql-tools/merge": ^9.0.0 + "@graphql-tools/utils": ^10.0.0 tslib: ^2.4.0 - value-or-promise: 1.0.12 + value-or-promise: ^1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 1c6513dd88b47d07702d01a48941ee164c4090c69b2475b1dde48a3d8866ed48fd39a33d15510682f6d8c18d19ceb72b77104eb4edbb194f96a129cc03909e89 + checksum: 550e9a4528584a4d108892f1553fb5b2590e63e88b9a9d3c1ad80b01c974ca9947adb9d1448a6969230d90c15dc96e8e84d62f32ef0fde804c389b43ac5bd739 languageName: node linkType: hard -"@graphql-tools/schema@npm:^9.0.0, @graphql-tools/schema@npm:^9.0.16": +"@graphql-tools/schema@npm:^9.0.0": version: 9.0.19 resolution: "@graphql-tools/schema@npm:9.0.19" dependencies: @@ -11731,6 +11585,19 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.0.5": + version: 10.0.5 + resolution: "@graphql-tools/utils@npm:10.0.5" + dependencies: + "@graphql-typed-document-node/core": ^3.1.1 + dset: ^3.1.2 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 600eb668b0a80264a56d1bfc2b431073a620de8ffea959362a1d13c4e0dd5738b627cc508ead8ecdbd7c1a2da05d5664e43443e15d3b221a809fa1a0e2740193 + languageName: node + linkType: hard + "@graphql-tools/wrap@npm:9.2.23": version: 9.2.23 resolution: "@graphql-tools/wrap@npm:9.2.23" @@ -11746,18 +11613,18 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/wrap@npm:^9.0.0": - version: 9.3.8 - resolution: "@graphql-tools/wrap@npm:9.3.8" +"@graphql-tools/wrap@npm:^10.0.0": + version: 10.0.0 + resolution: "@graphql-tools/wrap@npm:10.0.0" dependencies: - "@graphql-tools/delegate": 9.0.28 - "@graphql-tools/schema": 9.0.17 - "@graphql-tools/utils": 9.2.1 + "@graphql-tools/delegate": ^10.0.0 + "@graphql-tools/schema": ^10.0.0 + "@graphql-tools/utils": ^10.0.0 tslib: ^2.4.0 - value-or-promise: 1.0.12 + value-or-promise: ^1.0.12 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 1f280b939f1c8c33be8fbd92c0584e9125e30c59ce6f75f20657235d24e5a2c01eb3214eda9ae74d431d9da072cdfa6cedeafdc5b352dd33de439ba42402efcb + checksum: f7ce366cdd479c3f73c3c98f66fa13c528c63e2f791083e78ca14e010ebcd359b5c9bd78e2e8bd437a705418b87584eb6512f1eeb1971b81560c79c6fb3a4a6e languageName: node linkType: hard @@ -11770,12 +11637,12 @@ __metadata: languageName: node linkType: hard -"@graphql-typed-document-node/core@npm:3.1.2, @graphql-typed-document-node/core@npm:^3.1.0, @graphql-typed-document-node/core@npm:^3.1.1": - version: 3.1.2 - resolution: "@graphql-typed-document-node/core@npm:3.1.2" +"@graphql-typed-document-node/core@npm:3.2.0, @graphql-typed-document-node/core@npm:^3.1.0, @graphql-typed-document-node/core@npm:^3.1.1": + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm:3.2.0" peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: a61afa025acdabd7833e4f654a5802fc1a526171f81e0c435c8e651050a5a0682499a2c7a51304ceb61fde36cd69fc7975ce5e1b16b9ba7ea474c649f33eea8b + checksum: fa44443accd28c8cf4cb96aaaf39d144a22e8b091b13366843f4e97d19c7bfeaf609ce3c7603a4aeffe385081eaf8ea245d078633a7324c11c5ec4b2011bb76d languageName: node linkType: hard @@ -12878,60 +12745,60 @@ __metadata: languageName: node linkType: hard -"@microsoft/api-documenter@npm:^7.19.27": - version: 7.19.27 - resolution: "@microsoft/api-documenter@npm:7.19.27" +"@microsoft/api-documenter@npm:^7.22.33": + version: 7.22.33 + resolution: "@microsoft/api-documenter@npm:7.22.33" dependencies: - "@microsoft/api-extractor-model": 7.25.3 + "@microsoft/api-extractor-model": 7.27.6 "@microsoft/tsdoc": 0.14.2 - "@rushstack/node-core-library": 3.53.3 - "@rushstack/ts-command-line": 4.13.1 + "@rushstack/node-core-library": 3.59.7 + "@rushstack/ts-command-line": 4.15.2 colors: ~1.2.1 js-yaml: ~3.13.1 resolve: ~1.22.1 bin: api-documenter: bin/api-documenter - checksum: f243f473e1fe58e1cd6e32aad223724b6b621f569276415380abc4845859bf5c23fbe0e370be2a218f362a6c85b01b44b5427336f55800f2ea8bf3f398b0ee41 + checksum: b2aa75823c3a5bf65ce87b56bd85dfa276ff155eb2244187e163e4eb8f4a2daf15e39e9b5d1dea13f854309d9562ab845ff6c7c8b4973e409593852c3bc058f0 languageName: node linkType: hard -"@microsoft/api-extractor-model@npm:7.25.3": - version: 7.25.3 - resolution: "@microsoft/api-extractor-model@npm:7.25.3" +"@microsoft/api-extractor-model@npm:7.27.6": + version: 7.27.6 + resolution: "@microsoft/api-extractor-model@npm:7.27.6" dependencies: "@microsoft/tsdoc": 0.14.2 "@microsoft/tsdoc-config": ~0.16.1 - "@rushstack/node-core-library": 3.53.3 - checksum: 532ca30606b5649e90035ef70ff46868f6ccc63181e10b783bc5092e580bcb133112b300799b8f71a19da2b79f685a55f7ddbe84c8fe7ad93d71359c1763c521 + "@rushstack/node-core-library": 3.59.7 + checksum: 7867feaf3a0e5accfcce3a77681248a319952a266cffc644e4f8f7df1c9e1d55adb5124df901e8cca594bb3e12d361d1fcb2bffbdbb4b20fe3113928f6535975 languageName: node linkType: hard -"@microsoft/api-extractor@npm:^7.33.7": - version: 7.33.7 - resolution: "@microsoft/api-extractor@npm:7.33.7" +"@microsoft/api-extractor@npm:^7.36.4": + version: 7.36.4 + resolution: "@microsoft/api-extractor@npm:7.36.4" dependencies: - "@microsoft/api-extractor-model": 7.25.3 + "@microsoft/api-extractor-model": 7.27.6 "@microsoft/tsdoc": 0.14.2 "@microsoft/tsdoc-config": ~0.16.1 - "@rushstack/node-core-library": 3.53.3 - "@rushstack/rig-package": 0.3.17 - "@rushstack/ts-command-line": 4.13.1 + "@rushstack/node-core-library": 3.59.7 + "@rushstack/rig-package": 0.4.1 + "@rushstack/ts-command-line": 4.15.2 colors: ~1.2.1 lodash: ~4.17.15 - resolve: ~1.17.0 - semver: ~7.3.0 + resolve: ~1.22.1 + semver: ~7.5.4 source-map: ~0.6.1 - typescript: ~4.8.4 + typescript: ~5.0.4 bin: api-extractor: bin/api-extractor - checksum: 3f9034ca8e7bc7a6622cb8ac1f53f7f265ebca529e17f4abb2bef0ca297011cfa0927c0703a819607d974ca79b8f5307371491cfb192788e723cfd316250f9a6 + checksum: 92559325cf2407fa27cb9675772956511fa35005f295cdb4dc47abd7ef9c77ba61b0f684c2e952301a76dd2cfa9e398840c8f3d9117d621300e12b0ecfbf8147 languageName: node linkType: hard "@microsoft/microsoft-graph-types@npm:^2.25.0, @microsoft/microsoft-graph-types@npm:^2.6.0": - version: 2.35.0 - resolution: "@microsoft/microsoft-graph-types@npm:2.35.0" - checksum: d2053799a8b78f5743a64853727e3887101cbb6b26558b8a6e8501b561b04d4bca1dac6c74534229b9e40daa489f47188ae5837ada42108a50d4062cfb60f57e + version: 2.38.0 + resolution: "@microsoft/microsoft-graph-types@npm:2.38.0" + checksum: f5778650655034a6d92fbb932376002aefcf0cd358de323267b65fbec59284e8c0208e895c5523dcbbe79c4b583f3822c6beea01b70c687da26929c92de93c92 languageName: node linkType: hard @@ -13910,15 +13777,15 @@ __metadata: linkType: hard "@opensearch-project/opensearch@npm:^2.2.1": - version: 2.2.1 - resolution: "@opensearch-project/opensearch@npm:2.2.1" + version: 2.3.1 + resolution: "@opensearch-project/opensearch@npm:2.3.1" dependencies: aws4: ^1.11.0 debug: ^4.3.1 hpagent: ^1.2.0 ms: ^2.1.3 secure-json-parse: ^2.4.0 - checksum: ec75081574dbd036d8a72383a1a3bd99ed70a149a80960beb53052dcf8a7e2c4715a10b92fe7f6c2391ff681a3f27d9b3361f06bdbee7b88ddf869df57c2fe71 + checksum: 70324153eb9d74c005d5517a997f7027b829387af482956593a23b80c24ba8c9f9cdff40fec3d72e7066f3f16070b543c2d6f59f578e1be898784b49f9f6720f languageName: node linkType: hard @@ -14063,8 +13930,8 @@ __metadata: linkType: hard "@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.7": - version: 0.5.10 - resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.10" + version: 0.5.11 + resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.11" dependencies: ansi-html-community: ^0.0.8 common-path-prefix: ^3.0.0 @@ -14079,7 +13946,7 @@ __metadata: "@types/webpack": 4.x || 5.x react-refresh: ">=0.10.0 <1.0.0" sockjs-client: ^1.4.0 - type-fest: ">=0.17.0 <4.0.0" + type-fest: ">=0.17.0 <5.0.0" webpack: ">=4.43.0 <6.0.0" webpack-dev-server: 3.x || 4.x webpack-hot-middleware: 2.x @@ -14097,7 +13964,7 @@ __metadata: optional: true webpack-plugin-serve: optional: true - checksum: c45beded9c56fbbdc7213a2c36131ace5db360ed704d462cc39d6678f980173a91c9a3f691e6bd3a026f25486644cd0027e8a12a0a4eced8e8b886a0472e7d34 + checksum: a82eced9519f4dcac424acae719f819ab4150bfcf2874ac7daaf25a4f1c409e3d8b9d693fea0c686c24d520a5473756df32da90d8b89739670f8f8084c600bb4 languageName: node linkType: hard @@ -14234,7 +14101,7 @@ __metadata: languageName: node linkType: hard -"@repeaterjs/repeater@npm:3.0.4": +"@repeaterjs/repeater@npm:3.0.4, @repeaterjs/repeater@npm:^3.0.4": version: 3.0.4 resolution: "@repeaterjs/repeater@npm:3.0.4" checksum: cca0db3e802bc26fcce0b4a574074d9956da53bf43094de03c0e4732d05e13441279a92f0b96e2a7a39da50933684947a138c1213406eaafe39cfd4683d6c0df @@ -14383,14 +14250,14 @@ __metadata: linkType: hard "@roadiehq/backstage-plugin-github-pull-requests@npm:^2.2.7": - version: 2.5.13 - resolution: "@roadiehq/backstage-plugin-github-pull-requests@npm:2.5.13" + version: 2.5.14 + resolution: "@roadiehq/backstage-plugin-github-pull-requests@npm:2.5.14" dependencies: - "@backstage/catalog-model": ^1.4.0 - "@backstage/core-components": ^0.13.2 - "@backstage/core-plugin-api": ^1.5.2 - "@backstage/plugin-catalog-react": ^1.7.0 - "@backstage/plugin-home": ^0.5.3 + "@backstage/catalog-model": ^1.4.1 + "@backstage/core-components": ^0.13.3 + "@backstage/core-plugin-api": ^1.5.3 + "@backstage/plugin-catalog-react": ^1.8.0 + "@backstage/plugin-home": ^0.5.4 "@material-ui/core": ^4.11.0 "@material-ui/icons": ^4.9.1 "@material-ui/lab": ^4.0.0-alpha.60 @@ -14407,7 +14274,7 @@ __metadata: react: ^16.13.1 || ^17.0.0 react-dom: ^16.13.1 || ^17.0.0 react-router: 6.0.0-beta.0 || ^6.3.0 - checksum: 1af9997d26ee9dba046d71aa5415bde7e207c4118ca77e6e3e9623920ce7f6e314d8365fe2d1ef3eb1c279ac113653df903f1286005aba681faf75381a387189 + checksum: 6a51fa45c716c0313baedfb5e67a898b1cd0b02b7742473c46e7f6dc9742c88db950da110de7aa0ad2019fb2ed855ae5b9c567d5348ad31a3340ac49c77e1b11 languageName: node linkType: hard @@ -14550,41 +14417,45 @@ __metadata: languageName: node linkType: hard -"@rushstack/node-core-library@npm:3.53.3": - version: 3.53.3 - resolution: "@rushstack/node-core-library@npm:3.53.3" +"@rushstack/node-core-library@npm:3.59.7": + version: 3.59.7 + resolution: "@rushstack/node-core-library@npm:3.59.7" dependencies: - "@types/node": 12.20.24 colors: ~1.2.1 fs-extra: ~7.0.1 import-lazy: ~4.0.0 jju: ~1.4.0 - resolve: ~1.17.0 - semver: ~7.3.0 + resolve: ~1.22.1 + semver: ~7.5.4 z-schema: ~5.0.2 - checksum: 265d18e176079b8e90cd507e5d4d45f3afb1f811efdf491ea26f25f0397b77c0e9d42065166bf79a04503426c844ea92034cd3f8d5961b2a116de82e45cf6d6a + peerDependencies: + "@types/node": "*" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 57819d62fd662a6cf3306bf7d39c11204e094a2d5c2210639c2ac5baee58c183c02023203963cd0484a5623fd9f5dea7a223df843fb52b46a18508e6118cdc19 languageName: node linkType: hard -"@rushstack/rig-package@npm:0.3.17": - version: 0.3.17 - resolution: "@rushstack/rig-package@npm:0.3.17" +"@rushstack/rig-package@npm:0.4.1": + version: 0.4.1 + resolution: "@rushstack/rig-package@npm:0.4.1" dependencies: - resolve: ~1.17.0 + resolve: ~1.22.1 strip-json-comments: ~3.1.1 - checksum: 54eeea471c85b547575d7efc84fad3c9588f10106e2bfd8cd022bccb02c2fb0bf8ff597fab9114450b3c262abab0f0a4e52dd074bfd120e850b95037cd7b3102 + checksum: 68c5ec6c446c35939fca0444fa48e5beda736e3a5816e8b44d83df6ba8b9a2caf0ceddbdc866cd8ad3b523e42877cf6ecd467bc7839e3d618a9bb1c4b3e0b5a5 languageName: node linkType: hard -"@rushstack/ts-command-line@npm:4.13.1": - version: 4.13.1 - resolution: "@rushstack/ts-command-line@npm:4.13.1" +"@rushstack/ts-command-line@npm:4.15.2": + version: 4.15.2 + resolution: "@rushstack/ts-command-line@npm:4.15.2" dependencies: "@types/argparse": 1.0.38 argparse: ~1.0.9 colors: ~1.2.1 string-argv: ~0.3.1 - checksum: fea24b2549ecb7d3409b6b485d7c58bf8af8f8d1dd19c43a6b3532c45579ffc546bc4533b5db29c91ae1716581fdee4cb725f6a81ecb300e902ef06600e59f1d + checksum: c80dcfc99630ee51c6654c58ff41f69a3bd89c38e41d9871692bc73ee3c938ced79f8b75e182e492cafb2f6ddeb0628606856af494a0259ff6fac5b248996bed languageName: node linkType: hard @@ -14723,321 +14594,363 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^1.0.1, @smithy/abort-controller@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/abort-controller@npm:1.0.2" +"@smithy/abort-controller@npm:^2.0.1, @smithy/abort-controller@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/abort-controller@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 5d0c5b04636c1e27c812b3ac0f780602ff28ed60dbd5b9d39e9caa6f2e982b340efd3fea4d7e7b282451d1469ef6f9d5086e79a0945a078db2db7672dd174f49 + checksum: 68a331dc59decd842b2eecae3ff8705340d9504048a0e5af31286e9505e2473f81d96bfb2dd395161529adee5413bf6aaed7e264debd0862113325c85e0771da languageName: node linkType: hard -"@smithy/config-resolver@npm:^1.0.1, @smithy/config-resolver@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/config-resolver@npm:1.0.2" +"@smithy/chunked-blob-reader-native@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/chunked-blob-reader-native@npm:2.0.0" dependencies: - "@smithy/types": ^1.1.1 - "@smithy/util-config-provider": ^1.0.2 - "@smithy/util-middleware": ^1.0.2 + "@smithy/util-base64": ^2.0.0 tslib: ^2.5.0 - checksum: 23efcb59d682b7ab05ec920c92d9e7fcecc58b9ab204a4de504da18ef0687c051b27acf9bcfc91969a160ea137d6f2f2091b4f3e31c659327784f95240ec0d13 + checksum: 5f656dbc4913ab8312b6e687938f534a2ed28e749335560c21a6975f691630ede80afc4a81007078692da4eaa91839ae0a6e65dc39f3faf4423538f5d9bef37b languageName: node linkType: hard -"@smithy/credential-provider-imds@npm:^1.0.1, @smithy/credential-provider-imds@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/credential-provider-imds@npm:1.0.2" +"@smithy/chunked-blob-reader@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/chunked-blob-reader@npm:2.0.0" dependencies: - "@smithy/node-config-provider": ^1.0.2 - "@smithy/property-provider": ^1.0.2 - "@smithy/types": ^1.1.1 - "@smithy/url-parser": ^1.0.2 tslib: ^2.5.0 - checksum: 91b57891b68fd1e83b40d8bde72cbc38247982ba6eaaf32a80bdcccb1e30b42799ac7d8b50da55796ae873a95891b567298d5da41e1059150e240d075a82ee03 + checksum: a47e5298f0b28e25eaa5825ea9737718f0e2b7cf0f03a49cca186eb5544dd20ac91a2d92069f9805e40e5f3ab34d32f8091853518672fdbca009411179dbeb2a languageName: node linkType: hard -"@smithy/eventstream-codec@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/eventstream-codec@npm:1.0.2" +"@smithy/config-resolver@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/config-resolver@npm:2.0.2" + dependencies: + "@smithy/types": ^2.1.0 + "@smithy/util-config-provider": ^2.0.0 + "@smithy/util-middleware": ^2.0.0 + tslib: ^2.5.0 + checksum: deb7ecab3d3f2ce57304d99605e55739c94356105c8106c8ca9bb5c0fc329e2b4153788a12bb8600187c51645b9a5be799571c8e380cc4458d110c37f127c75b + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^2.0.0, @smithy/credential-provider-imds@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/credential-provider-imds@npm:2.0.2" + dependencies: + "@smithy/node-config-provider": ^2.0.2 + "@smithy/property-provider": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + tslib: ^2.5.0 + checksum: 519fd53a74e7fdb4a5e39bf4d0554983231919d80e2b9e324976a14ec41d4e0e4de6c9272af10820c220810ba30a9803a901c677b9994b8893aded10e1b36505 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^2.0.1, @smithy/eventstream-codec@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/eventstream-codec@npm:2.0.2" dependencies: "@aws-crypto/crc32": 3.0.0 - "@smithy/types": ^1.1.1 - "@smithy/util-hex-encoding": ^1.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-hex-encoding": ^2.0.0 tslib: ^2.5.0 - checksum: 64e2f6e5b03595815a2b968938459db3df31498b2befa657b5acc55242c57da56825431465bac271344cecf68d1f31a54ed3477ebd7d775ad3791d7e48a2bc62 + checksum: aa9136264c1f9a3bc8b9bc175d28b10d0ce72377593b4ed32d11b1753723a88e6305991ab0c6822d986f31edb914355df6bec8fb65ffe1f66c2ce6dab0768668 languageName: node linkType: hard -"@smithy/eventstream-serde-browser@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/eventstream-serde-browser@npm:1.0.2" +"@smithy/eventstream-serde-browser@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/eventstream-serde-browser@npm:2.0.2" dependencies: - "@smithy/eventstream-serde-universal": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/eventstream-serde-universal": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: d2ece075bcd5ba64788f74078217504b31cddabbb84e7dd5ed17b358f9f184e5ae840d36460251f06d84da7c8be4d70b87e1386418857466a7d0600145e2012d + checksum: b21c97d9e02fe3ba9110cbea35f674b3746c36eafde2874e878fcea03f7c1d1f34f38797df38b8b04dcc19576aa9f568e3e241f79aac696169941c4f44132d08 languageName: node linkType: hard -"@smithy/eventstream-serde-config-resolver@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/eventstream-serde-config-resolver@npm:1.0.2" +"@smithy/eventstream-serde-config-resolver@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/eventstream-serde-config-resolver@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: ff6eeec4439eafe8fb4d1ded405b14a67f79b74af18091bd218392f8450f7b9565c419b82291a0032e1da067a28b74551995b18110422169dc86e01e0581d2dc + checksum: 707e6e39e2ccb832db06f82bbf96c655548052458bf3f8b8b00e32461747b1fab1be9ec60a5ddbff8770e48b2cd153277bc81b203765d66b78fc2f76252ce8b9 languageName: node linkType: hard -"@smithy/eventstream-serde-node@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/eventstream-serde-node@npm:1.0.2" +"@smithy/eventstream-serde-node@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/eventstream-serde-node@npm:2.0.2" dependencies: - "@smithy/eventstream-serde-universal": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/eventstream-serde-universal": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 32b562735eaa59e7000d71887f9f6fc6c9986c595477619eae58830e8d720f4bdb8496d91f939bba6e933dda03ba2dde4fc03c67d090025c4b289c5851d7e8c1 + checksum: dfda607f0d76098616341c1e6f3012242c58f1d3211a464631f4d337f7113bce7f0380fe5cdf5b6e4e391beb54562d1a96494ca8d2af847f1f13264f362365aa languageName: node linkType: hard -"@smithy/eventstream-serde-universal@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/eventstream-serde-universal@npm:1.0.2" +"@smithy/eventstream-serde-universal@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/eventstream-serde-universal@npm:2.0.2" dependencies: - "@smithy/eventstream-codec": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/eventstream-codec": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 24a40513b9384ecadb78618d672cc013d06d43c743e60cf7e397fa4e0ad7ce19e34281ddcf9527a3343409f21659f8a3bab26963cb47fc3557726cd40580973c + checksum: 887d03832126054fdfd6426ee3153eec54ecfcddeb04c63c69f6f1fcc23e0b8af737dce7c8ae07ca456f1e0be63614498280b77ff5c919793a0633b1d7d11a2c languageName: node linkType: hard -"@smithy/fetch-http-handler@npm:^1.0.1, @smithy/fetch-http-handler@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/fetch-http-handler@npm:1.0.2" +"@smithy/fetch-http-handler@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/fetch-http-handler@npm:2.0.2" dependencies: - "@smithy/protocol-http": ^1.1.1 - "@smithy/querystring-builder": ^1.0.2 - "@smithy/types": ^1.1.1 - "@smithy/util-base64": ^1.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/querystring-builder": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-base64": ^2.0.0 tslib: ^2.5.0 - checksum: 62965f69ebcb7a7a67576d4ac909873080d0da33796f15ecf7b265e7f35e0d43e6d3cdf5edb53b9a51f97a88ee90777dd8a7a84ac8dd914cd3d4bcb1c6611506 + checksum: 2c7c97c02bc347b420c51a052b97884e70cfbed25475e68d5a1e8f46b1b0873d727c4989b4a71aaa5e5e1ba055c423f989f5b01bf105176580acc9d5cc80eb72 languageName: node linkType: hard -"@smithy/hash-node@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/hash-node@npm:1.0.2" +"@smithy/hash-blob-browser@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/hash-blob-browser@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 - "@smithy/util-buffer-from": ^1.0.2 - "@smithy/util-utf8": ^1.0.2 + "@smithy/chunked-blob-reader": ^2.0.0 + "@smithy/chunked-blob-reader-native": ^2.0.0 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 018ef3271c60e7c8df07c4b5f77b52010a8a6be53e9da3f7e361cda48a6df6f5a52870a7721110338725e83b88d5ef120c2312334b6618f3dfdf80d4e0725cab + checksum: add61e1fbdcaf5b019bb8ad4d1dbb70cbc59c65f039cab2ac9fc4617333ce7a3aa356d51b737732560d23db29f7afbd5c1addb4db738ec21dfa2cad5dd82ca05 languageName: node linkType: hard -"@smithy/invalid-dependency@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/invalid-dependency@npm:1.0.2" +"@smithy/hash-node@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/hash-node@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 + "@smithy/util-buffer-from": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 51d4bfc25854789a312d87c9bfe1a919e8d7a96052d4010f411c0cb532673a8a3fff92f2d0cf18ccbd05f618f36bfc7fab61159878559ffdd7096de2bd749916 + checksum: 2917822fcc965596755a284cbae8376adb0d1ebc5998ae8a5682e4385f29eaac4408b24e560eced4b7fb8a279fa4453277dc3853114724d4e4c62ce935863fa9 languageName: node linkType: hard -"@smithy/is-array-buffer@npm:^1.0.1, @smithy/is-array-buffer@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/is-array-buffer@npm:1.0.2" +"@smithy/hash-stream-node@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/hash-stream-node@npm:2.0.2" dependencies: + "@smithy/types": ^2.1.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 811b100b809bc1730ed33231ae22afbce5ccc8d2d5db5c8e86b52db718d738283337a43f777397d1308c24243d0be3e3d862684e9be1dbdfb547a8d8399f0911 + checksum: b588608e200978a09633591e5f155bb63890d0f96776eaf94bfbf8df7223bf6a83e8f1a0b256fd7ee286b7b2ca53f1f7124e2d436d5e21ddf10aeb52e7ab528d languageName: node linkType: hard -"@smithy/md5-js@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/md5-js@npm:1.0.2" +"@smithy/invalid-dependency@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/invalid-dependency@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 - "@smithy/util-utf8": ^1.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 61913e2a6db50eeba87b1fc23a977d8266c4d19d4c52193ac4b2263eab4fed58c25b23baab514cab104aabdbe6437518f2e2f68146e400c7a760a881552f2da5 + checksum: c31f5d8555729ac34a0e56f350061ee127a6869c6fbe5e30c303e8b273aa52370d7e3a9ec603448cf2e986740a4c67e5285a350d738896e2ad4f7312cb3ed943 languageName: node linkType: hard -"@smithy/middleware-content-length@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/middleware-content-length@npm:1.0.2" +"@smithy/is-array-buffer@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/is-array-buffer@npm:2.0.0" dependencies: - "@smithy/protocol-http": ^1.1.1 - "@smithy/types": ^1.1.1 tslib: ^2.5.0 - checksum: 4b00aa741179152cbdc2aa1229077d0a39196f7ce916a11d121cfa160af380651e031f6a8e59193061bbca0baf5fff5f6010b1a998a6c0b03f228fa862625a8a + checksum: 6d101cf509a7818667f42d297894f88f86ef41d3cc9d02eae38bbe5e69b16edf83b8e67eb691964d859a16a4e39db1aad323d83f6ae55ae4512a14ff6406c02d languageName: node linkType: hard -"@smithy/middleware-endpoint@npm:^1.0.2": - version: 1.0.3 - resolution: "@smithy/middleware-endpoint@npm:1.0.3" +"@smithy/md5-js@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/md5-js@npm:2.0.2" dependencies: - "@smithy/middleware-serde": ^1.0.2 - "@smithy/types": ^1.1.1 - "@smithy/url-parser": ^1.0.2 - "@smithy/util-middleware": ^1.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 8081b515955632828cdf6955368e46b5288f996fd6697dacf1d44eced488033f8f9d7a10e036fcebabc7f4e72dfcd69ab8ac76b4c17082b9b8326431441ef1a7 + checksum: 889056925d17f424f23540b25c237c7ad4e9b7b8a7817a0172cf0a648da5ab9c709593790629955365c93504787c534b2156aa8d056ea27fe81a88106126268b languageName: node linkType: hard -"@smithy/middleware-retry@npm:^1.0.3": - version: 1.0.4 - resolution: "@smithy/middleware-retry@npm:1.0.4" +"@smithy/middleware-content-length@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/middleware-content-length@npm:2.0.2" dependencies: - "@smithy/protocol-http": ^1.1.1 - "@smithy/service-error-classification": ^1.0.3 - "@smithy/types": ^1.1.1 - "@smithy/util-middleware": ^1.0.2 - "@smithy/util-retry": ^1.0.4 + "@smithy/protocol-http": ^2.0.2 + "@smithy/types": ^2.1.0 + tslib: ^2.5.0 + checksum: d4a28b967629eab3ee2265ad34e6f45b8006e247e51fdce53604431de58a7840a304ff530475b160d50dc9fb47b1c4c91e51e340003c57b9aaa8483686540336 + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/middleware-endpoint@npm:2.0.2" + dependencies: + "@smithy/middleware-serde": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/url-parser": ^2.0.2 + "@smithy/util-middleware": ^2.0.0 + tslib: ^2.5.0 + checksum: 2a1ba8fa8fe0aef2bdd87c6fc7d3567b4b03e63ad8b46531130042c226b627d438f163caad81295b7dcb506536fd0700fb0ddc20bfc894c7be589b8c891fa799 + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/middleware-retry@npm:2.0.2" + dependencies: + "@smithy/protocol-http": ^2.0.2 + "@smithy/service-error-classification": ^2.0.0 + "@smithy/types": ^2.1.0 + "@smithy/util-middleware": ^2.0.0 + "@smithy/util-retry": ^2.0.0 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 3ca5c4abe3cbfba1201a7777c0f41ae9df0985a1f155bab647cd88d86c8101f4de0bc118164a14d4ac8bd41d120d0c0f1b327b3512c341aeb06f60b0a32dc89d + checksum: c88134fe739b9b038117b388b10e29e53407af3bd6f61fbfdc2600b45b6415bb2e75ee4ce9b0423376d11472d0fb96ba6300c23a1c0ddf8a5d3806f8b7d3c088 languageName: node linkType: hard -"@smithy/middleware-serde@npm:^1.0.1, @smithy/middleware-serde@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/middleware-serde@npm:1.0.2" +"@smithy/middleware-serde@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/middleware-serde@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 563045c0ad6fd37548197a3844716e61bf969f4a7b3fbb6c6f0129d4745a0c373029acf069d142a59f1d22778e90f86869cd4ef3f9b297b04a23ef042c6c9c55 + checksum: 6d343eceeff45c08142ff0567885b910f6d77e7ec95396f8fc4b0fc259284c8d430bd8412ef36c5c45905ee631c660524e38450bda86400b06f3272f4504b08f languageName: node linkType: hard -"@smithy/middleware-stack@npm:^1.0.1, @smithy/middleware-stack@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/middleware-stack@npm:1.0.2" +"@smithy/middleware-stack@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/middleware-stack@npm:2.0.0" dependencies: tslib: ^2.5.0 - checksum: 34794d1e6d8b5fd9be70d5d0137f09d8ec88942d054575e35dbf6407f2040c3d0d667d26ff01132c4c42933785127d12b6406a1802af65a779e44cbf38fc9fe2 + checksum: dd23dff4da44964e936c5ae465de9416bb8dd67da2ae72ffe450156ad52e82475836ed5c18d82cef7edeca421b33d363889549e34482008eeb9ca0bb97f061f2 languageName: node linkType: hard -"@smithy/node-config-provider@npm:^1.0.1, @smithy/node-config-provider@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/node-config-provider@npm:1.0.2" +"@smithy/node-config-provider@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/node-config-provider@npm:2.0.2" dependencies: - "@smithy/property-provider": ^1.0.2 - "@smithy/shared-ini-file-loader": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/property-provider": ^2.0.2 + "@smithy/shared-ini-file-loader": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 593bd5adf0177851a191849e68760f7ebde943db2613898ecda468b31c47442e5fd380fdd95fe95f912bd15fe48a28dbde3f352b49a8108fc34c630d7eb50843 + checksum: 6cf3953dfd08f337b5ef41fa6808b083ceb06624e2de4996bfbb7d4326a8deefe73e47d6bb00ca81a2d2dc9b662d3eaaa3d1698262f2256d1a7c71290663ca12 languageName: node linkType: hard -"@smithy/node-http-handler@npm:^1.0.2, @smithy/node-http-handler@npm:^1.0.3": - version: 1.0.3 - resolution: "@smithy/node-http-handler@npm:1.0.3" +"@smithy/node-http-handler@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/node-http-handler@npm:2.0.2" dependencies: - "@smithy/abort-controller": ^1.0.2 - "@smithy/protocol-http": ^1.1.1 - "@smithy/querystring-builder": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/abort-controller": ^2.0.2 + "@smithy/protocol-http": ^2.0.2 + "@smithy/querystring-builder": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 727e1391ccc409b3ed7d7a697bfcd29e4e9fbc81404665719c47c8143832ef8f65ea9f8f5d783ea1930fd56a0d45eaaa6a7a8db76da785ba73f0fd29276fbe11 + checksum: 1c5e0189d2f68bfc4c50c107182eb34b34024de3213a552cab2088c01bea8707c51c7cbbd7cf61290cf8b71e13ec2d715e51b7647edc9f5d27ab8779011e2a4d languageName: node linkType: hard -"@smithy/property-provider@npm:^1.0.1, @smithy/property-provider@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/property-provider@npm:1.0.2" +"@smithy/property-provider@npm:^2.0.0, @smithy/property-provider@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/property-provider@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 55d09de3dab2e5858b45f981c67ec1cf4e8fc28a758b9830fd0c5e04ca6e9d69f31e92c80fa4fba5d8eed4a60b4f95528d41f9a6c63e2ff0aad726d3c3b83636 + checksum: e18df64d5ec92b2c7876dc1e7a596dc80066e13d49fcad6d8caac50394e8b3f3b15aa1f517429a85cdfefc218ee3d72028613454c706638352732ab1ae0f7418 languageName: node linkType: hard -"@smithy/protocol-http@npm:^1.1.0, @smithy/protocol-http@npm:^1.1.1": - version: 1.1.1 - resolution: "@smithy/protocol-http@npm:1.1.1" +"@smithy/protocol-http@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/protocol-http@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 6320e8b010d05a123efef19401b3e2fc0d03efa99082ad0d5fd41a3209a05332acb4b5ee21dcfd4764b6576019884602bd09ede0f7508540baf7ddb1f7eb49f5 + checksum: 61202d08d4433a4406a56e1e77863b89a60431a39984b38fec04a18276973857524b18d60c394236f3b664a08ec1996d837c295798e44f02ba93aca5336e9432 languageName: node linkType: hard -"@smithy/querystring-builder@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/querystring-builder@npm:1.0.2" +"@smithy/querystring-builder@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/querystring-builder@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 - "@smithy/util-uri-escape": ^1.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-uri-escape": ^2.0.0 tslib: ^2.5.0 - checksum: c0e6807a5b6c8438295ff8ebc11d9e2a28fe1434405688055a9dd4f4d354e49e14a4e459419af23f74aab0a281d58b70d29ae6777a42504a89d6f089b110ee52 + checksum: cdda228f0ac049aed302dcd19c47309a81eeb4d4c360189a399bc70bd6283ba218c87cfdee43e33c406cbe452d7979a389de9150b06de5221dfe4a6f69e53c1b languageName: node linkType: hard -"@smithy/querystring-parser@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/querystring-parser@npm:1.0.2" +"@smithy/querystring-parser@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/querystring-parser@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 348672b1bc4193de23055421756c47619de5a2dea496dcf85a7744e9a8d5b26a4573dd731f9be936f7b1ba564676ecdf6d166638f9961bff497a2c554c97f01c + checksum: 0a571dbf7876096823071583e4b8506d9ce4fc1ff2af9fac07987bdf9a6e0dfee1598280cf411a6709cf535e2904eeab385bb2186bb6fe3aa987bae84620256c languageName: node linkType: hard -"@smithy/service-error-classification@npm:^1.0.3": - version: 1.0.3 - resolution: "@smithy/service-error-classification@npm:1.0.3" - checksum: adf840de7865606946a68f55ae4f35e03d96422be4df886309300944243510baf1bd84ef0786bcd8184094e97fafdff66a52b8ba669267391a8d8856093edde0 +"@smithy/service-error-classification@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/service-error-classification@npm:2.0.0" + checksum: f6f9b869dca8e74871c428e1277dd3700f67b0ca61de69fc838ac55187bbc602193a7d1073113ea6916892babf3f8fe4938b182fc902ce0a415928799a7e3f9a languageName: node linkType: hard -"@smithy/shared-ini-file-loader@npm:^1.0.1, @smithy/shared-ini-file-loader@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/shared-ini-file-loader@npm:1.0.2" +"@smithy/shared-ini-file-loader@npm:^2.0.0, @smithy/shared-ini-file-loader@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/shared-ini-file-loader@npm:2.0.2" dependencies: - "@smithy/types": ^1.1.1 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: c9ba908a48c12a06bcbb872e2e0581805507efc9beffcdbad39baa09b22da63bbcbf538a95d099666869e080ad5556bfe2fab6d4e977a71163595f3f9c1d7edc + checksum: 5a7100b1db34d0a479dcbc2d649840dcf37fb2be4d5798ba7bef58186b636d867c3c555796f1372bcfc5554705a3f267e13008705451f44cd03a2c5a87c5c89e languageName: node linkType: hard -"@smithy/signature-v4@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/signature-v4@npm:1.0.2" +"@smithy/signature-v4@npm:^2.0.0": + version: 2.0.1 + resolution: "@smithy/signature-v4@npm:2.0.1" dependencies: - "@smithy/eventstream-codec": ^1.0.2 - "@smithy/is-array-buffer": ^1.0.2 - "@smithy/types": ^1.1.1 - "@smithy/util-hex-encoding": ^1.0.2 - "@smithy/util-middleware": ^1.0.2 - "@smithy/util-uri-escape": ^1.0.2 - "@smithy/util-utf8": ^1.0.2 + "@smithy/eventstream-codec": ^2.0.1 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/types": ^2.0.2 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-middleware": ^2.0.0 + "@smithy/util-uri-escape": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 3e3072ef956adbb9fe633f2e7c28fc9aaeddb7551c25a1af94214fd5e93617c2b0ede440c1cb61ee6268c502ce0418cf124fb21cb654605292a956ec3061d54a + checksum: 2c4bc499251dfbdbea0359fc5196002ac805df7f6f87d7cba36164d8fcd16a749a3c1a52259569134628511dc496724d5e76744c1c39fee3a3f42c5fbfe3749b languageName: node linkType: hard -"@smithy/smithy-client@npm:^1.0.3": - version: 1.0.4 - resolution: "@smithy/smithy-client@npm:1.0.4" +"@smithy/smithy-client@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/smithy-client@npm:2.0.2" dependencies: - "@smithy/middleware-stack": ^1.0.2 - "@smithy/types": ^1.1.1 - "@smithy/util-stream": ^1.0.2 + "@smithy/middleware-stack": ^2.0.0 + "@smithy/types": ^2.1.0 + "@smithy/util-stream": ^2.0.2 tslib: ^2.5.0 - checksum: 468aa2f28b6addb82861a543a2c1639dcfd40e49d3344237bd56c2d061af5e2bc88972297a2b86de7bf31d7ee49589a4e5a577518ef747e2c7458ae20f7c3322 + checksum: 999b814ece054dfcc4808e5e3bdf6d24f5bc1a16b375bccad95ba9f0ff709a68f4c6437d1378b8b1ef870ce75e4cd2e55df4143394890e9759cec69fd078062d languageName: node linkType: hard -"@smithy/types@npm:^1.1.0, @smithy/types@npm:^1.1.1": +"@smithy/types@npm:^1.1.0": version: 1.1.1 resolution: "@smithy/types@npm:1.1.1" dependencies: @@ -15046,161 +14959,170 @@ __metadata: languageName: node linkType: hard -"@smithy/url-parser@npm:^1.0.1, @smithy/url-parser@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/url-parser@npm:1.0.2" +"@smithy/types@npm:^2.0.2, @smithy/types@npm:^2.1.0": + version: 2.1.0 + resolution: "@smithy/types@npm:2.1.0" dependencies: - "@smithy/querystring-parser": ^1.0.2 - "@smithy/types": ^1.1.1 tslib: ^2.5.0 - checksum: 1844b23a30afb689bc9ec21238d9e6cc428e152201f8f30f252f6211ee8b74760d6568e42125b75bc4d110b15476eac22c204c7e36f6970d04e8afa5bc83ec1d + checksum: 15c61c1b6520ef21257b64e4be7c8334c1206db8f21c9fb301838b5e09699996060c4361e51d2c63ee972f8678df68a8a60e2dd18216ae70751393d9d4188c5a languageName: node linkType: hard -"@smithy/util-base64@npm:^1.0.1, @smithy/util-base64@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-base64@npm:1.0.2" +"@smithy/url-parser@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/url-parser@npm:2.0.2" dependencies: - "@smithy/util-buffer-from": ^1.0.2 + "@smithy/querystring-parser": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 618d2952fbbc2cabcc6234c9aad612417a2a6f2fcac556a81b75f59d651e5daea7b2c1b44894b30611a425f32045a2dbe0623ebf57654e2068486d0d5a4b6ac7 + checksum: 3a977e6a2c8d6d01e29d8925ef351d2cbc0951029d774490e3362d681fd18d97622afd2e99cea158f3e851cf423dc8dde5f6aee639cdf77f31e80150a732478b languageName: node linkType: hard -"@smithy/util-body-length-browser@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/util-body-length-browser@npm:1.0.2" +"@smithy/util-base64@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-base64@npm:2.0.0" dependencies: + "@smithy/util-buffer-from": ^2.0.0 tslib: ^2.5.0 - checksum: 3b0eacc3761625aaf5c384da8487a38663325385826ca9ac0fee17b7dff879a2cb3564a03d146e0b66ba6c503e1a07f159a9cccd4d7711b7063516acbfadb496 + checksum: 52124a684dfac853288acd2a0ffff02559c21bf7faaa3db58a914e4acb4b1f7925fd48593e7545db87f8f962250824d1249dc8be645ecbd2c1dd1728cfe1069b languageName: node linkType: hard -"@smithy/util-body-length-node@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/util-body-length-node@npm:1.0.2" +"@smithy/util-body-length-browser@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-body-length-browser@npm:2.0.0" dependencies: tslib: ^2.5.0 - checksum: 472371dc0a44a72333111758b8e60428cdca088cfe8a87c98413c81c4083e8988ae9d27f20fa2b0a8aed7fc26c0fec8e13c271494772c619f65f1a9f4e665eb3 + checksum: 4bccdd857bd24c9dcb6e9f2d5be03d59415f9a94d660ec7b3efb45e9aa04017f34c387368f176f24233a071af3b7a2b5f8236a2f5a83bfc884d24dfcc341e836 languageName: node linkType: hard -"@smithy/util-buffer-from@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-buffer-from@npm:1.0.2" +"@smithy/util-body-length-node@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-body-length-node@npm:2.0.0" dependencies: - "@smithy/is-array-buffer": ^1.0.2 tslib: ^2.5.0 - checksum: ca4308ebc54f372777fc53b2782c752ebfdfe3934b1c4da935c78bb2f4f921d2071056edc644704ba894d363f85cc52fc07095d93eb6cbba510ba19a767be265 + checksum: 47ded0cc99f880eda353700e10d6131289683164d4ef98a24a2d79449d33f3bbf70c1ce0a66fc457e0c8d14be9fb2a3430fa09302bbc3daa5d23129e11fff478 languageName: node linkType: hard -"@smithy/util-config-provider@npm:^1.0.1, @smithy/util-config-provider@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-config-provider@npm:1.0.2" +"@smithy/util-buffer-from@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-buffer-from@npm:2.0.0" dependencies: + "@smithy/is-array-buffer": ^2.0.0 tslib: ^2.5.0 - checksum: 625c100b8af62cb08c167ebd23f77250e62b012e085ec8db8f06e3539fc516c340e14b480a09012192595121a58276dfc4370529237a58393185f3782ed0244a + checksum: d33cbf3e488d23390c88705ddae71b08de7a87b6453e38b508cd37a22a02e8b5be9f0cd46c1347b496c3977a815a7399b18840544ecdc4cce8cf3dcd0f5bb009 languageName: node linkType: hard -"@smithy/util-defaults-mode-browser@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/util-defaults-mode-browser@npm:1.0.2" +"@smithy/util-config-provider@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-config-provider@npm:2.0.0" dependencies: - "@smithy/property-provider": ^1.0.2 - "@smithy/types": ^1.1.1 + tslib: ^2.5.0 + checksum: cdc34db5b42658a7c98652ddb2e35b31e0d76f22a051d71724927999a53467fb38fe6dcf228585544bc168cbd54ded3913e14cbc33c947d3c8a45ca518a9b7b0 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/util-defaults-mode-browser@npm:2.0.2" + dependencies: + "@smithy/property-provider": ^2.0.2 + "@smithy/types": ^2.1.0 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: ca03c9f596f9a26d392c289de7af7c7e7bdb12648b0339074bcbaddd762e58cb0cf127b8ed24e269bff004465702986b90d5151c502b418a615449171432024b + checksum: 0e4d70040268757120aec9b6207256d091772075e21dc1778c29d33d002c12e4825a192a2424ca3099d79ceb9ce8e6e77a627b680e12a657a171ca9ed84818e7 languageName: node linkType: hard -"@smithy/util-defaults-mode-node@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/util-defaults-mode-node@npm:1.0.2" +"@smithy/util-defaults-mode-node@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/util-defaults-mode-node@npm:2.0.2" dependencies: - "@smithy/config-resolver": ^1.0.2 - "@smithy/credential-provider-imds": ^1.0.2 - "@smithy/node-config-provider": ^1.0.2 - "@smithy/property-provider": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/config-resolver": ^2.0.2 + "@smithy/credential-provider-imds": ^2.0.2 + "@smithy/node-config-provider": ^2.0.2 + "@smithy/property-provider": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 805977758a5cabe3b8a4ee69f29cf6861356096239a60269eac6b83a4d11eddba030b0a5bcdb64add3d1301f611b21ed7d773e354cac8ff08b9d85ba47960252 + checksum: 25b8988bad21b7fb45c715e303b57bee4e865644f3c3282aa36f81ce71000e94289c84bccdd820556a8625d5d6bfa4ce70eb0f02bd74e9bb117ed713fabeba5d languageName: node linkType: hard -"@smithy/util-hex-encoding@npm:^1.0.1, @smithy/util-hex-encoding@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-hex-encoding@npm:1.0.2" +"@smithy/util-hex-encoding@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-hex-encoding@npm:2.0.0" dependencies: tslib: ^2.5.0 - checksum: c70bfccb41ac702b514a2bc3d6f897a616efa5aba880a6c7a7501ea874bea3c2213868cb2c494c19f6a5190f3f9e4f7d73d9ab87addbd243435e287dc892dc30 + checksum: 884373e089d909e3c9805bdb78f367d1f3612e4e1e6d8f0263cc82a8b9689eddc0bc80b8b58aa711bd5b48d9cb124f9996906c172e951c9dac78984459e831cf languageName: node linkType: hard -"@smithy/util-middleware@npm:^1.0.1, @smithy/util-middleware@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-middleware@npm:1.0.2" +"@smithy/util-middleware@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-middleware@npm:2.0.0" dependencies: tslib: ^2.5.0 - checksum: 899aca62efab92ac1b55e801b2bb1a3e5eb1b74f52387abe40f902ec2e5f3afd36e2942f392ff1a562e69dd0de901b9aa20c55e4af2ff0e37e20c14b20940852 + checksum: 10401734a10e0c48ed684f20b7a34c40ed85f2e906e61adb6295963d035f2a93b524e80149a252a259a4bca3626773bf89c5eaa2423fd565358c6b4eb9b6d4e0 languageName: node linkType: hard -"@smithy/util-retry@npm:^1.0.3, @smithy/util-retry@npm:^1.0.4": - version: 1.0.4 - resolution: "@smithy/util-retry@npm:1.0.4" +"@smithy/util-retry@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-retry@npm:2.0.0" dependencies: - "@smithy/service-error-classification": ^1.0.3 + "@smithy/service-error-classification": ^2.0.0 tslib: ^2.5.0 - checksum: b9a7e464d67b141a175f126b2a0fc5c91de387eb483b432db4769cee43bbbb546e4142711f293c3a04c82b6d56b676b7a2f7f7150e2c736a10d8e5df1ed63c04 + checksum: d5bfe5e81f41dffce6ba5aaf784f08247602d00f883c10c0de9e34a7f04f5369d7ac43901dd75eecc3864882b7390ad40885d07b3dcb35a366411b639482e673 languageName: node linkType: hard -"@smithy/util-stream@npm:^1.0.1, @smithy/util-stream@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-stream@npm:1.0.2" +"@smithy/util-stream@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/util-stream@npm:2.0.2" dependencies: - "@smithy/fetch-http-handler": ^1.0.2 - "@smithy/node-http-handler": ^1.0.3 - "@smithy/types": ^1.1.1 - "@smithy/util-base64": ^1.0.2 - "@smithy/util-buffer-from": ^1.0.2 - "@smithy/util-hex-encoding": ^1.0.2 - "@smithy/util-utf8": ^1.0.2 + "@smithy/fetch-http-handler": ^2.0.2 + "@smithy/node-http-handler": ^2.0.2 + "@smithy/types": ^2.1.0 + "@smithy/util-base64": ^2.0.0 + "@smithy/util-buffer-from": ^2.0.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: ebea6886766da812434d4ca39ee201302096451441d577eb5efeb44ce3437f3828df435676a5e453f8eb535e19224bc3d21c2d3e6632a5eef68d932a37343476 + checksum: 32115fa35437370671dcedf14714ab32b94ceb598baffe1d8afa7c574084b4cf0826f15fd3a76bc1450010512d2d067cbe62fe8c6ce4ee640fb0270768a8d87c languageName: node linkType: hard -"@smithy/util-uri-escape@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-uri-escape@npm:1.0.2" +"@smithy/util-uri-escape@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-uri-escape@npm:2.0.0" dependencies: tslib: ^2.5.0 - checksum: 6df9fb3aed8fc386c1b7ccb2b52268c2e67f3620a85fa42d35f964ae2c492b6395841a751a0ab4068825c7fd54a84933014571d6fbc12e9e1a711d0be3f2c747 + checksum: d201cee524ece997c406902463b5ea0b72599994f7b3ac1d923d5645497e9ef93126d146016f13dd4afafe33b9a3e92faf4e023cf0af510b270c1b9ce3d78da8 languageName: node linkType: hard -"@smithy/util-utf8@npm:^1.0.1, @smithy/util-utf8@npm:^1.0.2": - version: 1.0.2 - resolution: "@smithy/util-utf8@npm:1.0.2" +"@smithy/util-utf8@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-utf8@npm:2.0.0" dependencies: - "@smithy/util-buffer-from": ^1.0.2 + "@smithy/util-buffer-from": ^2.0.0 tslib: ^2.5.0 - checksum: 64add10ac1bbeb0c96a1ba8b05886ae86cee589fd4cccb25276849748fed7035c54e64d40107f22a57ca9b4f9324afe4baf0007c67536d520c1b8378e1e6ec55 + checksum: bc8cda84f85b513380a61352635b306ae50d3b92974454db32835b39bbaa38150332b89346098ba9dea2e0002e2963fcbdd622bc9b3eec7b7ea8fa3f8c7ce737 languageName: node linkType: hard -"@smithy/util-waiter@npm:^1.0.1": - version: 1.0.2 - resolution: "@smithy/util-waiter@npm:1.0.2" +"@smithy/util-waiter@npm:^2.0.2": + version: 2.0.2 + resolution: "@smithy/util-waiter@npm:2.0.2" dependencies: - "@smithy/abort-controller": ^1.0.2 - "@smithy/types": ^1.1.1 + "@smithy/abort-controller": ^2.0.2 + "@smithy/types": ^2.1.0 tslib: ^2.5.0 - checksum: 07b5284b2d10c4ddcfd61ce7bc2585f3ba401cf42dffa799317eb1895e9b061c54efd328e204c8e1881888018cd198889bccc86c6a707485fdec3738ed5b4aeb + checksum: a0d23641fb724684b299d5131d82578f1c11cdcde9d55af309e00a0f75b19792016207eb1effb58da21eeafcac6cd5ed7ea4632c2c2e2e5eb423aa40f2e50fdf languageName: node linkType: hard @@ -15365,8 +15287,8 @@ __metadata: linkType: hard "@stoplight/spectral-formatters@npm:^1.1.0": - version: 1.1.0 - resolution: "@stoplight/spectral-formatters@npm:1.1.0" + version: 1.2.0 + resolution: "@stoplight/spectral-formatters@npm:1.2.0" dependencies: "@stoplight/path": ^1.3.2 "@stoplight/spectral-core": ^1.15.1 @@ -15378,7 +15300,7 @@ __metadata: strip-ansi: 6.0 text-table: ^0.2.0 tslib: ^2.5.0 - checksum: a72d9ecb423af1f911b654f48ae04c19f0707e8eb8542de57090299dbff19e58a1dff8f229614056fbf2b2f44636423cd9c980189c4cceb58961d84925d9ae7b + checksum: e84cc06ed33348f532f513b64d7179e2e261d6b87e45be66169088bf8d4c0fbc8fcda92adcde0d3f88f03eb5f31fbf9b2c853cdd49d4d1ae809d9a76920adb16 languageName: node linkType: hard @@ -15474,12 +15396,12 @@ __metadata: linkType: hard "@stoplight/types@npm:^12.3.0 || ^13.0.0, @stoplight/types@npm:^13.0.0, @stoplight/types@npm:^13.14.0, @stoplight/types@npm:^13.15.0, @stoplight/types@npm:^13.6.0": - version: 13.15.0 - resolution: "@stoplight/types@npm:13.15.0" + version: 13.18.0 + resolution: "@stoplight/types@npm:13.18.0" dependencies: "@types/json-schema": ^7.0.4 utility-types: ^3.10.0 - checksum: 839f0bbedb791bd6792ef22b6a821ca504b14b705927f7c510c4cdcc591eddc8818c82b8857129501aa809d6f369b82e4487bfe18dfc5ce00e28317ecad2df9a + checksum: 17f464b6986fcb07719584cbb55c59e1c8d72d6d000ed64b13cae92acbdc069e5a725b4d14c4a2998eb701a0289e6ce3aee151e4200698df906ba058747e210f languageName: node linkType: hard @@ -15696,332 +15618,334 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-ast@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ast@npm:0.70.0" +"@swagger-api/apidom-ast@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ast@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@types/ramda": ~0.29.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - unraw: ^2.0.1 - checksum: f1dfd5eae236ee42880dd474943d4a6deb04c7c1bd72f7bc7c30b8d348455b8cbd1c56a07d4417714b5221d3473469a1edcfcab869838987cc91332157561fd3 + unraw: ^3.0.0 + checksum: 453880bafb11c9c53768629bf0c9c3cfdab76625dbd4eb34ec146c23eb782dace3c279ca4cff665ac20f3fae229ba96bff2bdbacba383630107f23522183f130 languageName: node linkType: hard -"@swagger-api/apidom-core@npm:>=0.70.0 <1.0.0, @swagger-api/apidom-core@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-core@npm:0.70.0" +"@swagger-api/apidom-core@npm:>=0.74.1 <1.0.0, @swagger-api/apidom-core@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-core@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-ast": ^0.74.1 + "@types/ramda": ~0.29.3 minim: ~0.23.8 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 short-unique-id: ^4.4.4 stampit: ^4.3.2 - checksum: 1db75c4e62ecaef9e788919c808dbd6f1c38781e4529639e4b10a2a8325677c335f4f1f650b9e581a6736dbccb6c0035c664145d6f6a39e954f8eb699d51553a + checksum: 8369c45198a2d955dd84a55564f89411394df388c2cf277208fbfcaf3014ae596f4de9916b55d918683b13fc18f0c54a0e190bf34206cf4c16c10a1a7e50ab07 languageName: node linkType: hard -"@swagger-api/apidom-json-pointer@npm:>=0.70.0 <1.0.0, @swagger-api/apidom-json-pointer@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-json-pointer@npm:0.70.0" +"@swagger-api/apidom-json-pointer@npm:>=0.74.1 <1.0.0, @swagger-api/apidom-json-pointer@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-json-pointer@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: dfa3b49e454d78d7ec5281c59537bfc2e99ba9b5fa1875cd5d600c2499298742c6a9e41e415937c2de99661f6cd27b7dc5b9900b75dc042e96e506266f409860 + checksum: 2adbbb735cd559b975fa58d11b7d99844eb5eb48282842cb6048e61742229198519cfbcf90f48c38cfbbad089b9d71a258c9aeb92c3fbec8abe392756e4816f0 languageName: node linkType: hard -"@swagger-api/apidom-ns-api-design-systems@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-api-design-systems@npm:0.70.0" +"@swagger-api/apidom-ns-api-design-systems@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-api-design-systems@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-1": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: 8b08d5d99a0ff6497a5e3e093daaa9e47d79c2c216ce5397992348438b8ab4a53b789b31caf11a71855496bdb379c5d159cbc3c05e269ba97a086f3fddaa390c + checksum: 83bb664015243a9fb342d81f1700f7dde12015a3c34c45ac0575fc79388eeec99dd97f6139207437b883b0e3c261916ebb931571c1c0e0240f84ea7fd57d8c93 languageName: node linkType: hard -"@swagger-api/apidom-ns-asyncapi-2@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-asyncapi-2@npm:0.70.0" +"@swagger-api/apidom-ns-asyncapi-2@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-asyncapi-2@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-json-schema-draft-7": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-json-schema-draft-7": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: d2d38354132b7894427222b5dbc924c9a36966795710cede1010496b2a3649362c110b57730e8ebea93a662d442fe29c2adc174e27b91f9056189604c2953e8b + checksum: 8577a17410f4bcc9d1a921f6e567c611b71316a6aba4bc3e06a11316b915d053a589693d44350ded7de67f8f6c58b4b0075cabee88a2603ce9790e54be2a1946 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-4@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:0.70.0" +"@swagger-api/apidom-ns-json-schema-draft-4@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-ast": ^0.74.1 + "@swagger-api/apidom-core": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: b7289bba4712ea601f6874d052172218b853a14868b603414018860a203bdcd7b12944920e790db04720d7b6117d0a8859d583e6f0ec66bf525475b91ac5691c + checksum: 1d07a05dee36b1fdb238f57a22b620814fcaae21bbf46b83296048a1806a4be66901bc5aefbc42e4928d058a5df9203f9ad3fdf3f57476b6b61bb985f82bd99b languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-6@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:0.70.0" +"@swagger-api/apidom-ns-json-schema-draft-6@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-json-schema-draft-4": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-json-schema-draft-4": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: 1368fe2906aca8766265f95f8847b7f49b17ecd973ef8126ad7f24636f76cec8a2961cdac846548dbccef8202a94354b8ce5358e3b76dedc625bb23b63169fd0 + checksum: d2460a3afaebfbb304aa610e4dd5dc5fdab4325dd9ce14d393b13bb7399e3b37f4bde837266ba9876c973b6dfc5eb179c27346c2941ad2fe77923b743b3a4ebc languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-7@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:0.70.0" +"@swagger-api/apidom-ns-json-schema-draft-7@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-json-schema-draft-6": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-json-schema-draft-6": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: 0c54517aad52db2661dc083fc9593f15f57bc99f36ed601c5a1162b71010397c8d6065650959a7fb72ec4b5a06f052140c642d126e01a2061dd0d08972c68af4 + checksum: cefc04786f2ce5640397e84141ab02e921d578ae5ebd9b3d06d8ad405f1ebc8589f2069f4c57cbdf5e8b911cb6a39fd248bfb7567a66968fe62542f6f64e52eb languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-0@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:0.70.0" +"@swagger-api/apidom-ns-openapi-3-0@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-json-schema-draft-4": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-json-schema-draft-4": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: 89140a077fc90612f0bd89106d9828e65ac9637d303d142388ac566e7d5b1496be0480257a268bdc3005ab603eb7af40a3368cd6c6bbb824fba112c665b1f58c + checksum: 4c482714ce82287493d54cd454633bb044ca02659f3080084f34fc93f2f6f162cc5809499ced20a4db33092d30396d22fc80c63db080320306216a84077e26f5 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-1@npm:>=0.70.0 <1.0.0, @swagger-api/apidom-ns-openapi-3-1@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:0.70.0" +"@swagger-api/apidom-ns-openapi-3-1@npm:>=0.74.1 <1.0.0, @swagger-api/apidom-ns-openapi-3-1@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-ast": ^0.74.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-0": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - checksum: e4fd4a24fc92f753e435ef179899d27732f10065cb5c2a89b66149778bd3fa6132c2456da04a14d5441bcf4b1bb41ed86233ac981a6ae5e9f79094ca19f1598c + checksum: 1de6568ad876481ab83cec6755854314511d45bfd67f5b65f2fc44238e4bd7269f688d1931b31d939fdddfa2b29ba3442e3fe778736a777462fc517a54b8c0d9 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-api-design-systems": ^0.70.0 - "@swagger-api/apidom-parser-adapter-json": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-api-design-systems": ^0.74.1 + "@swagger-api/apidom-parser-adapter-json": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: 3bcaefa2f890f5f0783ccaff3c88da0c9eeff549182a34d74dcc4e91a14c0a36f11eafd7950141d9525295856ea68f44dded7b7d758114a9a9fa66e3f6d86984 + checksum: 223720aac5979da9ccd931cebddfa6be944c5ba269ddddde4c5a9bec24627f0cc3655d83ea54b60bc093603c2d5fb169a451d356e5dafe49be66b150c6542a8b languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-api-design-systems": ^0.70.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-api-design-systems": ^0.74.1 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: c4b06f70be760f7e840d20032a582abe55a9d06ba810ab2b5d0f71bdfd39c46fe1ac2691155731821fbc7ffd1c2fa135331151c5bfdaa73850ab2fe352c00f13 + checksum: 0072ec50f650c8e8758e87deafffd86805bb858edcb83301739301a4e8f24e6a563311b65e261573c1858f1e7772cd3a6f9a78102ade9da225f9079ebbe5fdb5 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-asyncapi-2": ^0.70.0 - "@swagger-api/apidom-parser-adapter-json": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-asyncapi-2": ^0.74.1 + "@swagger-api/apidom-parser-adapter-json": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: 06c1c3bc9d29644476b082652021e1d81b448a516a826e867c13b55b229b503d618ad2cc63bfb9a544656e1b098197e1cda89025a6c0f63c3617ffc04bc51c40 + checksum: 0ba020d883cd305fd11dde0d25714bf2b59ba7772a67b743ae15b5efd204b48be8654c2f3fd10879cb84550aa53bba0ab0186ddd3de6c590f7da5a620f416c1a languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-asyncapi-2": ^0.70.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-asyncapi-2": ^0.74.1 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: ec71cdbc0701d62b6a15c6ab9278dfc1bf1e40ef4cbf22acc0a9fb9c3b624a8b80fe0670f61e6729989c4a2e5ca5a65c03cb9cee318494eb0f1eaa6b84ce3fbe + checksum: b446ffa2ce7189df644259dbbc9bcfef39e8327fb2c47febff0abea58889069f45a47143dfc5b846c90ca9258e64d277f436b1166bdfe67c22c935170301e22c languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-json@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-json@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-json@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-json@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.70.0 - "@swagger-api/apidom-core": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-ast": ^0.74.1 + "@swagger-api/apidom-core": ^0.74.1 + "@types/ramda": ~0.29.3 node-gyp: latest ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - tree-sitter: =0.20.1 + tree-sitter: =0.20.4 tree-sitter-json: =0.20.0 - web-tree-sitter: =0.20.7 - checksum: ca256e274eeded6c64985d8b3c12ebeb94849440b58513b4e0fb20a66a1b51961b76b343f3e71b2e0e9f91fedf1c62542f52a3fa9674b62b86fdd5584f18f254 + web-tree-sitter: =0.20.3 + checksum: d80a2f9880a73b6cf6dec91bdb119eb52d57d4bce5431e4b344c734878ebec91e3350f9220b7f7eca2987ae955f236c92884f617b50631ddf97477d4bef4ab5b languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.70.0 - "@swagger-api/apidom-parser-adapter-json": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-0": ^0.74.1 + "@swagger-api/apidom-parser-adapter-json": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: 6c97390eda94565545cee33d6f39b2131c3ffd7331a37b5a5164bd6417bc2bcf5e52345d335c025ea3cbcb8d76b6c5f95cb49520291f9c54fd6c3e4b979307ec + checksum: 223e6f8589b792b47a7c21006cdfe08f4472c3e085fdc66a99528a479a46fedef536e6f27e89b75bd8f3cd983749eabaabcba60700599dad8c041ba1417600a8 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.70.0 - "@swagger-api/apidom-parser-adapter-json": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-1": ^0.74.1 + "@swagger-api/apidom-parser-adapter-json": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: b3051e10683fa89c34f4cb415f01b16c0dbc9fb1d855bcb5d0737b2a46f7b3b4f9799597c69a4fec8b547bfb2802348f38675c658d35fee4b3016ea6e73c2b95 + checksum: defdbd6e641317bd0d6508b4f92dca0216e041a6672fc48c998aef53c08aeb89591954d6a348ead1be728b9cefcc9e9e786bcba3aa7bc2659cce7e47e08b31a0 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.70.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-0": ^0.74.1 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: 1cd68a65e21eb1df397093d56ff39344cb6d1812b9c498f7142719efb5f2e7446e3288ea7f255b3c33cddec4b4ad75f04b1b9333e9816f492af5e9d96b771561 + checksum: c63fcb9501f28a1a9c6815fc71a1a5200f49822fe101665bb5ffbe4fcb52f67e68c072f268a334d5495bac4cea261206add12d0deb08594cb934d897cd1cf604 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.70.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-1": ^0.74.1 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.74.1 + "@types/ramda": ~0.29.3 ramda: ~0.29.0 ramda-adjunct: ^4.0.0 - checksum: 15ff89007ada9b38d88321aa4fddd5741d88095888e37bacb1feb31ecd1a112878e73027f3d158b9ba2a77279751dcf1a64dc8f8d56fb45c0f6c58196ef9c64a + checksum: 11893a6477013377bc60785f4e2769c5c7ddd403358c3ad2def037b75227d661cc26ec85d5a5dad9cae906696fe9091cdaab9a1d822c80c627f5cd99af1ed295 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-yaml-1-2@npm:^0.70.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-parser-adapter-yaml-1-2@npm:0.70.0" +"@swagger-api/apidom-parser-adapter-yaml-1-2@npm:^0.74.1": + version: 0.74.1 + resolution: "@swagger-api/apidom-parser-adapter-yaml-1-2@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.70.0 - "@swagger-api/apidom-core": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-ast": ^0.74.1 + "@swagger-api/apidom-core": ^0.74.1 + "@types/ramda": ~0.29.3 node-gyp: latest ramda: ~0.29.0 ramda-adjunct: ^4.0.0 stampit: ^4.3.2 - tree-sitter: =0.20.1 + tree-sitter: =0.20.4 tree-sitter-yaml: =0.5.0 - web-tree-sitter: =0.20.7 - checksum: 9f63b3053d1853fb742847171bd1eb69163f665bcd080018c283bbd8355058eb96f5ed0b2216d3c9c934e14c9dcd5202f921d033d6d240d37edaade13c1008cd + web-tree-sitter: =0.20.3 + checksum: 145a1f248f9de7a7f31593f7a9ad593c653948414e3fe70349d54b2301a7bb501765ccd595db318d00fcb5d9eafaa394e514fc68c4f69741096b750ad4f3affe languageName: node linkType: hard -"@swagger-api/apidom-reference@npm:>=0.70.0 <1.0.0": - version: 0.70.0 - resolution: "@swagger-api/apidom-reference@npm:0.70.0" +"@swagger-api/apidom-reference@npm:>=0.74.1 <1.0.0": + version: 0.74.1 + resolution: "@swagger-api/apidom-reference@npm:0.74.1" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.70.0 - "@swagger-api/apidom-json-pointer": ^0.70.0 - "@swagger-api/apidom-ns-asyncapi-2": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.70.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.70.0 - "@swagger-api/apidom-parser-adapter-api-design-systems-json": ^0.70.0 - "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": ^0.70.0 - "@swagger-api/apidom-parser-adapter-asyncapi-json-2": ^0.70.0 - "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": ^0.70.0 - "@swagger-api/apidom-parser-adapter-json": ^0.70.0 - "@swagger-api/apidom-parser-adapter-openapi-json-3-0": ^0.70.0 - "@swagger-api/apidom-parser-adapter-openapi-json-3-1": ^0.70.0 - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": ^0.70.0 - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": ^0.70.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.70.0 - "@types/ramda": ~0.29.1 + "@swagger-api/apidom-core": ^0.74.1 + "@swagger-api/apidom-json-pointer": ^0.74.1 + "@swagger-api/apidom-ns-asyncapi-2": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-0": ^0.74.1 + "@swagger-api/apidom-ns-openapi-3-1": ^0.74.1 + "@swagger-api/apidom-parser-adapter-api-design-systems-json": ^0.74.1 + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": ^0.74.1 + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": ^0.74.1 + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": ^0.74.1 + "@swagger-api/apidom-parser-adapter-json": ^0.74.1 + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": ^0.74.1 + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": ^0.74.1 + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": ^0.74.1 + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": ^0.74.1 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.74.1 + "@types/ramda": ~0.29.3 axios: ^1.4.0 minimatch: ^7.4.3 process: ^0.11.10 @@ -16057,94 +15981,94 @@ __metadata: optional: true "@swagger-api/apidom-parser-adapter-yaml-1-2": optional: true - checksum: ea8de1fa634f4f6092da5a19c015e64a7ffe45097e580dedfe1941b931a7352b3cb07410b5c83396ab10ff22fef4ad6a631a42517677e68824dc883270218ed7 + checksum: dda3f3da4fcea911bd19136635293c07e62d3ba6d4248fe3fee22eb659250631809f045a439cad03b6afc5a5472bd4ee3168bfe5be1bf53bfbd3dceed7ef60f4 languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-darwin-arm64@npm:1.3.68" +"@swc/core-darwin-arm64@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-darwin-arm64@npm:1.3.74" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-darwin-x64@npm:1.3.68" +"@swc/core-darwin-x64@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-darwin-x64@npm:1.3.74" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.68" +"@swc/core-linux-arm-gnueabihf@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.74" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.68" +"@swc/core-linux-arm64-gnu@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.74" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.68" +"@swc/core-linux-arm64-musl@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.74" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.68" +"@swc/core-linux-x64-gnu@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.74" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-linux-x64-musl@npm:1.3.68" +"@swc/core-linux-x64-musl@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-linux-x64-musl@npm:1.3.74" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.68" +"@swc/core-win32-arm64-msvc@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.74" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.68" +"@swc/core-win32-ia32-msvc@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.74" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.68": - version: 1.3.68 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.68" +"@swc/core-win32-x64-msvc@npm:1.3.74": + version: 1.3.74 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.74" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.68 - resolution: "@swc/core@npm:1.3.68" + version: 1.3.74 + resolution: "@swc/core@npm:1.3.74" dependencies: - "@swc/core-darwin-arm64": 1.3.68 - "@swc/core-darwin-x64": 1.3.68 - "@swc/core-linux-arm-gnueabihf": 1.3.68 - "@swc/core-linux-arm64-gnu": 1.3.68 - "@swc/core-linux-arm64-musl": 1.3.68 - "@swc/core-linux-x64-gnu": 1.3.68 - "@swc/core-linux-x64-musl": 1.3.68 - "@swc/core-win32-arm64-msvc": 1.3.68 - "@swc/core-win32-ia32-msvc": 1.3.68 - "@swc/core-win32-x64-msvc": 1.3.68 + "@swc/core-darwin-arm64": 1.3.74 + "@swc/core-darwin-x64": 1.3.74 + "@swc/core-linux-arm-gnueabihf": 1.3.74 + "@swc/core-linux-arm64-gnu": 1.3.74 + "@swc/core-linux-arm64-musl": 1.3.74 + "@swc/core-linux-x64-gnu": 1.3.74 + "@swc/core-linux-x64-musl": 1.3.74 + "@swc/core-win32-arm64-msvc": 1.3.74 + "@swc/core-win32-ia32-msvc": 1.3.74 + "@swc/core-win32-x64-msvc": 1.3.74 peerDependencies: "@swc/helpers": ^0.5.0 dependenciesMeta: @@ -16171,7 +16095,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: f56ad1d4cb91f7cc1cb5d4b9894bbb528da0b2eabc98984581f5b3f3187cf2c0d512003880f6ff7a3f6d215b7a3dcbf5a9c45e8a428ac2d711941027c0151d89 + checksum: fd61d65fbfceb372178a14cfa998c649481a728e33b68bad90fed0ff05b34fb432f88dafb0c2257d54c4de49cdcdd12d2b5fe66f79abc553656445910f163adb languageName: node linkType: hard @@ -16185,14 +16109,14 @@ __metadata: linkType: hard "@swc/jest@npm:^0.2.22": - version: 0.2.26 - resolution: "@swc/jest@npm:0.2.26" + version: 0.2.28 + resolution: "@swc/jest@npm:0.2.28" dependencies: "@jest/create-cache-key-function": ^27.4.2 jsonc-parser: ^3.2.0 peerDependencies: "@swc/core": "*" - checksum: 771821ed08cf168ca0b6307dee7689253d0af0685acd08408ac431860a7c42ace892db2cb6bb6dcfe297edbdce0f2e22d44ed4ed72d1c621be9e841cffd408a0 + checksum: 9f9fe7e091077079511e602afe0d0f270c5a274197fe6d55e7f8a8a0f44aadc70f726d8c2571b0eb2af8030d2bba601786adff2d811d6c01f27395c94c746edd languageName: node linkType: hard @@ -16205,18 +16129,18 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:4.29.19": - version: 4.29.19 - resolution: "@tanstack/query-core@npm:4.29.19" - checksum: 91a79dbebebc139d118542a5fe83fb54a6f6448bbe1563db1a0bed0c6d44e1d023613efde3c4ac747c72d067110013ac56f90611bf8affc390eda1ac3f99066e +"@tanstack/query-core@npm:4.32.6": + version: 4.32.6 + resolution: "@tanstack/query-core@npm:4.32.6" + checksum: c06f4b8d7edfc237d34da14c4ff2c71f4e6de662f123914419e9c2fbb9dccb7c1d3f46afd66ca5fea40687c5a99e514750a32a05e809d0db76343a90a45c76a8 languageName: node linkType: hard "@tanstack/react-query@npm:^4.1.3": - version: 4.29.19 - resolution: "@tanstack/react-query@npm:4.29.19" + version: 4.32.6 + resolution: "@tanstack/react-query@npm:4.32.6" dependencies: - "@tanstack/query-core": 4.29.19 + "@tanstack/query-core": 4.32.6 use-sync-external-store: ^1.2.0 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -16227,7 +16151,7 @@ __metadata: optional: true react-native: optional: true - checksum: 2780ac35b4d1dabcd3e708b95891376e7e609109270d40442b33f4747c508c23e46bcadc692f997f81d38f62b5ab31f8e42bc9a785dfa4afe0c390adb6f37e52 + checksum: 5ce7939515329d64aee35703de8f030bba383717e68eefb17a32492cca44d8421175b7045e0fd6314faff11cee8a570f42208685947a97bc50d0023d5ae1029e languageName: node linkType: hard @@ -16256,7 +16180,7 @@ __metadata: fs-extra: ^10.0.1 global-agent: ^3.0.0 http-proxy: ^1.18.1 - nodemon: ^2.0.2 + nodemon: ^3.0.1 react-dev-utils: ^12.0.0-next.60 serve-handler: ^6.1.3 techdocs-cli-embedded-app: "link:../techdocs-cli-embedded-app" @@ -17367,9 +17291,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.151, @types/lodash@npm:^4.14.173, @types/lodash@npm:^4.14.175": - version: 4.14.196 - resolution: "@types/lodash@npm:4.14.196" - checksum: 201d17c3e62ae02a93c99ec78e024b2be9bd75564dd8fd8c26f6ac51a985ab280d28ce2688c3bcdfe785b0991cd9814edff19ee000234c7b45d9a697f09feb6a + version: 4.14.197 + resolution: "@types/lodash@npm:4.14.197" + checksum: 53d7567d1704de76cf33266c78062e0fd722d4b846e5b1417d0b6ef0ee41c0d9c451b92bc34f73d5f1fcc45c7d36511e92f6f47a9279b48157ba60a92ddaa078 languageName: node linkType: hard @@ -17526,16 +17450,9 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": - version: 20.4.5 - resolution: "@types/node@npm:20.4.5" - checksum: 36a0304a8dc346a1b2d2edac4c4633eecf70875793d61a5274d0df052d7a7af7a8e34f29884eac4fbd094c4f0201477dcb39c0ecd3307ca141688806538d1138 - languageName: node - linkType: hard - -"@types/node@npm:12.20.24": - version: 12.20.24 - resolution: "@types/node@npm:12.20.24" - checksum: e7a13460e2f5b0b5a32c0f3af7daf1a05201552a66d542d3cc3b1ea8b52d4730250f9eb1961d755e31cfe5d03c78340911a6242657a0a9a17d6f7e341fc9f366 + version: 20.5.0 + resolution: "@types/node@npm:20.5.0" + checksum: 659bc5fc93b5c02bd88ca4bfae4f6b9dc307d45884d1dd9d69df85819a9943cdc00cd3c87eec3048866df6a67f52297f74d170e47a44f61edb3e8f770d94e85e languageName: node linkType: hard @@ -17561,16 +17478,16 @@ __metadata: linkType: hard "@types/node@npm:^16.0.0, @types/node@npm:^16.11.26, @types/node@npm:^16.9.2": - version: 16.18.39 - resolution: "@types/node@npm:16.18.39" - checksum: eac9b202b76013256cb517ca8d3e3f61df206edb1615ca8d8df4c80616e92879fe4d3f8570a11d60f4216a82724a3265d5888b24c6994c80b057a0423c9ff1d2 + version: 16.18.40 + resolution: "@types/node@npm:16.18.40" + checksum: a683930491b4fd7cb2dc7684e32bbeedc4a83fb1949a7b15ea724fbfaa9988cec59091f169a3f1090cb91992caba8c1a7d50315b2c67c6e2579a3788bb09eec4 languageName: node linkType: hard "@types/node@npm:^18.11.17": - version: 18.17.1 - resolution: "@types/node@npm:18.17.1" - checksum: 56201bda9a2d05d68602df63b4e67b0545ac8c6d0280bd5fb31701350a978a577a027501fbf49db99bf177f2242ebd1244896bfd35e89042d5bd7dfebff28d4e + version: 18.17.5 + resolution: "@types/node@npm:18.17.5" + checksum: b8c658a99234b99425243c324b641ed7b9ceb6bee6b06421fdc9bb7c58f9a5552e353225cc549e6982462ac384abe1985022ed76e2e4728797f59b21f659ca2b languageName: node linkType: hard @@ -17611,6 +17528,13 @@ __metadata: languageName: node linkType: hard +"@types/parse-link-header@npm:^2.0.1": + version: 2.0.1 + resolution: "@types/parse-link-header@npm:2.0.1" + checksum: f76678612511365aefc23704f00f3262fcb1d6bd9c4dd83f783a38e50e3ccf26615f9a62c757952cab5af6f2bd8b3b1763ce391376458afce5fe47c6fe2b80e1 + languageName: node + linkType: hard + "@types/passport-auth0@npm:^1.0.5": version: 1.0.5 resolution: "@types/passport-auth0@npm:1.0.5" @@ -17710,10 +17634,10 @@ __metadata: languageName: node linkType: hard -"@types/pluralize@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/pluralize@npm:0.0.29" - checksum: db7732b733ba1dc59f080f87364c9f985c1ae9f1e5ec5fcefb83c1327149e01ecac42766352eb7b19b117cf39defa99cf514629f3666968a3bdeb00e74f2a97e +"@types/pluralize@npm:^0.0.30": + version: 0.0.30 + resolution: "@types/pluralize@npm:0.0.30" + checksum: 7a9a4a24aac1f74bd63e592a824ebec114486e0635a94496e92aa37fa388fac585be108d3ccfc7b2c490963679ec4900fbf394d22fa6cbf419f22cc499ab5a29 languageName: node linkType: hard @@ -17747,12 +17671,12 @@ __metadata: languageName: node linkType: hard -"@types/ramda@npm:~0.29.1": - version: 0.29.2 - resolution: "@types/ramda@npm:0.29.2" +"@types/ramda@npm:~0.29.3": + version: 0.29.3 + resolution: "@types/ramda@npm:0.29.3" dependencies: - types-ramda: ^0.29.3 - checksum: 152151848a02aca8b7eb7e95e247a10c8e0f12e07d096a25c4c230cc3bd704009670c735737d3f843373a4598869c2b2d6f81e425fa17c762e4b57808400d968 + types-ramda: ^0.29.4 + checksum: 172b18d62473d4bffb1e4e92fa293e56fa4f85fcf91b1b2c9178955f775e34065cc408e93f1171436b52e240599ecb1be51955dab07ca389bf2da98f85f820a9 languageName: node linkType: hard @@ -17821,11 +17745,11 @@ __metadata: linkType: hard "@types/react-syntax-highlighter@npm:^15.0.0": - version: 15.5.6 - resolution: "@types/react-syntax-highlighter@npm:15.5.6" + version: 15.5.7 + resolution: "@types/react-syntax-highlighter@npm:15.5.7" dependencies: "@types/react": "*" - checksum: 81a9f26da41606ff595d42a064ec4097cd17c4ef242558aa5b3b76a1063729b2ae73f063350069a4692218cf6843d7ea59d3eaeac4df2a4894d969d911fb80b2 + checksum: 1918d01baaa9bf485093fb04167d0dc87131be708bd68d32d3f614c0e7dba05de765fc62df139fa1972836b13e27983a2d89552eda5b5a38691a4ec300949648 languageName: node linkType: hard @@ -18359,9 +18283,9 @@ __metadata: linkType: hard "@types/yarnpkg__lockfile@npm:^1.1.4": - version: 1.1.5 - resolution: "@types/yarnpkg__lockfile@npm:1.1.5" - checksum: 2aa761eebea2e8780ddc9d2819a356451d04166e3ea4fad3fd43d81e25a68806edf884f71aef12f77f0a256eb98aa152d32a8af16e32d04a918424d10cb6d858 + version: 1.1.6 + resolution: "@types/yarnpkg__lockfile@npm:1.1.6" + checksum: c0983d88c479f7b00a66d927fda9f8972e072745d5d16922cf381807aa6e88553319bb9f2b21b58f9e1ebea1a198a22d4da3111d93c81288589371ebb00d65f5 languageName: node linkType: hard @@ -18836,34 +18760,25 @@ __metadata: languageName: node linkType: hard -"@wry/context@npm:^0.7.0": - version: 0.7.0 - resolution: "@wry/context@npm:0.7.0" +"@wry/context@npm:^0.7.0, @wry/context@npm:^0.7.3": + version: 0.7.3 + resolution: "@wry/context@npm:0.7.3" dependencies: tslib: ^2.3.0 - checksum: f4ff78023a0b949122037aae766232b7d2284dc415204d22d9ea6d7969ff8f5f29b18128bc9a40e68dc054c8a12b1bf5868a357fdb50c398c447290c3a5b0496 + checksum: 91c1e9eee9046c48ff857d60dcbb59f22246ce0f9bb2d9b190e0555227e7ba3f86024032cc057f3f5141d3bee93fc6b2a15ce2c79fa512569d3432eb8e1af02b languageName: node linkType: hard -"@wry/equality@npm:^0.5.0": - version: 0.5.3 - resolution: "@wry/equality@npm:0.5.3" +"@wry/equality@npm:^0.5.6": + version: 0.5.6 + resolution: "@wry/equality@npm:0.5.6" dependencies: tslib: ^2.3.0 - checksum: 7ea8ded51462911217183b93cc3ffbb4d18dc02a62d4a79e0d9983463739bf54106aaeb25649bf33168120bd044b61d135018bfcf4fefad8099c13eac9238aa6 + checksum: 9addf8891bdff5e23eecff03641846e7a56c1de3c9362c25e69c0b2ee3303e74b22e9a0376920283cd9d3bdd1bada12df54be5eaa29c2d801d33d94992672e14 languageName: node linkType: hard -"@wry/trie@npm:^0.3.0": - version: 0.3.2 - resolution: "@wry/trie@npm:0.3.2" - dependencies: - tslib: ^2.3.0 - checksum: 151d06b519e1ff1c3acf6ee6846161b1d7d50bbecd4c48e5cd1b05f9e37c30602aff02e88f20105f6e6c54ae4123f9c4eb7715044d7fd927d4ba4ec3e755cd36 - languageName: node - linkType: hard - -"@wry/trie@npm:^0.4.0": +"@wry/trie@npm:^0.4.3": version: 0.4.3 resolution: "@wry/trie@npm:0.4.3" dependencies: @@ -19154,7 +19069,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.10.1, ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.5.5, ajv@npm:^6.7.0, ajv@npm:~6.12.6": +"ajv@npm:^6.10.1, ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.5.5, ajv@npm:^6.7.0, ajv@npm:~6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -19445,6 +19360,16 @@ __metadata: languageName: node linkType: hard +"array-buffer-byte-length@npm:^1.0.0": + version: 1.0.0 + resolution: "array-buffer-byte-length@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + is-array-buffer: ^3.0.1 + checksum: 044e101ce150f4804ad19c51d6c4d4cfa505c5b2577bd179256e4aa3f3f6a0a5e9874c78cd428ee566ac574c8a04d7ce21af9fe52e844abfdccb82b33035a7c3 + languageName: node + linkType: hard + "array-differ@npm:^3.0.0": version: 3.0.0 resolution: "array-differ@npm:3.0.0" @@ -19493,6 +19418,19 @@ __metadata: languageName: node linkType: hard +"array.prototype.findlastindex@npm:^1.2.2": + version: 1.2.2 + resolution: "array.prototype.findlastindex@npm:1.2.2" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + es-shim-unscopables: ^1.0.0 + get-intrinsic: ^1.1.3 + checksum: 8a166359f69a2a751c843f26b9c8cd03d0dc396a92cdcb85f4126b5f1cecdae5b2c0c616a71ea8aff026bde68165b44950b3664404bb73db0673e288495ba264 + languageName: node + linkType: hard + "array.prototype.flat@npm:^1.2.3, array.prototype.flat@npm:^1.3.1": version: 1.3.1 resolution: "array.prototype.flat@npm:1.3.1" @@ -19530,6 +19468,20 @@ __metadata: languageName: node linkType: hard +"arraybuffer.prototype.slice@npm:^1.0.1": + version: 1.0.1 + resolution: "arraybuffer.prototype.slice@npm:1.0.1" + dependencies: + array-buffer-byte-length: ^1.0.0 + call-bind: ^1.0.2 + define-properties: ^1.2.0 + get-intrinsic: ^1.2.1 + is-array-buffer: ^3.0.2 + is-shared-array-buffer: ^1.0.2 + checksum: e3e9b2a3e988ebfeddce4c7e8f69df730c9e48cb04b0d40ff0874ce3d86b3d1339dd520ffde5e39c02610bc172ecfbd4bc93324b1cabd9554c44a56b131ce0ce + languageName: node + linkType: hard + "arrify@npm:^1.0.1": version: 1.0.1 resolution: "arrify@npm:1.0.1" @@ -19675,6 +19627,15 @@ __metadata: languageName: node linkType: hard +"asynciterator.prototype@npm:^1.0.0": + version: 1.0.0 + resolution: "asynciterator.prototype@npm:1.0.0" + dependencies: + has-symbols: ^1.0.3 + checksum: e8ebfd9493ac651cf9b4165e9d64030b3da1d17181bb1963627b59e240cdaf021d9b59d44b827dc1dde4e22387ec04c2d0f8720cf58a1c282e34e40cc12721b3 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -20180,13 +20141,13 @@ __metadata: linkType: hard "better-sqlite3@npm:^8.0.0": - version: 8.4.0 - resolution: "better-sqlite3@npm:8.4.0" + version: 8.5.0 + resolution: "better-sqlite3@npm:8.5.0" dependencies: bindings: ^1.5.0 node-gyp: latest prebuild-install: ^7.1.0 - checksum: f8b180c26428a2d381482e83b4519d0d81a918e00f92cafd255f42eb9583f49b2fed1015121ad49ebe1f3f790cc8c6f4697d1e805193d65e70dacf2e8abbd6af + checksum: 98243cb08a410fad0b5c443410428c8ea00376a4eb7d2c9a8a5a695970bc7a2a4007221930460939594d728e7d6bcb1889f46ec11a3bbb2257b996c80fbaacb1 languageName: node linkType: hard @@ -20292,7 +20253,7 @@ __metadata: languageName: node linkType: hard -"bluebird@npm:3.7.2, bluebird@npm:^3.3.5, bluebird@npm:^3.5.5, bluebird@npm:^3.7.2": +"bluebird@npm:3.7.2, bluebird@npm:^3.5.5, bluebird@npm:^3.7.2": version: 3.7.2 resolution: "bluebird@npm:3.7.2" checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef @@ -20832,13 +20793,6 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^3.0.0": - version: 3.0.0 - resolution: "camelcase@npm:3.0.0" - checksum: ae4fe1c17c8442a3a345a6b7d2393f028ab7a7601af0c352ad15d1ab97ca75112e19e29c942b2a214898e160194829b68923bce30e018d62149c6d84187f1673 - languageName: node - linkType: hard - "camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" @@ -21139,17 +21093,10 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^2.0.0": - version: 2.0.0 - resolution: "ci-info@npm:2.0.0" - checksum: 3b374666a85ea3ca43fa49aa3a048d21c9b475c96eb13c133505d2324e7ae5efd6a454f41efe46a152269e9b6a00c9edbe63ec7fa1921957165aae16625acd67 - languageName: node - linkType: hard - -"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0": - version: 3.3.2 - resolution: "ci-info@npm:3.3.2" - checksum: fd81f1edd2d3b0f6cb077b2e84365136d87b9db8c055928c1ad69da8a76c2c2f19cba8ea51b90238302157ca927f91f92b653e933f2398dde4867500f08d6e62 +"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0, ci-info@npm:^3.7.0": + version: 3.8.0 + resolution: "ci-info@npm:3.8.0" + checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard @@ -21283,6 +21230,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "clipboard@npm:^2.0.4": version: 2.0.11 resolution: "clipboard@npm:2.0.11" @@ -21305,17 +21259,6 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^3.2.0": - version: 3.2.0 - resolution: "cliui@npm:3.2.0" - dependencies: - string-width: ^1.0.1 - strip-ansi: ^3.0.1 - wrap-ansi: ^2.0.0 - checksum: c68d1dbc3e347bfe79ed19cc7f48007d5edd6cd8438342e32073e0b4e311e3c44e1f4f19221462bc6590de56c2df520e427533a9dde95dee25710bec322746ad - languageName: node - linkType: hard - "cliui@npm:^6.0.0": version: 6.0.0 resolution: "cliui@npm:6.0.0" @@ -21327,6 +21270,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.1 + wrap-ansi: ^7.0.0 + checksum: 79648b3b0045f2e285b76fb2e24e207c6db44323581e421c3acbd0e86454cba1b37aea976ab50195a49e7384b871e6dfb2247ad7dec53c02454ac6497394cb56 + languageName: node + linkType: hard + "clone-buffer@npm:^1.0.0": version: 1.0.0 resolution: "clone-buffer@npm:1.0.0" @@ -21883,23 +21837,23 @@ __metadata: languageName: node linkType: hard -"concurrently@npm:^7.0.0": - version: 7.6.0 - resolution: "concurrently@npm:7.6.0" +"concurrently@npm:^8.0.0": + version: 8.2.0 + resolution: "concurrently@npm:8.2.0" dependencies: - chalk: ^4.1.0 - date-fns: ^2.29.1 + chalk: ^4.1.2 + date-fns: ^2.30.0 lodash: ^4.17.21 - rxjs: ^7.0.0 - shell-quote: ^1.7.3 - spawn-command: ^0.0.2-1 - supports-color: ^8.1.0 + rxjs: ^7.8.1 + shell-quote: ^1.8.1 + spawn-command: 0.0.2 + supports-color: ^8.1.1 tree-kill: ^1.2.2 - yargs: ^17.3.1 + yargs: ^17.7.2 bin: conc: dist/bin/concurrently.js concurrently: dist/bin/concurrently.js - checksum: f705c9a7960f1b16559ca64958043faeeef6385c0bf30a03d1375e15ab2d96dba4f8166f1bbbb1c85e8da35ca0ce3c353875d71dff2aa132b2357bb533b3332e + checksum: eafe6a4d9b7fda87f55ea285cfc6acd937a5286ceec8991ab48e6cc27c45fce6a5c6f45e18d7555defa15dc7d7e8941bc5a9d1ceaf182e31441d420e00333434 languageName: node linkType: hard @@ -22016,7 +21970,7 @@ __metadata: languageName: node linkType: hard -"cookie-parser@npm:^1.4.5": +"cookie-parser@npm:^1.4.5, cookie-parser@npm:^1.4.6": version: 1.4.6 resolution: "cookie-parser@npm:1.4.6" dependencies: @@ -22102,9 +22056,9 @@ __metadata: linkType: hard "core-js@npm:^3.26.0, core-js@npm:^3.6.5": - version: 3.31.0 - resolution: "core-js@npm:3.31.0" - checksum: f7cf9b3010f7ca99c026d95b61743baca1a85512742ed2b67e8f65a72ac4f4fe0b90b00057783e886bdd39d3a295f42f845d33e7cba3973ed263df978343ab79 + version: 3.32.0 + resolution: "core-js@npm:3.32.0" + checksum: 52921395028550e4c9d21d47b9836439bb5b6b9eefc34d45a3948a68d81fdd093acc0fadf69f9cf632b82f01f95f22f484408a93dd9e940b19119ac204cd2925 languageName: node linkType: hard @@ -22247,20 +22201,20 @@ __metadata: linkType: hard "cron@npm:^2.0.0": - version: 2.3.1 - resolution: "cron@npm:2.3.1" + version: 2.4.1 + resolution: "cron@npm:2.4.1" dependencies: luxon: ^3.2.1 - checksum: ac27c257cb8dba4b51978b4767119fc79bfc0dfa35ef0be2533bc4142ae006130515236e2102d04ee3ce44bffc2a4e37a3bee18a2152bbb303bf5ff944e25204 + checksum: a421a837f99cf18da2f51e3608610f71817c38e6f756b4c85a26f64521dac327ac79861ae3dcf991076f72f3be0daadfffd413b4be0c3b4b029509b89e86b6d4 languageName: node linkType: hard "cronstrue@npm:^2.2.0": - version: 2.27.0 - resolution: "cronstrue@npm:2.27.0" + version: 2.29.0 + resolution: "cronstrue@npm:2.29.0" bin: cronstrue: bin/cli.js - checksum: 310d933c4448896519c203a434dc1ede7029967df941e810f02bafb62146045c16df4987edde344d757bafb6d45f63c9ecb7385df19b985ca91486f4efa6ff7b + checksum: 92b06f98e4ef9b1b7c671f8752d9ddc1f1da8097ed0f976a6085e4e56acaed490a69f5095853eca0a59987a28edb85a9fc2f0831dfa96db1ab481e0277405dd5 languageName: node linkType: hard @@ -22305,19 +22259,6 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^6.0.5": - version: 6.0.5 - resolution: "cross-spawn@npm:6.0.5" - dependencies: - nice-try: ^1.0.4 - path-key: ^2.0.1 - semver: ^5.5.0 - shebang-command: ^1.2.0 - which: ^1.2.9 - checksum: f893bb0d96cd3d5751d04e67145bdddf25f99449531a72e82dcbbd42796bbc8268c1076c6b3ea51d4d455839902804b94bc45dfb37ecbb32ea8e54a6741c3ab9 - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -22935,17 +22876,19 @@ __metadata: languageName: node linkType: hard -"dataloader@npm:2.2.2, dataloader@npm:^2.0.0, dataloader@npm:^2.2.2": +"dataloader@npm:^2.0.0, dataloader@npm:^2.2.2": version: 2.2.2 resolution: "dataloader@npm:2.2.2" checksum: 4dabd247089c29f194e94d5434d504f99156c5c214a03463c20f3f17f40398d7e179edee69a27c16e315519ac8739042a810090087ae26449a0e685156a02c65 languageName: node linkType: hard -"date-fns@npm:^2.16.1, date-fns@npm:^2.18.0, date-fns@npm:^2.29.1": - version: 2.29.3 - resolution: "date-fns@npm:2.29.3" - checksum: e01cf5b62af04e05dfff921bb9c9933310ed0e1ae9a81eb8653452e64dc841acf7f6e01e1a5ae5644d0337e9a7f936175fd2cb6819dc122fdd9c5e86c56be484 +"date-fns@npm:^2.16.1, date-fns@npm:^2.18.0, date-fns@npm:^2.30.0": + version: 2.30.0 + resolution: "date-fns@npm:2.30.0" + dependencies: + "@babel/runtime": ^7.21.0 + checksum: f7be01523282e9bb06c0cd2693d34f245247a29098527d4420628966a2d9aad154bd0e90a6b1cf66d37adcb769cd108cf8a7bd49d76db0fb119af5cdd13644f4 languageName: node linkType: hard @@ -23024,7 +22967,7 @@ __metadata: languageName: node linkType: hard -"decamelize@npm:^1.1.0, decamelize@npm:^1.1.1, decamelize@npm:^1.2.0": +"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa @@ -23171,13 +23114,13 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4": - version: 1.1.4 - resolution: "define-properties@npm:1.1.4" +"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": + version: 1.2.0 + resolution: "define-properties@npm:1.2.0" dependencies: has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b + checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 languageName: node linkType: hard @@ -23586,10 +23529,10 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:=3.0.2": - version: 3.0.2 - resolution: "dompurify@npm:3.0.2" - checksum: 768c3bb2f139ce1e1a53faf81aae6abe03e24d0b043e2fda0b70e91ef15bb1df854a35e82d8c519adfe5b81c24bfdad3e0b1085534bfbf427137cb742406c47f +"dompurify@npm:=3.0.5": + version: 3.0.5 + resolution: "dompurify@npm:3.0.5" + checksum: 2d9421570c833ce26ce7022955241749b646d41e8bf453f42ede9f22d0e98af482cedb7dfbf8129419eb48b351c1d677a08fc9f1cd91836ce7f6c1807a0676b2 languageName: node linkType: hard @@ -23655,7 +23598,7 @@ __metadata: languageName: node linkType: hard -"dset@npm:3.1.2": +"dset@npm:3.1.2, dset@npm:^3.1.2": version: 3.1.2 resolution: "dset@npm:3.1.2" checksum: 4f8066f517aa0a70af688c66e9a0a5590f0aada76f6edc7ba9ddb309e27d3a6d65c0a2e31ab2a84005d4c791e5327773cdde59b8ab169050330a0dc283663e87 @@ -23697,8 +23640,8 @@ __metadata: cross-fetch: ^3.1.5 fs-extra: 10.1.0 handlebars: ^4.7.3 - nodemon: ^2.0.2 - pgtools: ^0.3.0 + nodemon: ^3.0.1 + pgtools: ^1.0.0 puppeteer: ^17.0.0 tree-kill: ^1.2.2 ts-node: ^10.0.0 @@ -23945,7 +23888,7 @@ __metadata: languageName: node linkType: hard -"error-ex@npm:^1.2.0, error-ex@npm:^1.3.1": +"error-ex@npm:^1.3.1": version: 1.3.2 resolution: "error-ex@npm:1.3.2" dependencies: @@ -23970,35 +23913,50 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.4": - version: 1.20.4 - resolution: "es-abstract@npm:1.20.4" +"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2, es-abstract@npm:^1.21.3": + version: 1.22.1 + resolution: "es-abstract@npm:1.22.1" dependencies: + array-buffer-byte-length: ^1.0.0 + arraybuffer.prototype.slice: ^1.0.1 + available-typed-arrays: ^1.0.5 call-bind: ^1.0.2 + es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 - function-bind: ^1.1.1 function.prototype.name: ^1.1.5 - get-intrinsic: ^1.1.3 + get-intrinsic: ^1.2.1 get-symbol-description: ^1.0.0 + globalthis: ^1.0.3 + gopd: ^1.0.1 has: ^1.0.3 has-property-descriptors: ^1.0.0 + has-proto: ^1.0.1 has-symbols: ^1.0.3 - internal-slot: ^1.0.3 + internal-slot: ^1.0.5 + is-array-buffer: ^3.0.2 is-callable: ^1.2.7 is-negative-zero: ^2.0.2 is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 + is-typed-array: ^1.1.10 is-weakref: ^1.0.2 - object-inspect: ^1.12.2 + object-inspect: ^1.12.3 object-keys: ^1.1.1 object.assign: ^4.1.4 - regexp.prototype.flags: ^1.4.3 + regexp.prototype.flags: ^1.5.0 + safe-array-concat: ^1.0.0 safe-regex-test: ^1.0.0 - string.prototype.trimend: ^1.0.5 - string.prototype.trimstart: ^1.0.5 + string.prototype.trim: ^1.2.7 + string.prototype.trimend: ^1.0.6 + string.prototype.trimstart: ^1.0.6 + typed-array-buffer: ^1.0.0 + typed-array-byte-length: ^1.0.0 + typed-array-byte-offset: ^1.0.0 + typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - checksum: 89297cc785c31aedf961a603d5a07ed16471e435d3a1b6d070b54f157cf48454b95cda2ac55e4b86ff4fe3276e835fcffd2771578e6fa634337da49b26826141 + which-typed-array: ^1.1.10 + checksum: 614e2c1c3717cb8d30b6128ef12ea110e06fd7d75ad77091ca1c5dbfb00da130e62e4bbbbbdda190eada098a22b27fe0f99ae5a1171dac2c8663b1e8be8a3a9b languageName: node linkType: hard @@ -24033,6 +23991,24 @@ __metadata: languageName: node linkType: hard +"es-iterator-helpers@npm:^1.0.12": + version: 1.0.12 + resolution: "es-iterator-helpers@npm:1.0.12" + dependencies: + asynciterator.prototype: ^1.0.0 + es-abstract: ^1.21.3 + es-set-tostringtag: ^2.0.1 + function-bind: ^1.1.1 + globalthis: ^1.0.3 + has-proto: ^1.0.1 + has-symbols: ^1.0.3 + internal-slot: ^1.0.5 + iterator.prototype: ^1.1.0 + safe-array-concat: ^1.0.0 + checksum: df1800a64be755e61718d2e62eef17c22590ed668c44eb3a5c9b78f874e8e01bfa7ba27a0f258b1322f577c2b2032fec194aa20912f7417ddc580c4ec31882e4 + languageName: node + linkType: hard + "es-module-lexer@npm:^0.9.3": version: 0.9.3 resolution: "es-module-lexer@npm:0.9.3" @@ -24047,6 +24023,17 @@ __metadata: languageName: node linkType: hard +"es-set-tostringtag@npm:^2.0.1": + version: 2.0.1 + resolution: "es-set-tostringtag@npm:2.0.1" + dependencies: + get-intrinsic: ^1.1.3 + has: ^1.0.3 + has-tostringtag: ^1.0.0 + checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 + languageName: node + linkType: hard + "es-shim-unscopables@npm:^1.0.0": version: 1.0.0 resolution: "es-shim-unscopables@npm:1.0.0" @@ -24314,32 +24301,32 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0": - version: 0.18.17 - resolution: "esbuild@npm:0.18.17" +"esbuild@npm:^0.19.0": + version: 0.19.2 + resolution: "esbuild@npm:0.19.2" dependencies: - "@esbuild/android-arm": 0.18.17 - "@esbuild/android-arm64": 0.18.17 - "@esbuild/android-x64": 0.18.17 - "@esbuild/darwin-arm64": 0.18.17 - "@esbuild/darwin-x64": 0.18.17 - "@esbuild/freebsd-arm64": 0.18.17 - "@esbuild/freebsd-x64": 0.18.17 - "@esbuild/linux-arm": 0.18.17 - "@esbuild/linux-arm64": 0.18.17 - "@esbuild/linux-ia32": 0.18.17 - "@esbuild/linux-loong64": 0.18.17 - "@esbuild/linux-mips64el": 0.18.17 - "@esbuild/linux-ppc64": 0.18.17 - "@esbuild/linux-riscv64": 0.18.17 - "@esbuild/linux-s390x": 0.18.17 - "@esbuild/linux-x64": 0.18.17 - "@esbuild/netbsd-x64": 0.18.17 - "@esbuild/openbsd-x64": 0.18.17 - "@esbuild/sunos-x64": 0.18.17 - "@esbuild/win32-arm64": 0.18.17 - "@esbuild/win32-ia32": 0.18.17 - "@esbuild/win32-x64": 0.18.17 + "@esbuild/android-arm": 0.19.2 + "@esbuild/android-arm64": 0.19.2 + "@esbuild/android-x64": 0.19.2 + "@esbuild/darwin-arm64": 0.19.2 + "@esbuild/darwin-x64": 0.19.2 + "@esbuild/freebsd-arm64": 0.19.2 + "@esbuild/freebsd-x64": 0.19.2 + "@esbuild/linux-arm": 0.19.2 + "@esbuild/linux-arm64": 0.19.2 + "@esbuild/linux-ia32": 0.19.2 + "@esbuild/linux-loong64": 0.19.2 + "@esbuild/linux-mips64el": 0.19.2 + "@esbuild/linux-ppc64": 0.19.2 + "@esbuild/linux-riscv64": 0.19.2 + "@esbuild/linux-s390x": 0.19.2 + "@esbuild/linux-x64": 0.19.2 + "@esbuild/netbsd-x64": 0.19.2 + "@esbuild/openbsd-x64": 0.19.2 + "@esbuild/sunos-x64": 0.19.2 + "@esbuild/win32-arm64": 0.19.2 + "@esbuild/win32-ia32": 0.19.2 + "@esbuild/win32-x64": 0.19.2 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -24387,7 +24374,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: c6e1ffa776978a45697763a07ec9b16411db3d3b3997b2c4a0165a211727fce8b63b87165a28d8ef60d3a28b98197bbbc2833e51b89888a4437e0a483dffc8ff + checksum: f9ad8ad4f0cbcc675c059f2676c4458d75307af20f9168859de8642accd7f2b7d6bbe8286a23633790dcba07d1d66a8f63c204ea933a0d51300c1b69d4f25d8f languageName: node linkType: hard @@ -24549,13 +24536,13 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.3.0": - version: 8.8.0 - resolution: "eslint-config-prettier@npm:8.8.0" + version: 8.10.0 + resolution: "eslint-config-prettier@npm:8.10.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: 1e94c3882c4d5e41e1dcfa2c368dbccbfe3134f6ac7d40101644d3bfbe3eb2f2ffac757f3145910b5eacf20c0e85e02b91293d3126d770cbf3dc390b3564681c + checksum: 153266badd477e49b0759816246b2132f1dbdb6c7f313ca60a9af5822fd1071c2bc5684a3720d78b725452bbac04bb130878b2513aea5e72b1b792de5a69fec8 languageName: node linkType: hard @@ -24583,32 +24570,32 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.7.4": - version: 2.7.4 - resolution: "eslint-module-utils@npm:2.7.4" +"eslint-module-utils@npm:^2.8.0": + version: 2.8.0 + resolution: "eslint-module-utils@npm:2.8.0" dependencies: debug: ^3.2.7 peerDependenciesMeta: eslint: optional: true - checksum: 5da13645daff145a5c922896b258f8bba560722c3767254e458d894ff5fbb505d6dfd945bffa932a5b0ae06714da2379bd41011c4c20d2d59cc83e23895360f7 + checksum: 74c6dfea7641ebcfe174be61168541a11a14aa8d72e515f5f09af55cd0d0862686104b0524aa4b8e0ce66418a44aa38a94d2588743db5fd07a6b49ffd16921d2 languageName: node linkType: hard "eslint-plugin-cypress@npm:^2.10.3": - version: 2.13.3 - resolution: "eslint-plugin-cypress@npm:2.13.3" + version: 2.14.0 + resolution: "eslint-plugin-cypress@npm:2.14.0" dependencies: - globals: ^11.12.0 + globals: ^13.20.0 peerDependencies: eslint: ">= 3.2.1" - checksum: 9affbcee29e030a4251c4794f7533e8e8c0e3b98ab3470a2c730ed059f733c5857a04c7ac214cc0ca7aeef1b11242e72595de7fc1f6b8b4d4578d9eca10af203 + checksum: 3fa118a757aebb1aa6b419b2944744796aa4fa3cc1e2e19fa97777fd6792fba12b5ae117bf19bf7e7d9a1abdd48398cfba9ca6f2c62fd690a2108a9a02f3f2ae languageName: node linkType: hard "eslint-plugin-deprecation@npm:^1.3.2": - version: 1.4.1 - resolution: "eslint-plugin-deprecation@npm:1.4.1" + version: 1.5.0 + resolution: "eslint-plugin-deprecation@npm:1.5.0" dependencies: "@typescript-eslint/utils": ^5.57.0 tslib: ^2.3.1 @@ -24616,32 +24603,35 @@ __metadata: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: ^3.7.5 || ^4.0.0 || ^5.0.0 - checksum: 75c7535d820d1749664705724cc979d706da126a2277f2937467f70156a2c220b10c66670f18226801c9555e4cd02312d353936f14d5752c2d2c648455fe5769 + checksum: ec0ff3df1dbbbb85d14c8f6656bb126377280db58789c2ba3c4500250b291559c651a0fb2ac29aa977408fef3a919ad41e706100b55672ceb6c1ad09550e7396 languageName: node linkType: hard "eslint-plugin-import@npm:^2.25.4": - version: 2.27.5 - resolution: "eslint-plugin-import@npm:2.27.5" + version: 2.28.0 + resolution: "eslint-plugin-import@npm:2.28.0" dependencies: array-includes: ^3.1.6 + array.prototype.findlastindex: ^1.2.2 array.prototype.flat: ^1.3.1 array.prototype.flatmap: ^1.3.1 debug: ^3.2.7 doctrine: ^2.1.0 eslint-import-resolver-node: ^0.3.7 - eslint-module-utils: ^2.7.4 + eslint-module-utils: ^2.8.0 has: ^1.0.3 - is-core-module: ^2.11.0 + is-core-module: ^2.12.1 is-glob: ^4.0.3 minimatch: ^3.1.2 + object.fromentries: ^2.0.6 + object.groupby: ^1.0.0 object.values: ^1.1.6 - resolve: ^1.22.1 - semver: ^6.3.0 - tsconfig-paths: ^3.14.1 + resolve: ^1.22.3 + semver: ^6.3.1 + tsconfig-paths: ^3.14.2 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: f500571a380167e25d72a4d925ef9a7aae8899eada57653e5f3051ec3d3c16d08271fcefe41a30a9a2f4fefc232f066253673ee4ea77b30dba65ae173dade85d + checksum: f9eba311b93ca1bb89311856b1f7285bd79e0181d7eb70fe115053ff77e2235fea749b30f538b78927dc65769340b5be61f4c9581d1c82bcdcccb2061f440ad1 languageName: node linkType: hard @@ -24712,13 +24702,14 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.28.0": - version: 7.32.2 - resolution: "eslint-plugin-react@npm:7.32.2" + version: 7.33.2 + resolution: "eslint-plugin-react@npm:7.33.2" dependencies: array-includes: ^3.1.6 array.prototype.flatmap: ^1.3.1 array.prototype.tosorted: ^1.1.1 doctrine: ^2.1.0 + es-iterator-helpers: ^1.0.12 estraverse: ^5.3.0 jsx-ast-utils: ^2.4.1 || ^3.0.0 minimatch: ^3.1.2 @@ -24728,22 +24719,22 @@ __metadata: object.values: ^1.1.6 prop-types: ^15.8.1 resolve: ^2.0.0-next.4 - semver: ^6.3.0 + semver: ^6.3.1 string.prototype.matchall: ^4.0.8 peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 2232b3b8945aa50b7773919c15cd96892acf35d2f82503667a79e2f55def90f728ed4f0e496f0f157acbe1bd4397c5615b676ae7428fe84488a544ca53feb944 + checksum: b4c3d76390b0ae6b6f9fed78170604cc2c04b48e6778a637db339e8e3911ec9ef22510b0ae77c429698151d0f1b245f282177f384105b6830e7b29b9c9b26610 languageName: node linkType: hard "eslint-plugin-testing-library@npm:^5.9.1": - version: 5.11.0 - resolution: "eslint-plugin-testing-library@npm:5.11.0" + version: 5.11.1 + resolution: "eslint-plugin-testing-library@npm:5.11.1" dependencies: "@typescript-eslint/utils": ^5.58.0 peerDependencies: eslint: ^7.5.0 || ^8.0.0 - checksum: 7f19d3dedd7788b411ca3d9045de682feb26025b9c26d97d4e2f0bf62f5eaa276147d946bd5d0cd967b822e546a954330fdb7ef80485301264f646143f011a02 + checksum: 9f3fc68ef9f13016a4381b33ab5dbffcc189e5de3eaeba184bcf7d2771faa7f54e59c04b652162fb1c0f83fb52428dd909db5450a25508b94be59eba69fcc990 languageName: node linkType: hard @@ -24757,13 +24748,13 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^7.2.0": - version: 7.2.0 - resolution: "eslint-scope@npm:7.2.0" +"eslint-scope@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" dependencies: esrecurse: ^4.3.0 estraverse: ^5.2.0 - checksum: 64591a2d8b244ade9c690b59ef238a11d5c721a98bcee9e9f445454f442d03d3e04eda88e95a4daec558220a99fa384309d9faae3d459bd40e7a81b4063980ae + checksum: ec97dbf5fb04b94e8f4c5a91a7f0a6dd3c55e46bfc7bbcd0e3138c3a76977570e02ed89a1810c778dcd72072ff0e9621ba1379b4babe53921d71e2e4486fda3e languageName: node linkType: hard @@ -24785,10 +24776,10 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": - version: 3.4.1 - resolution: "eslint-visitor-keys@npm:3.4.1" - checksum: f05121d868202736b97de7d750847a328fcfa8593b031c95ea89425333db59676ac087fa905eba438d0a3c5769632f828187e0c1a0d271832a2153c1d3661c2c +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2": + version: 3.4.2 + resolution: "eslint-visitor-keys@npm:3.4.2" + checksum: 9e0e7e4aaea705c097ae37c97410e5f167d4d2193be2edcb1f0760762ede3df01545e4820ae314f42dcec687745f2c6dcaf6d83575c4a2a241eb0c8517d724f2 languageName: node linkType: hard @@ -24809,25 +24800,25 @@ __metadata: linkType: hard "eslint@npm:^8.33.0, eslint@npm:^8.6.0": - version: 8.44.0 - resolution: "eslint@npm:8.44.0" + version: 8.46.0 + resolution: "eslint@npm:8.46.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 - "@eslint-community/regexpp": ^4.4.0 - "@eslint/eslintrc": ^2.1.0 - "@eslint/js": 8.44.0 + "@eslint-community/regexpp": ^4.6.1 + "@eslint/eslintrc": ^2.1.1 + "@eslint/js": ^8.46.0 "@humanwhocodes/config-array": ^0.11.10 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 - ajv: ^6.10.0 + ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 debug: ^4.3.2 doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 - eslint-scope: ^7.2.0 - eslint-visitor-keys: ^3.4.1 - espree: ^9.6.0 + eslint-scope: ^7.2.2 + eslint-visitor-keys: ^3.4.2 + espree: ^9.6.1 esquery: ^1.4.2 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 @@ -24837,7 +24828,6 @@ __metadata: globals: ^13.19.0 graphemer: ^1.4.0 ignore: ^5.2.0 - import-fresh: ^3.0.0 imurmurhash: ^0.1.4 is-glob: ^4.0.0 is-path-inside: ^3.0.3 @@ -24849,11 +24839,10 @@ __metadata: natural-compare: ^1.4.0 optionator: ^0.9.3 strip-ansi: ^6.0.1 - strip-json-comments: ^3.1.0 text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: d06309ce4aafb9d27d558c8e5e5aa5cba3bbec3ce8ceccbc7d4b7a35f2b67fd40189159155553270e2e6febeb69bd8a3b60d6241c8f5ddc2ef1702ccbd328501 + checksum: 7a7d36b1a3bbc12e08fbb5bc36fd482a7a5a1797e62e762499dd45601b9e45aaa53a129f31ce0b4444551a9639b8b681ad535f379893dd1e3ae37b31dccd82aa languageName: node linkType: hard @@ -24864,14 +24853,14 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.0.0, espree@npm:^9.6.0": - version: 9.6.0 - resolution: "espree@npm:9.6.0" +"espree@npm:^9.0.0, espree@npm:^9.6.0, espree@npm:^9.6.1": + version: 9.6.1 + resolution: "espree@npm:9.6.1" dependencies: acorn: ^8.9.0 acorn-jsx: ^5.3.2 eslint-visitor-keys: ^3.4.1 - checksum: 1287979510efb052a6a97c73067ea5d0a40701b29adde87bbe2d3eb1667e39ca55e8129e20e2517fed3da570150e7ef470585228459a8f3e3755f45007a1c662 + checksum: eb8c149c7a2a77b3f33a5af80c10875c3abd65450f60b8af6db1bfcfa8f101e21c1e56a561c6dc13b848e18148d43469e7cd208506238554fb5395a9ea5a1ab9 languageName: node linkType: hard @@ -25153,6 +25142,7 @@ __metadata: resolution: "example-backend-next@workspace:packages/backend-next" dependencies: "@backstage/backend-defaults": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-tasks": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/plugin-adr-backend": "workspace:^" @@ -25170,6 +25160,7 @@ __metadata: "@backstage/plugin-permission-backend": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-permission-node": "workspace:^" + "@backstage/plugin-proxy-backend": "workspace:^" "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-search-backend": "workspace:^" "@backstage/plugin-search-backend-module-catalog": "workspace:^" @@ -25448,7 +25439,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1": +"express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1, express@npm:^4.18.2": version: 4.18.2 resolution: "express@npm:4.18.2" dependencies: @@ -25598,7 +25589,7 @@ __metadata: languageName: node linkType: hard -"fast-json-patch@npm:^3.0.0-1": +"fast-json-patch@npm:^3.0.0-1, fast-json-patch@npm:^3.1.0": version: 3.1.1 resolution: "fast-json-patch@npm:3.1.1" checksum: c4525b61b2471df60d4b025b4118b036d99778a93431aa44d1084218182841d82ce93056f0f3bbd731a24e6a8e69820128adf1873eb2199a26c62ef58d137833 @@ -25683,6 +25674,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:^4.2.2": + version: 4.2.7 + resolution: "fast-xml-parser@npm:4.2.7" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: d8b0c9e04756f6c43fa0399428f30149acadae21350e42e26e8fe98e24e6afa6b9b00aa554453795036b00e9fee974a1b556fe2ba18be391d51a9bf1ab790e7c + languageName: node + linkType: hard + "fastest-stable-stringify@npm:^2.0.2": version: 2.0.2 resolution: "fastest-stable-stringify@npm:2.0.2" @@ -25928,16 +25930,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^1.0.0": - version: 1.1.2 - resolution: "find-up@npm:1.1.2" - dependencies: - path-exists: ^2.0.0 - pinkie-promise: ^2.0.0 - checksum: a2cb9f4c9f06ee3a1e92ed71d5aed41ac8ae30aefa568132f6c556fac7678a5035126153b59eaec68da78ac409eef02503b2b059706bdbf232668d7245e3240a - languageName: node - linkType: hard - "find-up@npm:^3.0.0": version: 3.0.0 resolution: "find-up@npm:3.0.0" @@ -26516,13 +26508,6 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^1.0.1": - version: 1.0.3 - resolution: "get-caller-file@npm:1.0.3" - checksum: 2b90a7f848896abcebcdc0acc627a435bcf05b9cd280599bc980ebfcdc222416c3df12c24c4845f69adc4346728e8966f70b758f9369f3534182791dfbc25c05 - languageName: node - linkType: hard - "get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -26530,14 +26515,15 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3": - version: 1.1.3 - resolution: "get-intrinsic@npm:1.1.3" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": + version: 1.2.1 + resolution: "get-intrinsic@npm:1.2.1" dependencies: function-bind: ^1.1.1 has: ^1.0.3 + has-proto: ^1.0.1 has-symbols: ^1.0.3 - checksum: 152d79e87251d536cf880ba75cfc3d6c6c50e12b3a64e1ea960e73a3752b47c69f46034456eae1b0894359ce3bc64c55c186f2811f8a788b75b638b06fab228a + checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f languageName: node linkType: hard @@ -26770,19 +26756,19 @@ __metadata: languageName: node linkType: hard -"globals@npm:^11.1.0, globals@npm:^11.12.0": +"globals@npm:^11.1.0": version: 11.12.0 resolution: "globals@npm:11.12.0" checksum: 67051a45eca3db904aee189dfc7cd53c20c7d881679c93f6146ddd4c9f4ab2268e68a919df740d39c71f4445d2b38ee360fc234428baea1dbdfe68bbcb46979e languageName: node linkType: hard -"globals@npm:^13.19.0": - version: 13.19.0 - resolution: "globals@npm:13.19.0" +"globals@npm:^13.19.0, globals@npm:^13.20.0": + version: 13.20.0 + resolution: "globals@npm:13.20.0" dependencies: type-fest: ^0.20.2 - checksum: a000dbd00bcf28f0941d8a29c3522b1c3b8e4bfe4e60e262c477a550c3cbbe8dbe2925a6905f037acd40f9a93c039242e1f7079c76b0fd184bc41dcc3b5c8e2e + checksum: ad1ecf914bd051325faad281d02ea2c0b1df5d01bd94d368dcc5513340eac41d14b3c61af325768e3c7f8d44576e72780ec0b6f2d366121f8eec6e03c3a3b97a languageName: node linkType: hard @@ -26849,8 +26835,8 @@ __metadata: linkType: hard "google-gax@npm:^3.5.7, google-gax@npm:^3.5.8": - version: 3.6.0 - resolution: "google-gax@npm:3.6.0" + version: 3.6.1 + resolution: "google-gax@npm:3.6.1" dependencies: "@grpc/grpc-js": ~1.8.0 "@grpc/proto-loader": ^0.7.0 @@ -26864,13 +26850,13 @@ __metadata: node-fetch: ^2.6.1 object-hash: ^3.0.0 proto3-json-serializer: ^1.0.0 - protobufjs: 7.2.3 + protobufjs: 7.2.4 protobufjs-cli: 1.1.1 retry-request: ^5.0.0 bin: compileProtos: build/tools/compileProtos.js minifyProtoJson: build/tools/minify.js - checksum: 4c7b1b5a278ffda3eaa92e7f25a9e0ea2d0a76aca704aca4baee4fe9d109916e97e83062ad751006ed93ac675a7fd74028061204fa9ed92833a853e6657a71a1 + checksum: 16e5fb211d75c6a4cb4d2e62adba7bbf41d160feba74fe39435a70fc31ef8ebc740af4527a2897abab39a1806d131792b2a761da432ae1b916198c9c43aab36e languageName: node linkType: hard @@ -27007,16 +26993,16 @@ __metadata: linkType: hard "graphql-modules@npm:^2.0.0": - version: 2.1.2 - resolution: "graphql-modules@npm:2.1.2" + version: 2.2.0 + resolution: "graphql-modules@npm:2.2.0" dependencies: - "@graphql-tools/schema": ^9.0.0 - "@graphql-tools/wrap": ^9.0.0 + "@graphql-tools/schema": ^10.0.0 + "@graphql-tools/wrap": ^10.0.0 "@graphql-typed-document-node/core": ^3.1.0 - ramda: ^0.28.0 + ramda: ^0.29.0 peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 3a4732c54acb707350b7249d96952b5bb1e5a2c054090181d4e4f8adcd66d4d0675208d06f873bca75b924833df9a2c5bc7ce90aeb04b2d9e256605313a6080a + checksum: 81e36df3869f54274b1ab2d178d96ae649f6261724235566361caa770a6011b02633e211cdd2e57b4720b8fa61c2469618a1dc7e2ed213855bf48a2fbf30a76d languageName: node linkType: hard @@ -27159,11 +27145,11 @@ __metadata: linkType: hard "handlebars@npm:^4.7.3": - version: 4.7.7 - resolution: "handlebars@npm:4.7.7" + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" dependencies: minimist: ^1.2.5 - neo-async: ^2.6.0 + neo-async: ^2.6.2 source-map: ^0.6.1 uglify-js: ^3.1.4 wordwrap: ^1.0.0 @@ -27172,7 +27158,7 @@ __metadata: optional: true bin: handlebars: bin/handlebars - checksum: 1e79a43f5e18d15742977cb987923eab3e2a8f44f2d9d340982bcb69e1735ed049226e534d7c1074eaddaf37e4fb4f471a8adb71cddd5bc8cf3f894241df5cee + checksum: 00e68bb5c183fd7b8b63322e6234b5ac8fbb960d712cb3f25587d559c2951d9642df83c04a1172c918c41bcfc81bfbd7a7718bbce93b893e0135fc99edea93ff languageName: node linkType: hard @@ -27237,6 +27223,13 @@ __metadata: languageName: node linkType: hard +"has-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "has-proto@npm:1.0.1" + checksum: febc5b5b531de8022806ad7407935e2135f1cc9e64636c3916c6842bd7995994ca3b29871ecd7954bd35f9e2986c17b3b227880484d22259e2f8e6ce63fd383e + languageName: node + linkType: hard + "has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": version: 1.0.3 resolution: "has-symbols@npm:1.0.3" @@ -27868,7 +27861,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1": version: 3.2.1 resolution: "import-fresh@npm:3.2.1" dependencies: @@ -28011,7 +28004,7 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:8.2.5, inquirer@npm:^8.0.0, inquirer@npm:^8.2.0": +"inquirer@npm:8.2.5": version: 8.2.5 resolution: "inquirer@npm:8.2.5" dependencies: @@ -28034,14 +28027,37 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.3": - version: 1.0.3 - resolution: "internal-slot@npm:1.0.3" +"inquirer@npm:^8.0.0, inquirer@npm:^8.2.0": + version: 8.2.6 + resolution: "inquirer@npm:8.2.6" dependencies: - get-intrinsic: ^1.1.0 + ansi-escapes: ^4.2.1 + chalk: ^4.1.1 + cli-cursor: ^3.1.0 + cli-width: ^3.0.0 + external-editor: ^3.0.3 + figures: ^3.0.0 + lodash: ^4.17.21 + mute-stream: 0.0.8 + ora: ^5.4.1 + run-async: ^2.4.0 + rxjs: ^7.5.5 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + through: ^2.3.6 + wrap-ansi: ^6.0.1 + checksum: 387ffb0a513559cc7414eb42c57556a60e302f820d6960e89d376d092e257a919961cd485a1b4de693dbb5c0de8bc58320bfd6247dfd827a873aa82a4215a240 + languageName: node + linkType: hard + +"internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.5": + version: 1.0.5 + resolution: "internal-slot@npm:1.0.5" + dependencies: + get-intrinsic: ^1.2.0 has: ^1.0.3 side-channel: ^1.0.4 - checksum: 1944f92e981e47aebc98a88ff0db579fd90543d937806104d0b96557b10c1f170c51fb777b97740a8b6ddeec585fca8c39ae99fd08a8e058dfc8ab70937238bf + checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a languageName: node linkType: hard @@ -28085,13 +28101,6 @@ __metadata: languageName: node linkType: hard -"invert-kv@npm:^1.0.0": - version: 1.0.0 - resolution: "invert-kv@npm:1.0.0" - checksum: aebeee31dda3b3d25ffd242e9a050926e7fe5df642d60953ab183aca1a7d1ffb39922eb2618affb0e850cf2923116f0da1345367759d88d097df5da1f1e1590e - languageName: node - linkType: hard - "ioredis@npm:^5.3.2": version: 5.3.2 resolution: "ioredis@npm:5.3.2" @@ -28198,14 +28207,14 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.1": - version: 3.0.1 - resolution: "is-array-buffer@npm:3.0.1" +"is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": + version: 3.0.2 + resolution: "is-array-buffer@npm:3.0.2" dependencies: call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 + get-intrinsic: ^1.2.0 is-typed-array: ^1.1.10 - checksum: f26ab87448e698285daf707e52a533920449f7abf63714140ffab9d5571aa5a71ac2fa2677e8b793ad0d5d3e40078d4d2c8a0ab39c957e3cfc6513bb6c9dfdc9 + checksum: dcac9dda66ff17df9cabdc58214172bf41082f956eab30bb0d86bc0fab1e44b690fc8e1f855cf2481245caf4e8a5a006a982a71ddccec84032ed41f9d8da8c14 languageName: node linkType: hard @@ -28223,6 +28232,15 @@ __metadata: languageName: node linkType: hard +"is-async-function@npm:^2.0.0": + version: 2.0.0 + resolution: "is-async-function@npm:2.0.0" + dependencies: + has-tostringtag: ^1.0.0 + checksum: e3471d95e6c014bf37cad8a93f2f4b6aac962178e0a5041e8903147166964fdc1c5c1d2ef87e86d77322c370ca18f2ea004fa7420581fa747bcaf7c223069dbd + languageName: node + linkType: hard + "is-bigint@npm:^1.0.1": version: 1.0.4 resolution: "is-bigint@npm:1.0.4" @@ -28274,17 +28292,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^2.0.0": - version: 2.0.0 - resolution: "is-ci@npm:2.0.0" - dependencies: - ci-info: ^2.0.0 - bin: - is-ci: bin.js - checksum: 77b869057510f3efa439bbb36e9be429d53b3f51abd4776eeea79ab3b221337fe1753d1e50058a9e2c650d38246108beffb15ccfd443929d77748d8c0cc90144 - languageName: node - linkType: hard - "is-ci@npm:^3.0.0, is-ci@npm:^3.0.1": version: 3.0.1 resolution: "is-ci@npm:3.0.1" @@ -28296,12 +28303,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.9.0": - version: 2.11.0 - resolution: "is-core-module@npm:2.11.0" +"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.1, is-core-module@npm:^2.13.0, is-core-module@npm:^2.9.0": + version: 2.13.0 + resolution: "is-core-module@npm:2.13.0" dependencies: has: ^1.0.3 - checksum: f96fd490c6b48eb4f6d10ba815c6ef13f410b0ba6f7eb8577af51697de523e5f2cd9de1c441b51d27251bf0e4aebc936545e33a5d26d5d51f28d25698d4a8bab + checksum: 053ab101fb390bfeb2333360fd131387bed54e476b26860dc7f5a700bbf34a0ec4454f7c8c4d43e8a0030957e4b3db6e16d35e1890ea6fb654c833095e040355 languageName: node linkType: hard @@ -28344,6 +28351,15 @@ __metadata: languageName: node linkType: hard +"is-finalizationregistry@npm:^1.0.2": + version: 1.0.2 + resolution: "is-finalizationregistry@npm:1.0.2" + dependencies: + call-bind: ^1.0.2 + checksum: 4f243a8e06228cd45bdab8608d2cb7abfc20f6f0189c8ac21ea8d603f1f196eabd531ce0bb8e08cbab047e9845ef2c191a3761c9a17ad5cabf8b35499c4ad35d + languageName: node + linkType: hard + "is-fullwidth-code-point@npm:^1.0.0": version: 1.0.0 resolution: "is-fullwidth-code-point@npm:1.0.0" @@ -28374,10 +28390,12 @@ __metadata: languageName: node linkType: hard -"is-generator-function@npm:^1.0.7": - version: 1.0.8 - resolution: "is-generator-function@npm:1.0.8" - checksum: ff6a510416885616fa57a2a1e043224a386ed37451c3686cc1ed38a63acd59b240f09b5d937aa86dedc3a7fb91ae6c0ce6477149d4b6f9be608a870e038b38bd +"is-generator-function@npm:^1.0.10, is-generator-function@npm:^1.0.7": + version: 1.0.10 + resolution: "is-generator-function@npm:1.0.10" + dependencies: + has-tostringtag: ^1.0.0 + checksum: d54644e7dbaccef15ceb1e5d91d680eb5068c9ee9f9eb0a9e04173eb5542c9b51b5ab52c5537f5703e48d5fddfd376817c1ca07a84a407b7115b769d4bdde72b languageName: node linkType: hard @@ -28729,16 +28747,12 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3": - version: 1.1.10 - resolution: "is-typed-array@npm:1.1.10" +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": + version: 1.1.12 + resolution: "is-typed-array@npm:1.1.12" dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - checksum: aac6ecb59d4c56a1cdeb69b1f129154ef462bbffe434cb8a8235ca89b42f258b7ae94073c41b3cb7bce37f6a1733ad4499f07882d5d5093a7ba84dfc4ebb8017 + which-typed-array: ^1.1.11 + checksum: 4c89c4a3be07186caddadf92197b17fda663a9d259ea0d44a85f171558270d36059d1c386d34a12cba22dfade5aba497ce22778e866adc9406098c8fc4771796 languageName: node linkType: hard @@ -29024,6 +29038,19 @@ __metadata: languageName: node linkType: hard +"iterator.prototype@npm:^1.1.0": + version: 1.1.0 + resolution: "iterator.prototype@npm:1.1.0" + dependencies: + define-properties: ^1.1.4 + get-intrinsic: ^1.1.3 + has-symbols: ^1.0.3 + has-tostringtag: ^1.0.0 + reflect.getprototypeof: ^1.0.3 + checksum: 462fe16c770affeb9c08620b13fc98d38307335821f4fabd489f491d38c79855c6a93d4b56f6146eaa56711f61690aa5c7eb0ce8586c95145d2f665a3834d916 + languageName: node + linkType: hard + "jackspeak@npm:^2.0.3": version: 2.2.1 resolution: "jackspeak@npm:2.2.1" @@ -29564,11 +29591,11 @@ __metadata: linkType: hard "jest-when@npm:^3.1.0": - version: 3.5.2 - resolution: "jest-when@npm:3.5.2" + version: 3.6.0 + resolution: "jest-when@npm:3.6.0" peerDependencies: jest: ">= 25" - checksum: 9ad95552d377ef4d517c96a14c38bd8626d6856f518e7efc8a01d76a45a39d3c52281392c98da781c302114c78cd1ea17556c7f638d49d15971fcce9d58306e8 + checksum: ed32ed84e5802bb6fec98966cdf862ce59677551be33d3795e6ac7a6207acf467ed559573d671c8d94c59f7fc0780cf358d2d165d81cdc7d9611250d975ee024 languageName: node linkType: hard @@ -30045,13 +30072,13 @@ __metadata: linkType: hard "json-schema-to-ts@npm:^2.6.2": - version: 2.9.1 - resolution: "json-schema-to-ts@npm:2.9.1" + version: 2.9.2 + resolution: "json-schema-to-ts@npm:2.9.2" dependencies: "@babel/runtime": ^7.18.3 "@types/json-schema": ^7.0.9 ts-algebra: ^1.2.0 - checksum: c2471993484f69452eb4f1c732a3b4d73842430f8c9a1486d72fda9fc9ca6ae4761164817edddd8463204caec2d3169a95d5e9e4d9d39f1e94f7377bdafb740b + checksum: 64bf7226511a3719075d28e715fbd203576088b6ebcf494d6c5ee2bcf5b24d6081fa0cfce22ce254ad8798b7044603db4e4d19779c6f5765285fb9ddaa0756ef languageName: node linkType: hard @@ -30133,7 +30160,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.1": +"json5@npm:^1.0.1, json5@npm:^1.0.2": version: 1.0.2 resolution: "json5@npm:1.0.2" dependencies: @@ -30695,15 +30722,6 @@ __metadata: languageName: node linkType: hard -"lcid@npm:^1.0.0": - version: 1.0.0 - resolution: "lcid@npm:1.0.0" - dependencies: - invert-kv: ^1.0.0 - checksum: e8c7a4db07663068c5c44b650938a2bc41aa992037eebb69376214320f202c1250e70b50c32f939e28345fd30c2d35b8e8cd9a19d5932c398246a864ce54843d - languageName: node - linkType: hard - "ldap-filter@npm:^0.3.3": version: 0.3.3 resolution: "ldap-filter@npm:0.3.3" @@ -30953,19 +30971,6 @@ __metadata: languageName: node linkType: hard -"load-json-file@npm:^1.0.0": - version: 1.1.0 - resolution: "load-json-file@npm:1.1.0" - dependencies: - graceful-fs: ^4.1.2 - parse-json: ^2.2.0 - pify: ^2.0.0 - pinkie-promise: ^2.0.0 - strip-bom: ^2.0.0 - checksum: 0e4e4f380d897e13aa236246a917527ea5a14e4fc34d49e01ce4e7e2a1e08e2740ee463a03fb021c04f594f29a178f4adb994087549d7c1c5315fcd29bf9934b - languageName: node - linkType: hard - "load-yaml-file@npm:^0.2.0": version: 0.2.0 resolution: "load-yaml-file@npm:0.2.0" @@ -31049,13 +31054,6 @@ __metadata: languageName: node linkType: hard -"lodash.assign@npm:^4.1.0, lodash.assign@npm:^4.2.0": - version: 4.2.0 - resolution: "lodash.assign@npm:4.2.0" - checksum: 75bbc6733c9f577c448031b4051f990f068802708891f94be9d4c2faffd6a9ec67a2c49671dafc908a068d35687765464853282842b4560b662e6c903d11cc90 - languageName: node - linkType: hard - "lodash.camelcase@npm:^4.3.0": version: 4.3.0 resolution: "lodash.camelcase@npm:4.3.0" @@ -32842,9 +32840,9 @@ __metadata: languageName: node linkType: hard -"msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1": - version: 1.2.2 - resolution: "msw@npm:1.2.2" +"msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1, msw@npm:^1.2.3": + version: 1.2.3 + resolution: "msw@npm:1.2.3" dependencies: "@mswjs/cookies": ^0.2.2 "@mswjs/interceptors": ^0.17.5 @@ -32872,7 +32870,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: e42cec8f5523663020bdecf6a7977a10aa86a4718d1920def3fbde0ff3734391873668cc6e3996d6790add3c74dac95a952f8560ce2543697280125eb55138e8 + checksum: 832a6fc3973726a97e03ce2690c3b6e1774b5156d064a478657a1b33f9933e4834af833cc8a859e1b717fa9b71f1271b3e66a1ec6eed7f76bfe79406e9ec3986 languageName: node linkType: hard @@ -33063,20 +33061,13 @@ __metadata: languageName: node linkType: hard -"neo-async@npm:^2.5.0, neo-async@npm:^2.6.0, neo-async@npm:^2.6.2": +"neo-async@npm:^2.5.0, neo-async@npm:^2.6.2": version: 2.6.2 resolution: "neo-async@npm:2.6.2" checksum: deac9f8d00eda7b2e5cd1b2549e26e10a0faa70adaa6fdadca701cc55f49ee9018e427f424bac0c790b7c7e2d3068db97f3093f1093975f2acb8f8818b936ed9 languageName: node linkType: hard -"nice-try@npm:^1.0.4": - version: 1.0.5 - resolution: "nice-try@npm:1.0.5" - checksum: 0b4af3b5bb5d86c289f7a026303d192a7eb4417231fe47245c460baeabae7277bcd8fd9c728fb6bd62c30b3e15cd6620373e2cf33353b095d8b403d3e8a15aff - languageName: node - linkType: hard - "nimma@npm:0.2.2": version: 0.2.2 resolution: "nimma@npm:0.2.2" @@ -33119,15 +33110,6 @@ __metadata: languageName: node linkType: hard -"node-abi@npm:^2.21.0": - version: 2.30.1 - resolution: "node-abi@npm:2.30.1" - dependencies: - semver: ^5.4.1 - checksum: 3f4b0c912ce4befcd7ceab4493ba90b51d60dfcc90f567c93f731d897ef8691add601cb64c181683b800f21d479d68f9a6e15d8ab8acd16a5706333b9e30a881 - languageName: node - linkType: hard - "node-abi@npm:^3.3.0": version: 3.8.0 resolution: "node-abi@npm:3.8.0" @@ -33348,23 +33330,23 @@ __metadata: languageName: node linkType: hard -"nodemon@npm:^2.0.2": - version: 2.0.22 - resolution: "nodemon@npm:2.0.22" +"nodemon@npm:^3.0.1": + version: 3.0.1 + resolution: "nodemon@npm:3.0.1" dependencies: chokidar: ^3.5.2 debug: ^3.2.7 ignore-by-default: ^1.0.1 minimatch: ^3.1.2 pstree.remy: ^1.1.8 - semver: ^5.7.1 - simple-update-notifier: ^1.0.7 + semver: ^7.5.3 + simple-update-notifier: ^2.0.0 supports-color: ^5.5.0 touch: ^3.1.0 undefsafe: ^2.0.5 bin: nodemon: bin/nodemon.js - checksum: 9c987e139748f5b5c480c6c9080bdc97304ee7d29172b7b3da1a7db590b1323ad57b96346304e9b522b0e445c336dc393ccd3f9f45c73b20d476d2347890dcd0 + checksum: 6a5d81855760d6617049eccce10ccf02bddb482dab13ceea5280ae595ec7004eee13e7b934368e3f46c37fe4d970342a8c38c99cae7e93e4d7a3ed1c1ecb6acf languageName: node linkType: hard @@ -33401,7 +33383,7 @@ __metadata: languageName: node linkType: hard -"normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.5.0": +"normalize-package-data@npm:^2.5.0": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" dependencies: @@ -33567,7 +33549,7 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^4.0.1, npmlog@npm:^4.1.2": +"npmlog@npm:^4.1.2": version: 4.1.2 resolution: "npmlog@npm:4.1.2" dependencies: @@ -33645,9 +33627,9 @@ __metadata: linkType: hard "nwsapi@npm:^2.2.0": - version: 2.2.0 - resolution: "nwsapi@npm:2.2.0" - checksum: 5ef4a9bc0c1a5b7f2e014aa6a4b359a257503b796618ed1ef0eb852098f77e772305bb0e92856e4bbfa3e6c75da48c0113505c76f144555ff38867229c2400a7 + version: 2.2.7 + resolution: "nwsapi@npm:2.2.7" + checksum: cab25f7983acec7e23490fec3ef7be608041b460504229770e3bfcf9977c41d6fe58f518994d3bd9aa3a101f501089a3d4a63536f4ff8ae4b8c4ca23bdbfda4e languageName: node linkType: hard @@ -33686,7 +33668,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.2, object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": version: 1.12.3 resolution: "object-inspect@npm:1.12.3" checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db @@ -33744,6 +33726,18 @@ __metadata: languageName: node linkType: hard +"object.groupby@npm:^1.0.0": + version: 1.0.0 + resolution: "object.groupby@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.21.2 + get-intrinsic: ^1.2.1 + checksum: 64b00b287d57580111c958e7ff375c9b61811fa356f2cf0d35372d43cab61965701f00fac66c19fd8f49c4dfa28744bee6822379c69a73648ad03e09fcdeae70 + languageName: node + linkType: hard + "object.hasown@npm:^1.1.2": version: 1.1.2 resolution: "object.hasown@npm:1.1.2" @@ -33934,13 +33928,23 @@ __metadata: languageName: node linkType: hard -"optimism@npm:^0.16.2": - version: 0.16.2 - resolution: "optimism@npm:0.16.2" +"oppa@npm:^0.4.0": + version: 0.4.0 + resolution: "oppa@npm:0.4.0" + dependencies: + chalk: ^4.1.1 + checksum: ecc43e63ede05c3ccb10e0f2c3f3020a6d72e1a3b318f3e37b8cc8a1a279e300991c043e5385d560c1eebb54a56c7f9b69bf0db0d1933acf350bcd2980c96055 + languageName: node + linkType: hard + +"optimism@npm:^0.17.5": + version: 0.17.5 + resolution: "optimism@npm:0.17.5" dependencies: "@wry/context": ^0.7.0 - "@wry/trie": ^0.3.0 - checksum: a98ed9a0b8ee2b031010222099b60860d52860bf8182889f2695a7cf2185f21aca59020f78e2b47c0ae7697843caa576798d792967314ff59f6aa7c5d9de7f3a + "@wry/trie": ^0.4.3 + tslib: ^2.3.0 + checksum: 5990217d989e9857dc523a64cb6e5a9205eae68c7acac78f7dde8fbe50045d0f11ca8068cdbb51b1eae15510d96ad593a99cf98c6f86c41d1b6f90e54956ff11 languageName: node linkType: hard @@ -33996,15 +34000,6 @@ __metadata: languageName: node linkType: hard -"os-locale@npm:^1.4.0": - version: 1.4.0 - resolution: "os-locale@npm:1.4.0" - dependencies: - lcid: ^1.0.0 - checksum: 0161a1b6b5a8492f99f4b47fe465df9fc521c55ba5414fce6444c45e2500487b8ed5b40a47a98a2363fe83ff04ab033785300ed8df717255ec4c3b625e55b1fb - languageName: node - linkType: hard - "os-tmpdir@npm:~1.0.2": version: 1.0.2 resolution: "os-tmpdir@npm:1.0.2" @@ -34321,15 +34316,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^2.2.0": - version: 2.2.0 - resolution: "parse-json@npm:2.2.0" - dependencies: - error-ex: ^1.2.0 - checksum: dda78a63e57a47b713a038630868538f718a7ca0cd172a36887b0392ccf544ed0374902eb28f8bf3409e8b71d62b79d17062f8543afccf2745f9b0b2d2bb80ca - languageName: node - linkType: hard - "parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -34342,6 +34328,15 @@ __metadata: languageName: node linkType: hard +"parse-link-header@npm:^2.0.0": + version: 2.0.0 + resolution: "parse-link-header@npm:2.0.0" + dependencies: + xtend: ~4.0.1 + checksum: 0e96c6af9910e8f92084b49b8dc6a10dd58db470847d1499f562576180c1ac5e49d18007697f0d538e5f3efdc8ce1d8777641f3ae225302b74af0dd0578b628e + languageName: node + linkType: hard + "parse-path@npm:^7.0.0": version: 7.0.0 resolution: "parse-path@npm:7.0.0" @@ -34542,27 +34537,27 @@ __metadata: languageName: node linkType: hard -"patch-package@npm:^6.5.1": - version: 6.5.1 - resolution: "patch-package@npm:6.5.1" +"patch-package@npm:^7.0.2": + version: 7.0.2 + resolution: "patch-package@npm:7.0.2" dependencies: "@yarnpkg/lockfile": ^1.1.0 chalk: ^4.1.2 - cross-spawn: ^6.0.5 + ci-info: ^3.7.0 + cross-spawn: ^7.0.3 find-yarn-workspace-root: ^2.0.0 fs-extra: ^9.0.0 - is-ci: ^2.0.0 klaw-sync: ^6.0.0 minimist: ^1.2.6 open: ^7.4.2 rimraf: ^2.6.3 - semver: ^5.6.0 + semver: ^7.5.3 slash: ^2.0.0 tmp: ^0.0.33 - yaml: ^1.10.2 + yaml: ^2.2.2 bin: patch-package: index.js - checksum: 8530ffa30f11136b527c6eddf6da48fa12856ee510a47edb1f9cdf8a025636adb82968f5fae778b5e04ce8c87915ebdf5911422b54add59a5a42e372a8f30eb2 + checksum: de2cf60effc8b59ee15d4930f84eea63c217525f0133c926850ee3a2437651d8aabbd0fec4ddee1b8b8da4fd380bcea90ef3c4acd69026057fa80cdc823b59a4 languageName: node linkType: hard @@ -34597,15 +34592,6 @@ __metadata: languageName: node linkType: hard -"path-exists@npm:^2.0.0": - version: 2.1.0 - resolution: "path-exists@npm:2.1.0" - dependencies: - pinkie-promise: ^2.0.0 - checksum: fdb734f1d00f225f7a0033ce6d73bff6a7f76ea08936abf0e5196fa6e54a645103538cd8aedcb90d6d8c3fa3705ded0c58a4da5948ae92aa8834892c1ab44a84 - languageName: node - linkType: hard - "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -34634,13 +34620,6 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^2.0.1": - version: 2.0.1 - resolution: "path-key@npm:2.0.1" - checksum: f7ab0ad42fe3fb8c7f11d0c4f849871e28fbd8e1add65c370e422512fc5887097b9cf34d09c1747d45c942a8c1e26468d6356e2df3f740bf177ab8ca7301ebfd - languageName: node - linkType: hard - "path-key@npm:^3.0.0, path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -34725,17 +34704,6 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^1.0.0": - version: 1.1.0 - resolution: "path-type@npm:1.1.0" - dependencies: - graceful-fs: ^4.1.2 - pify: ^2.0.0 - pinkie-promise: ^2.0.0 - checksum: 59a4b2c0e566baf4db3021a1ed4ec09a8b36fca960a490b54a6bcefdb9987dafe772852982b6011cd09579478a96e57960a01f75fa78a794192853c9d468fc79 - languageName: node - linkType: hard - "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -34807,10 +34775,10 @@ __metadata: languageName: node linkType: hard -"pg-connection-string@npm:^2.3.0, pg-connection-string@npm:^2.4.0, pg-connection-string@npm:^2.6.1": - version: 2.6.1 - resolution: "pg-connection-string@npm:2.6.1" - checksum: 882344a47e1ecf3a91383e0809bf2ac48facea97fcec0358d6e060e1cbcb8737acde419b4c86f05da4ce4a16634ee50fff1d2bb787d73b52ccbfde697243ad8a +"pg-connection-string@npm:^2.3.0, pg-connection-string@npm:^2.5.0, pg-connection-string@npm:^2.6.2": + version: 2.6.2 + resolution: "pg-connection-string@npm:2.6.2" + checksum: 22265882c3b6f2320785378d0760b051294a684989163d5a1cde4009e64e84448d7bf67d9a7b9e7f69440c3ee9e2212f9aa10dd17ad6773f6143c6020cebbcb5 languageName: node linkType: hard @@ -34872,14 +34840,14 @@ __metadata: languageName: node linkType: hard -"pg@npm:^8.3.0, pg@npm:^8.4.0": - version: 8.11.1 - resolution: "pg@npm:8.11.1" +"pg@npm:^8.3.0, pg@npm:^8.9.0": + version: 8.11.2 + resolution: "pg@npm:8.11.2" dependencies: buffer-writer: 2.0.0 packet-reader: 1.0.0 pg-cloudflare: ^1.1.1 - pg-connection-string: ^2.6.1 + pg-connection-string: ^2.6.2 pg-pool: ^3.6.1 pg-protocol: ^1.6.0 pg-types: ^2.1.0 @@ -34892,7 +34860,7 @@ __metadata: peerDependenciesMeta: pg-native: optional: true - checksum: e608fe1c52725e1c0c4cbdf97e29df8f41b9fd21aed821866e3488b3a0622be1c19801d8d8eb31f0de35f040c4f69163c211358e7df8c48d15ee8f660d2bd4cc + checksum: dfea8a2269d500dee8c17291207e5a25897163480037beb7a59be35f51e33b519c297c943ea6898b285d6a74a0d661901dc9cff2e587cc4be0bbf09b833a71a5 languageName: node linkType: hard @@ -34905,18 +34873,17 @@ __metadata: languageName: node linkType: hard -"pgtools@npm:^0.3.0": - version: 0.3.2 - resolution: "pgtools@npm:0.3.2" +"pgtools@npm:^1.0.0": + version: 1.0.1 + resolution: "pgtools@npm:1.0.1" dependencies: - bluebird: ^3.3.5 - pg: ^8.4.0 - pg-connection-string: ^2.4.0 - yargs: ^5.0.0 + pg: ^8.9.0 + pg-connection-string: ^2.5.0 + yargs: ^17.7.1 bin: - createdbjs: createdb.js - dropdbjs: dropdb.js - checksum: 764644cde13431070e4e3558ed08b67fe73cb92d5573d0c0017f76b76cd85b25aecdf3fa948e6d50ceabf315f50ed2cfe52275d04321c274f1a00c26fe8485b4 + createdbjs: src/bin/createdb.js + dropdbjs: src/bin/dropdb.js + checksum: d047230e529a37f66cb0253adf505d61bc8f27aa9eb1ab88fe3ce75e3e2118b6771c408e2dbea1be8256b8ab5cc035f44298d47128085626fad6df6c33d026dc languageName: node linkType: hard @@ -34950,7 +34917,7 @@ __metadata: languageName: node linkType: hard -"pify@npm:^2.0.0, pify@npm:^2.2.0, pify@npm:^2.3.0": +"pify@npm:^2.2.0, pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba @@ -34978,22 +34945,6 @@ __metadata: languageName: node linkType: hard -"pinkie-promise@npm:^2.0.0": - version: 2.0.1 - resolution: "pinkie-promise@npm:2.0.1" - dependencies: - pinkie: ^2.0.0 - checksum: b53a4a2e73bf56b6f421eef711e7bdcb693d6abb474d57c5c413b809f654ba5ee750c6a96dd7225052d4b96c4d053cdcb34b708a86fceed4663303abee52fcca - languageName: node - linkType: hard - -"pinkie@npm:^2.0.0": - version: 2.0.4 - resolution: "pinkie@npm:2.0.4" - checksum: b12b10afea1177595aab036fc220785488f67b4b0fc49e7a27979472592e971614fa1c728e63ad3e7eb748b4ec3c3dbd780819331dad6f7d635c77c10537b9db - languageName: node - linkType: hard - "pino-pretty@npm:^2.6.0": version: 2.6.1 resolution: "pino-pretty@npm:2.6.1" @@ -35546,13 +35497,13 @@ __metadata: linkType: hard "postcss@npm:^8.1.0, postcss@npm:^8.4.21": - version: 8.4.27 - resolution: "postcss@npm:8.4.27" + version: 8.4.28 + resolution: "postcss@npm:8.4.28" dependencies: nanoid: ^3.3.6 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 + checksum: f605c24a36f7e400bad379735fbfc893ccb8d293ad6d419bb824db77cdcb69f43d614ef35f9f7091f32ca588d130ec60dbcf53b366e6bf88a8a64bbeb3c05f6d languageName: node linkType: hard @@ -35623,29 +35574,6 @@ __metadata: languageName: node linkType: hard -"prebuild-install@npm:^6.0.1": - version: 6.1.4 - resolution: "prebuild-install@npm:6.1.4" - dependencies: - detect-libc: ^1.0.3 - expand-template: ^2.0.3 - github-from-package: 0.0.0 - minimist: ^1.2.3 - mkdirp-classic: ^0.5.3 - napi-build-utils: ^1.0.1 - node-abi: ^2.21.0 - npmlog: ^4.0.1 - pump: ^3.0.0 - rc: ^1.2.7 - simple-get: ^3.0.3 - tar-fs: ^2.0.0 - tunnel-agent: ^0.6.0 - bin: - prebuild-install: bin.js - checksum: de4313eda821305912af922700a2db04bb8e77fe8aa9c2788550f1000c026cbefc82da468ec0c0a37764c5417bd8169dbd540928535fb38d00bb9bbd673dd217 - languageName: node - linkType: hard - "prebuild-install@npm:^7.1.0, prebuild-install@npm:^7.1.1": version: 7.1.1 resolution: "prebuild-install@npm:7.1.1" @@ -35915,11 +35843,11 @@ __metadata: linkType: hard "proto3-json-serializer@npm:^1.0.0": - version: 1.0.0 - resolution: "proto3-json-serializer@npm:1.0.0" + version: 1.1.1 + resolution: "proto3-json-serializer@npm:1.1.1" dependencies: - protobufjs: ^6.11.2 - checksum: 47ac3b7c48eb177aba3e59431704c5db24d670827bd47ef7488e3529ab85e0de4833ed22615945fa72597dd382aa505b8dd427c917c4c50d31e7570d8af21294 + protobufjs: ^7.0.0 + checksum: 0cd94cb635a9b9b3a2d047700175be4a6c7b7a43e2698826edad17604793764bcdfc270585ea58cb94aa690211b6cdaae5bf7a22522bea68ca67a2844773b4b7 languageName: node linkType: hard @@ -35946,9 +35874,9 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:7.2.3, protobufjs@npm:^7.0.0": - version: 7.2.3 - resolution: "protobufjs@npm:7.2.3" +"protobufjs@npm:7.2.4, protobufjs@npm:^7.0.0": + version: 7.2.4 + resolution: "protobufjs@npm:7.2.4" dependencies: "@protobufjs/aspromise": ^1.1.2 "@protobufjs/base64": ^1.1.2 @@ -35962,31 +35890,7 @@ __metadata: "@protobufjs/utf8": ^1.1.0 "@types/node": ">=13.7.0" long: ^5.0.0 - checksum: 9afa6de5fced0139a5180c063718508fac3ea734a9f1aceb99712367b15473a83327f91193f16b63540f9112b09a40912f5f0441a9b0d3f3c6a1c7f707d78249 - languageName: node - linkType: hard - -"protobufjs@npm:^6.11.2": - version: 6.11.3 - resolution: "protobufjs@npm:6.11.3" - dependencies: - "@protobufjs/aspromise": ^1.1.2 - "@protobufjs/base64": ^1.1.2 - "@protobufjs/codegen": ^2.0.4 - "@protobufjs/eventemitter": ^1.1.0 - "@protobufjs/fetch": ^1.1.0 - "@protobufjs/float": ^1.0.2 - "@protobufjs/inquire": ^1.1.0 - "@protobufjs/path": ^1.1.2 - "@protobufjs/pool": ^1.1.0 - "@protobufjs/utf8": ^1.1.0 - "@types/long": ^4.0.1 - "@types/node": ">=13.7.0" - long: ^4.0.0 - bin: - pbjs: bin/pbjs - pbts: bin/pbts - checksum: 4a6ce1964167e4c45c53fd8a312d7646415c777dd31b4ba346719947b88e61654912326101f927da387d6b6473ab52a7ea4f54d6f15d63b31130ce28e2e15070 + checksum: a952cdf2a5e5250c16ae651b570849b6f5b20a5475c3eef63ffb290ad239aa2916adfc1cc676f7fc93c69f48113df268761c0c246f7f023118c85bdd1a170044 languageName: node linkType: hard @@ -36256,14 +36160,7 @@ __metadata: languageName: node linkType: hard -"ramda@npm:^0.28.0": - version: 0.28.0 - resolution: "ramda@npm:0.28.0" - checksum: 44ea6e5010bba70151b6a92d8114a91915e8b5a16105cce65fae58c9d7386b812c429645e35f21141d7087568550ce383bc10ee1a65cdec951f4b69ea457e6a4 - languageName: node - linkType: hard - -"ramda@npm:~0.29.0": +"ramda@npm:^0.29.0, ramda@npm:~0.29.0": version: 0.29.0 resolution: "ramda@npm:0.29.0" checksum: 9ab26c06eb7545cbb7eebcf75526d6ee2fcaae19e338f165b2bf32772121e7b28192d6664d1ba222ff76188ba26ab307342d66e805dbb02c860560adc4d5dd57 @@ -36760,9 +36657,9 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:^8.0.5": - version: 8.0.5 - resolution: "react-redux@npm:8.0.5" +"react-redux@npm:^8.1.2": + version: 8.1.2 + resolution: "react-redux@npm:8.1.2" dependencies: "@babel/runtime": ^7.12.1 "@types/hoist-non-react-statics": ^3.3.1 @@ -36776,7 +36673,7 @@ __metadata: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 react-native: ">=0.59" - redux: ^4 + redux: ^4 || ^5.0.0-beta.0 peerDependenciesMeta: "@types/react": optional: true @@ -36788,7 +36685,7 @@ __metadata: optional: true redux: optional: true - checksum: a108f4f7ead6ac005e656d46051474a2bbdb31ede481bbbb3d8d779c1a35e1940b8655577cc5021313411864d305f67fc719aa48d6e5ed8288cf9cbe8b7042e4 + checksum: 4d5976b0f721e4148475871fcabce2fee875cc7f70f9a292f3370d63b38aa1dd474eb303c073c5555f3e69fc732f3bac05303def60304775deb28361e3f4b7cc languageName: node linkType: hard @@ -37101,16 +36998,6 @@ __metadata: languageName: node linkType: hard -"read-pkg-up@npm:^1.0.1": - version: 1.0.1 - resolution: "read-pkg-up@npm:1.0.1" - dependencies: - find-up: ^1.0.0 - read-pkg: ^1.0.0 - checksum: d18399a0f46e2da32beb2f041edd0cda49d2f2cc30195a05c759ef3ed9b5e6e19ba1ad1bae2362bdec8c6a9f2c3d18f4d5e8c369e808b03d498d5781cb9122c7 - languageName: node - linkType: hard - "read-pkg-up@npm:^7.0.1": version: 7.0.1 resolution: "read-pkg-up@npm:7.0.1" @@ -37122,17 +37009,6 @@ __metadata: languageName: node linkType: hard -"read-pkg@npm:^1.0.0": - version: 1.1.0 - resolution: "read-pkg@npm:1.1.0" - dependencies: - load-json-file: ^1.0.0 - normalize-package-data: ^2.3.2 - path-type: ^1.0.0 - checksum: a0f5d5e32227ec8e6a028dd5c5134eab229768dcb7a5d9a41a284ed28ad4b9284fecc47383dc1593b5694f4de603a7ffaee84b738956b9b77e0999567485a366 - languageName: node - linkType: hard - "read-pkg@npm:^5.2.0": version: 5.2.0 resolution: "read-pkg@npm:5.2.0" @@ -37381,6 +37257,20 @@ __metadata: languageName: node linkType: hard +"reflect.getprototypeof@npm:^1.0.3": + version: 1.0.3 + resolution: "reflect.getprototypeof@npm:1.0.3" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + get-intrinsic: ^1.1.1 + globalthis: ^1.0.3 + which-builtin-type: ^1.1.3 + checksum: 843e2506c013da66f83635f943c5bd41243bc6c7703298531cfb16eb6baaefd92f83031fa37140ad31c4edc86938b6eb385e6fc85bf1628e79348ed49e044f3d + languageName: node + linkType: hard + "refractor@npm:^3.6.0": version: 3.6.0 resolution: "refractor@npm:3.6.0" @@ -37429,6 +37319,13 @@ __metadata: languageName: node linkType: hard +"regenerator-runtime@npm:^0.14.0": + version: 0.14.0 + resolution: "regenerator-runtime@npm:0.14.0" + checksum: 1c977ad82a82a4412e4f639d65d22be376d3ebdd30da2c003eeafdaaacd03fc00c2320f18120007ee700900979284fc78a9f00da7fb593f6e6eeebc673fba9a3 + languageName: node + linkType: hard + "regenerator-transform@npm:^0.15.0": version: 0.15.0 resolution: "regenerator-transform@npm:0.15.0" @@ -37438,14 +37335,14 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.4.3": - version: 1.4.3 - resolution: "regexp.prototype.flags@npm:1.4.3" +"regexp.prototype.flags@npm:^1.4.3, regexp.prototype.flags@npm:^1.5.0": + version: 1.5.0 + resolution: "regexp.prototype.flags@npm:1.5.0" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - functions-have-names: ^1.2.2 - checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 + define-properties: ^1.2.0 + functions-have-names: ^1.2.3 + checksum: c541687cdbdfff1b9a07f6e44879f82c66bbf07665f9a7544c5fd16acdb3ec8d1436caab01662d2fbcad403f3499d49ab0b77fbc7ef29ef961d98cc4bc9755b4 languageName: node linkType: hard @@ -37672,13 +37569,6 @@ __metadata: languageName: node linkType: hard -"require-main-filename@npm:^1.0.1": - version: 1.0.1 - resolution: "require-main-filename@npm:1.0.1" - checksum: 1fef30754da961f4e13c450c3eb60c7ae898a529c6ad6fa708a70bd2eed01564ceb299187b2899f5562804d797a059f39a5789884d0ac7b7ae1defc68fba4abf - languageName: node - linkType: hard - "require-main-filename@npm:^2.0.0": version: 2.0.0 resolution: "require-main-filename@npm:2.0.0" @@ -37753,16 +37643,16 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:~1.22.1": - version: 1.22.1 - resolution: "resolve@npm:1.22.1" +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3, resolve@npm:~1.22.1": + version: 1.22.4 + resolution: "resolve@npm:1.22.4" dependencies: - is-core-module: ^2.9.0 + is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e + checksum: 23f25174c2736ce24c6d918910e0d1f89b6b38fefa07a995dff864acd7863d59a7f049e691f93b4b2ee29696303390d921552b6d1b841ed4a8101f517e1d0124 languageName: node linkType: hard @@ -37779,15 +37669,6 @@ __metadata: languageName: node linkType: hard -"resolve@npm:~1.17.0": - version: 1.17.0 - resolution: "resolve@npm:1.17.0" - dependencies: - path-parse: ^1.0.6 - checksum: 9ceaf83b3429f2d7ff5d0281b8d8f18a1f05b6ca86efea7633e76b8f76547f33800799dfdd24434942dec4fbd9e651ed3aef577d9a6b5ec87ad89c1060e24759 - languageName: node - linkType: hard - "resolve@npm:~1.19.0": version: 1.19.0 resolution: "resolve@npm:1.19.0" @@ -37798,16 +37679,16 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@~1.22.1#~builtin": - version: 1.22.1 - resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.3#~builtin, resolve@patch:resolve@~1.22.1#~builtin": + version: 1.22.4 + resolution: "resolve@patch:resolve@npm%3A1.22.4#~builtin::version=1.22.4&hash=07638b" dependencies: - is-core-module: ^2.9.0 + is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b + checksum: c45f2545fdc4d21883861b032789e20aa67a2f2692f68da320cc84d5724cd02f2923766c5354b3210897e88f1a7b3d6d2c7c22faeead8eed7078e4c783a444bc languageName: node linkType: hard @@ -37824,15 +37705,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@~1.17.0#~builtin": - version: 1.17.0 - resolution: "resolve@patch:resolve@npm%3A1.17.0#~builtin::version=1.17.0&hash=07638b" - dependencies: - path-parse: ^1.0.6 - checksum: 6fd799f282ddf078c4bc20ce863e3af01fa8cb218f0658d9162c57161a2dbafe092b13015b9a4c58d0e1e801cf7aa7a4f13115fea9db98c3f9a0c43e429bad6f - languageName: node - linkType: hard - "resolve@patch:resolve@~1.19.0#~builtin": version: 1.19.0 resolution: "resolve@patch:resolve@npm%3A1.19.0#~builtin::version=1.19.0&hash=07638b" @@ -38131,7 +38003,7 @@ __metadata: "@types/node": ^16.11.26 "@types/webpack": ^5.28.0 command-exists: ^1.2.9 - concurrently: ^7.0.0 + concurrently: ^8.0.0 cross-env: ^7.0.0 e2e-test: "workspace:*" eslint: ^8.6.0 @@ -38215,7 +38087,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:7.8.0, rxjs@npm:^7.0.0, rxjs@npm:^7.2.0, rxjs@npm:^7.5.1, rxjs@npm:^7.5.5, rxjs@npm:^7.8.0": +"rxjs@npm:7.8.0": version: 7.8.0 resolution: "rxjs@npm:7.8.0" dependencies: @@ -38233,6 +38105,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:^7.2.0, rxjs@npm:^7.5.1, rxjs@npm:^7.5.5, rxjs@npm:^7.8.0, rxjs@npm:^7.8.1": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" + dependencies: + tslib: ^2.1.0 + checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 + languageName: node + linkType: hard + "sade@npm:^1.7.3": version: 1.8.1 resolution: "sade@npm:1.8.1" @@ -38242,6 +38123,18 @@ __metadata: languageName: node linkType: hard +"safe-array-concat@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-array-concat@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.0 + has-symbols: ^1.0.3 + isarray: ^2.0.5 + checksum: f43cb98fe3b566327d0c09284de2b15fb85ae964a89495c1b1a5d50c7c8ed484190f4e5e71aacc167e16231940079b326f2c0807aea633d47cc7322f40a6b57f + languageName: node + linkType: hard + "safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" @@ -38457,7 +38350,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.6.0, semver@npm:^5.7.1": +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.6.0": version: 5.7.2 resolution: "semver@npm:5.7.2" bin: @@ -38466,7 +38359,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.0.0, semver@npm:~7.0.0": +"semver@npm:7.0.0": version: 7.0.0 resolution: "semver@npm:7.0.0" bin: @@ -38475,7 +38368,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0": +"semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -38484,7 +38377,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.0, semver@npm:^7.5.3": +"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.0, semver@npm:^7.5.3, semver@npm:~7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -38495,17 +38388,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:~7.3.0": - version: 7.3.8 - resolution: "semver@npm:7.3.8" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 - languageName: node - linkType: hard - "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -38744,10 +38626,10 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.7.3": - version: 1.7.3 - resolution: "shell-quote@npm:1.7.3" - checksum: aca58e73a3a5d933d02e0bdddedc53ee14f7c2ec264f97ac915b9d4482d077a38e422aa664631d60a672cd3cdb4054eb2e6c0303f54882453dacb6483e482d34 +"shell-quote@npm:^1.7.3, shell-quote@npm:^1.8.1": + version: 1.8.1 + resolution: "shell-quote@npm:1.8.1" + checksum: 5f01201f4ef504d4c6a9d0d283fa17075f6770bfbe4c5850b074974c68062f37929ca61700d95ad2ac8822e14e8c4b990ca0e6e9272e64befd74ce5e19f0736b languageName: node linkType: hard @@ -38865,12 +38747,12 @@ __metadata: languageName: node linkType: hard -"simple-update-notifier@npm:^1.0.7": - version: 1.0.7 - resolution: "simple-update-notifier@npm:1.0.7" +"simple-update-notifier@npm:^2.0.0": + version: 2.0.0 + resolution: "simple-update-notifier@npm:2.0.0" dependencies: - semver: ~7.0.0 - checksum: aaadc1f158ad5101b363d1c7aed1f30fc1cac59a760aa31702633e0e6fe423348f07d0e78185aef0aad29130a7b7f0f188c21c7bc7353f897a0ea3682e051a70 + semver: ^7.5.3 + checksum: 9ba00d38ce6a29682f64a46213834e4eb01634c2f52c813a9a7b8873ca49cdbb703696f3290f3b27dc067de6d9418b0b84bef22c3eb074acf352529b2d6c27fd languageName: node linkType: hard @@ -39133,10 +39015,10 @@ __metadata: languageName: node linkType: hard -"spawn-command@npm:^0.0.2-1": - version: 0.0.2-1 - resolution: "spawn-command@npm:0.0.2-1" - checksum: 2cac8519332193d1ed37d57298c4a1f73095e9edd20440fbab4aa47f531da83831734f2b51c44bb42b2747bf3485dec3fa2b0a1003f74c67561f2636622e328b +"spawn-command@npm:0.0.2, spawn-command@npm:^0.0.2-1": + version: 0.0.2 + resolution: "spawn-command@npm:0.0.2" + checksum: e35c5d28177b4d461d33c88cc11f6f3a5079e2b132c11e1746453bbb7a0c0b8a634f07541a2a234fa4758239d88203b758def509161b651e81958894c0b4b64b languageName: node linkType: hard @@ -39618,7 +39500,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^1.0.1, string-width@npm:^1.0.2": +"string-width@npm:^1.0.1": version: 1.0.2 resolution: "string-width@npm:1.0.2" dependencies: @@ -39663,25 +39545,36 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.5": - version: 1.0.5 - resolution: "string.prototype.trimend@npm:1.0.5" +"string.prototype.trim@npm:^1.2.7": + version: 1.2.7 + resolution: "string.prototype.trim@npm:1.2.7" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: d44f543833112f57224e79182debadc9f4f3bf9d48a0414d6f0cbd2a86f2b3e8c0ca1f95c3f8e5b32ae83e91554d79d932fc746b411895f03f93d89ed3dfb6bc + es-abstract: ^1.20.4 + checksum: 05b7b2d6af63648e70e44c4a8d10d8cc457536df78b55b9d6230918bde75c5987f6b8604438c4c8652eb55e4fc9725d2912789eb4ec457d6995f3495af190c09 languageName: node linkType: hard -"string.prototype.trimstart@npm:^1.0.5": - version: 1.0.5 - resolution: "string.prototype.trimstart@npm:1.0.5" +"string.prototype.trimend@npm:^1.0.6": + version: 1.0.6 + resolution: "string.prototype.trimend@npm:1.0.6" dependencies: call-bind: ^1.0.2 define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: a4857c5399ad709d159a77371eeaa8f9cc284469a0b5e1bfe405de16f1fd4166a8ea6f4180e55032f348d1b679b1599fd4301fbc7a8b72bdb3e795e43f7b1048 + es-abstract: ^1.20.4 + checksum: 0fdc34645a639bd35179b5a08227a353b88dc089adf438f46be8a7c197fc3f22f8514c1c9be4629b3cd29c281582730a8cbbad6466c60f76b5f99cf2addb132e + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.6": + version: 1.0.6 + resolution: "string.prototype.trimstart@npm:1.0.6" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 89080feef416621e6ef1279588994305477a7a91648d9436490d56010a1f7adc39167cddac7ce0b9884b8cdbef086987c4dcb2960209f2af8bac0d23ceff4f41 languageName: node linkType: hard @@ -40040,15 +39933,15 @@ __metadata: languageName: node linkType: hard -"swagger-client@npm:^3.19.8": - version: 3.19.8 - resolution: "swagger-client@npm:3.19.8" +"swagger-client@npm:^3.20.0": + version: 3.20.0 + resolution: "swagger-client@npm:3.20.0" dependencies: "@babel/runtime-corejs3": ^7.20.13 - "@swagger-api/apidom-core": ">=0.70.0 <1.0.0" - "@swagger-api/apidom-json-pointer": ">=0.70.0 <1.0.0" - "@swagger-api/apidom-ns-openapi-3-1": ">=0.70.0 <1.0.0" - "@swagger-api/apidom-reference": ">=0.70.0 <1.0.0" + "@swagger-api/apidom-core": ">=0.74.1 <1.0.0" + "@swagger-api/apidom-json-pointer": ">=0.74.1 <1.0.0" + "@swagger-api/apidom-ns-openapi-3-1": ">=0.74.1 <1.0.0" + "@swagger-api/apidom-reference": ">=0.74.1 <1.0.0" cookie: ~0.5.0 cross-fetch: ^3.1.5 deepmerge: ~4.3.0 @@ -40061,27 +39954,27 @@ __metadata: qs: ^6.10.2 traverse: ~0.6.6 url: ~0.11.0 - checksum: 6acb65fe70a24a5c9aace54193b085a594198b1edd3a4533ef86b2ee733a0fcaad8abd7ab0d77d5f14468bccaec88b4b39793a6bc9fa07ff7a925101a106bed4 + checksum: c61452d0c810e05ec319dfc9bf8b876a87d1d5ed74156764ccc627e79e8a6c0d84f4660754dd1e2f7570adfa17a89c2d16cc337421b7da3bec0b428224a70436 languageName: node linkType: hard "swagger-ui-react@npm:^5.0.0": - version: 5.0.0 - resolution: "swagger-ui-react@npm:5.0.0" + version: 5.3.1 + resolution: "swagger-ui-react@npm:5.3.1" dependencies: - "@babel/runtime-corejs3": ^7.22.5 + "@babel/runtime-corejs3": ^7.22.6 "@braintree/sanitize-url": =6.0.2 base64-js: ^1.5.1 classnames: ^2.3.1 css.escape: 1.5.1 deep-extend: 0.6.0 - dompurify: =3.0.2 + dompurify: =3.0.5 ieee754: ^1.2.1 immutable: ^3.x.x js-file-download: ^0.4.12 js-yaml: =4.1.0 lodash: ^4.17.21 - patch-package: ^6.5.1 + patch-package: ^7.0.2 prop-types: ^15.8.1 randexp: ^0.5.3 randombytes: ^2.1.0 @@ -40090,7 +39983,7 @@ __metadata: react-immutable-proptypes: 2.2.0 react-immutable-pure-component: ^2.2.0 react-inspector: ^6.0.1 - react-redux: ^8.0.5 + react-redux: ^8.1.2 react-syntax-highlighter: ^15.5.0 redux: ^4.1.2 redux-immutable: ^4.0.0 @@ -40098,7 +39991,7 @@ __metadata: reselect: ^4.1.8 serialize-error: ^8.1.0 sha.js: ^2.4.11 - swagger-client: ^3.19.8 + swagger-client: ^3.20.0 url-parse: ^1.5.10 xml: =1.0.1 xml-but-prettier: ^1.0.1 @@ -40106,7 +39999,7 @@ __metadata: peerDependencies: react: ">=17.0.0" react-dom: ">=17.0.0" - checksum: 88c6232027ee0ec515bd21d183567b672821410df29281a7996e98f82392c41e9eb8955f463d8e835f93ba21201651fa46cd465cb1639c90501475a9757247f4 + checksum: 6f27e72bbd537a0c9d9d091d4632b4a09d95077c59815330186c495d67e17fd4dd1af649d5e8d93a61c896163d81d3faa2ae402418c3e7f43853b7d840712f7c languageName: node linkType: hard @@ -40130,13 +40023,14 @@ __metadata: linkType: hard "swr@npm:^2.0.0": - version: 2.2.0 - resolution: "swr@npm:2.2.0" + version: 2.2.1 + resolution: "swr@npm:2.2.1" dependencies: + client-only: ^0.0.1 use-sync-external-store: ^1.2.0 peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 - checksum: 1f04795ff9dff54987cbf8a544afc9e42d1350a99e899be8a7cdc4885f561e3ee464f78245ee2ebc8ced262b04023d134f731e276227319dc2d6e1843389ddd8 + checksum: 62ff1b8132966fc977df192cf4452bab94c73dbea304a1438745f27041917154dad32448d8a65fb40d5e3de0b1751ef3fd57b1d71ba450e031517f009d0fc052 languageName: node linkType: hard @@ -40724,14 +40618,14 @@ __metadata: languageName: node linkType: hard -"tree-sitter@npm:=0.20.1": - version: 0.20.1 - resolution: "tree-sitter@npm:0.20.1" +"tree-sitter@npm:=0.20.4": + version: 0.20.4 + resolution: "tree-sitter@npm:0.20.4" dependencies: - nan: ^2.14.0 + nan: ^2.17.0 node-gyp: latest - prebuild-install: ^6.0.1 - checksum: a33bf01f9dc558aa0075c9634d06743ed9b7fbf273d2acfbe96daf83f48aec46be7dc42d80d2423c36593d36d3bf189d3fa023d6ce389e20ff9527fae118b215 + prebuild-install: ^7.1.1 + checksum: 724f9773759a6ece317fff08deef2d2c63a6ea3b4f6723d5d6d56a7a886d27f799641d189d616c121a580e8492992bc2ede8d2e5c4241f30ff4ee9036dc6bb92 languageName: node linkType: hard @@ -40862,15 +40756,15 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.14.1": - version: 3.14.1 - resolution: "tsconfig-paths@npm:3.14.1" +"tsconfig-paths@npm:^3.14.2": + version: 3.14.2 + resolution: "tsconfig-paths@npm:3.14.2" dependencies: "@types/json5": ^0.0.29 - json5: ^1.0.1 + json5: ^1.0.2 minimist: ^1.2.6 strip-bom: ^3.0.0 - checksum: 8afa01c673ebb4782ba53d3a12df97fa837ce524f8ad38ee4e2b2fd57f5ac79abc21c574e9e9eb014d93efe7fe8214001b96233b5c6ea75bd1ea82afe17a4c6d + checksum: a6162eaa1aed680537f93621b82399c7856afd10ec299867b13a0675e981acac4e0ec00896860480efc59fc10fd0b16fdc928c0b885865b52be62cadac692447 languageName: node linkType: hard @@ -41037,6 +40931,53 @@ __metadata: languageName: node linkType: hard +"typed-array-buffer@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-buffer@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.1 + is-typed-array: ^1.1.10 + checksum: 3e0281c79b2a40cd97fe715db803884301993f4e8c18e8d79d75fd18f796e8cd203310fec8c7fdb5e6c09bedf0af4f6ab8b75eb3d3a85da69328f28a80456bd3 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-length@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: b03db16458322b263d87a702ff25388293f1356326c8a678d7515767ef563ef80e1e67ce648b821ec13178dd628eb2afdc19f97001ceae7a31acf674c849af94 + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-offset@npm:1.0.0" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: 04f6f02d0e9a948a95fbfe0d5a70b002191fae0b8fe0fe3130a9b2336f043daf7a3dda56a31333c35a067a97e13f539949ab261ca0f3692c41603a46a94e960b + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.4": + version: 1.0.4 + resolution: "typed-array-length@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + is-typed-array: ^1.1.9 + checksum: 2228febc93c7feff142b8c96a58d4a0d7623ecde6c7a24b2b98eb3170e99f7c7eff8c114f9b283085cd59dcd2bd43aadf20e25bba4b034a53c5bb292f71f8956 + languageName: node + linkType: hard + "typed-rest-client@npm:^1.8.4": version: 1.8.4 resolution: "typed-rest-client@npm:1.8.4" @@ -41055,12 +40996,12 @@ __metadata: languageName: node linkType: hard -"types-ramda@npm:^0.29.3": - version: 0.29.3 - resolution: "types-ramda@npm:0.29.3" +"types-ramda@npm:^0.29.4": + version: 0.29.4 + resolution: "types-ramda@npm:0.29.4" dependencies: ts-toolbelt: ^9.6.0 - checksum: 2efb5182a02b9b11b0fd960c501c8a3c595f7fe82922526872020868a6a98c788b544c6beccb48360f1af9401d0c7f5e0be9b2ca484987f0339a71f48cec6199 + checksum: 08a872e71555fe6a3f1fd9381989a573d493f2156d9689f3bb72278db73c848122d67a9efff1867ce97b3dfbb2fdadaa3864daaee014ab19cf9bbfe2c171ed52 languageName: node linkType: hard @@ -41082,7 +41023,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~4.8.2, typescript@npm:~4.8.4": +"typescript@npm:~4.8.2": version: 4.8.4 resolution: "typescript@npm:4.8.4" bin: @@ -41102,7 +41043,17 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@~4.8.2#~builtin, typescript@patch:typescript@~4.8.4#~builtin": +"typescript@npm:~5.0.4": + version: 5.0.4 + resolution: "typescript@npm:5.0.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172 + languageName: node + linkType: hard + +"typescript@patch:typescript@~4.8.2#~builtin": version: 4.8.4 resolution: "typescript@patch:typescript@npm%3A4.8.4#~builtin::version=4.8.4&hash=a1c5e5" bin: @@ -41122,6 +41073,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@~5.0.4#~builtin": + version: 5.0.4 + resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=a1c5e5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 6a1fe9a77bb9c5176ead919cc4a1499ee63e46b4e05bf667079f11bf3a8f7887f135aa72460a4c3b016e6e6bb65a822cb8689a6d86cbfe92d22cc9f501f09213 + languageName: node + linkType: hard + "ua-parser-js@npm:^0.7.30": version: 0.7.33 resolution: "ua-parser-js@npm:0.7.33" @@ -41439,10 +41400,10 @@ __metadata: languageName: node linkType: hard -"unraw@npm:^2.0.1": - version: 2.0.1 - resolution: "unraw@npm:2.0.1" - checksum: af9a9d2f6e420cb4f52fe2f1f5982e6b0be95da640d6ae8d6d9ff631d864771793cb9fe7e2a16ef1ce631b94065f4438e7bd7f1701076fc69296edc4e704d42f +"unraw@npm:^3.0.0": + version: 3.0.0 + resolution: "unraw@npm:3.0.0" + checksum: 19eee0bc500ce197d262b79723a2c8c81c1d716baaa2a62c48a4d0d6b9e1fd9d350c5df86262e51343d591ab9c8a47ed150317d0b867b2b65795cdc17ef69873 languageName: node linkType: hard @@ -42093,10 +42054,10 @@ __metadata: languageName: node linkType: hard -"web-tree-sitter@npm:=0.20.7": - version: 0.20.7 - resolution: "web-tree-sitter@npm:0.20.7" - checksum: f2949a679bbca5ecb225ab26a67ff99e6e3907fa905c6bd62bfe6d2cfa216347f8cc481efcd32f181cb6b964bc03c42f9611e16da0825bff43de389b17ff9f7f +"web-tree-sitter@npm:=0.20.3": + version: 0.20.3 + resolution: "web-tree-sitter@npm:0.20.3" + checksum: 1187b48d69d6f6319c74ca8f413e8d7c1703869a351070053351ef169c045aad16e5c6b2a73779beaade2f0b6bb3433166363355c9d02e9b2dcf60a195dbffdb languageName: node linkType: hard @@ -42235,8 +42196,8 @@ __metadata: linkType: hard "webpack@npm:^5, webpack@npm:^5.70.0": - version: 5.88.1 - resolution: "webpack@npm:5.88.1" + version: 5.88.2 + resolution: "webpack@npm:5.88.2" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^1.0.0 @@ -42267,7 +42228,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 726e7e05ab2e7c142609a673dd6aa1a711ed97f349418a2a393d650c5ddad172d191257f60e1e37f6b2a77261571c202aabd5ce9240791a686774f0801cf5ec2 + checksum: 79476a782da31a21f6dd38fbbd06b68da93baf6a62f0d08ca99222367f3b8668f5a1f2086b7bb78e23172e31fa6df6fa7ab09b25e827866c4fc4dc2b30443ce2 languageName: node linkType: hard @@ -42372,6 +42333,26 @@ __metadata: languageName: node linkType: hard +"which-builtin-type@npm:^1.1.3": + version: 1.1.3 + resolution: "which-builtin-type@npm:1.1.3" + dependencies: + function.prototype.name: ^1.1.5 + has-tostringtag: ^1.0.0 + is-async-function: ^2.0.0 + is-date-object: ^1.0.5 + is-finalizationregistry: ^1.0.2 + is-generator-function: ^1.0.10 + is-regex: ^1.1.4 + is-weakref: ^1.0.2 + isarray: ^2.0.5 + which-boxed-primitive: ^1.0.2 + which-collection: ^1.0.1 + which-typed-array: ^1.1.9 + checksum: 43730f7d8660ff9e33d1d3f9f9451c4784265ee7bf222babc35e61674a11a08e1c2925019d6c03154fcaaca4541df43abe35d2720843b9b4cbcebdcc31408f36 + languageName: node + linkType: hard + "which-collection@npm:^1.0.1": version: 1.0.1 resolution: "which-collection@npm:1.0.1" @@ -42384,13 +42365,6 @@ __metadata: languageName: node linkType: hard -"which-module@npm:^1.0.0": - version: 1.0.0 - resolution: "which-module@npm:1.0.0" - checksum: 98434f7deb36350cb543c1f15612188541737e1f12d39b23b1c371dff5cf4aa4746210f2bdec202d5fe9da8682adaf8e3f7c44c520687d30948cfc59d5534edb - languageName: node - linkType: hard - "which-module@npm:^2.0.0": version: 2.0.0 resolution: "which-module@npm:2.0.0" @@ -42408,17 +42382,16 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": - version: 1.1.9 - resolution: "which-typed-array@npm:1.1.9" +"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": + version: 1.1.11 + resolution: "which-typed-array@npm:1.1.11" dependencies: available-typed-arrays: ^1.0.5 call-bind: ^1.0.2 for-each: ^0.3.3 gopd: ^1.0.1 has-tostringtag: ^1.0.0 - is-typed-array: ^1.1.10 - checksum: fe0178ca44c57699ca2c0e657b64eaa8d2db2372a4e2851184f568f98c478ae3dc3fdb5f7e46c384487046b0cf9e23241423242b277e03e8ba3dabc7c84c98ef + checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 languageName: node linkType: hard @@ -42453,15 +42426,6 @@ __metadata: languageName: node linkType: hard -"window-size@npm:^0.2.0": - version: 0.2.0 - resolution: "window-size@npm:0.2.0" - bin: - window-size: cli.js - checksum: a85e2acf156cfa194301294809867bdadd8a48ee5d972d9fa8e3e1b3420a1d0201b13ac8eb0348a0d14bbf2c3316565b6a749749c2384c5d286caf8a064c4f90 - languageName: node - linkType: hard - "winston-transport@npm:^4.5.0": version: 4.5.0 resolution: "winston-transport@npm:4.5.0" @@ -42517,17 +42481,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^2.0.0": - version: 2.1.0 - resolution: "wrap-ansi@npm:2.1.0" - dependencies: - string-width: ^1.0.1 - strip-ansi: ^3.0.1 - checksum: 2dacd4b3636f7a53ee13d4d0fe7fa2ed9ad81e9967e17231924ea88a286ec4619a78288de8d41881ee483f4449ab2c0287cde8154ba1bd0126c10271101b2ee3 - languageName: node - linkType: hard - -"wrap-ansi@npm:^6.2.0": +"wrap-ansi@npm:^6.0.1, wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" dependencies: @@ -42750,20 +42704,13 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0": +"xtend@npm:^4.0.0, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a languageName: node linkType: hard -"y18n@npm:^3.2.1": - version: 3.2.2 - resolution: "y18n@npm:3.2.2" - checksum: 6154fd7544f8bbf5b18cdf77692ed88d389be49c87238ecb4e0d6a5276446cd2a5c29cc4bdbdddfc7e4e498b08df9d7e38df4a1453cf75eecfead392246ea74a - languageName: node - linkType: hard - "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3" @@ -42806,6 +42753,21 @@ __metadata: languageName: node linkType: hard +"yaml-diff-patch@npm:^2.0.0": + version: 2.0.0 + resolution: "yaml-diff-patch@npm:2.0.0" + dependencies: + fast-json-patch: ^3.1.0 + oppa: ^0.4.0 + yaml: ^2.0.0-10 + bin: + yaml-diff-patch: dist/bin/yaml-patch.js + yaml-overwrite: dist/bin/yaml-patch.js + yaml-patch: dist/bin/yaml-patch.js + checksum: 5207d8523584eb6088fe32a0c6010599260ecfa5f959d120a1bad02f19143d1ddeafe10c37ccf125ac04d079072a5ead92b55c6787fd64d12f5acbb0d172e7ec + languageName: node + linkType: hard + "yaml@npm:^1.10.0, yaml@npm:^1.10.2, yaml@npm:^1.7.2": version: 1.10.2 resolution: "yaml@npm:1.10.2" @@ -42813,10 +42775,10 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.0.0, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2": - version: 2.2.2 - resolution: "yaml@npm:2.2.2" - checksum: d90c235e099e30094dcff61ba3350437aef53325db4a6bcd04ca96e1bfe7e348b191f6a7a52b5211e2dbc4eeedb22a00b291527da030de7c189728ef3f2b4eb3 +"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2": + version: 2.3.1 + resolution: "yaml@npm:2.3.1" + checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54 languageName: node linkType: hard @@ -42837,23 +42799,13 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.0.0": +"yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c languageName: node linkType: hard -"yargs-parser@npm:^3.2.0": - version: 3.2.0 - resolution: "yargs-parser@npm:3.2.0" - dependencies: - camelcase: ^3.0.0 - lodash.assign: ^4.1.0 - checksum: d86fd69816a28a617f4cad21f7bfe7f7c931b16d063c73449cd914db409b8d5981a0f29f9e2a05014a7229164aa47336adf5a8856fa9870ab892346d29138e9a - languageName: node - linkType: hard - "yargs@npm:^15.1.0, yargs@npm:^15.3.1": version: 15.4.1 resolution: "yargs@npm:15.4.1" @@ -42888,40 +42840,18 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.0.0, yargs@npm:^17.1.1, yargs@npm:^17.2.1, yargs@npm:^17.3.1": - version: 17.5.1 - resolution: "yargs@npm:17.5.1" +"yargs@npm:^17.0.0, yargs@npm:^17.1.1, yargs@npm:^17.2.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" dependencies: - cliui: ^7.0.2 + cliui: ^8.0.1 escalade: ^3.1.1 get-caller-file: ^2.0.5 require-directory: ^2.1.1 string-width: ^4.2.3 y18n: ^5.0.5 - yargs-parser: ^21.0.0 - checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde - languageName: node - linkType: hard - -"yargs@npm:^5.0.0": - version: 5.0.0 - resolution: "yargs@npm:5.0.0" - dependencies: - cliui: ^3.2.0 - decamelize: ^1.1.1 - get-caller-file: ^1.0.1 - lodash.assign: ^4.2.0 - os-locale: ^1.4.0 - read-pkg-up: ^1.0.1 - require-directory: ^2.1.1 - require-main-filename: ^1.0.1 - set-blocking: ^2.0.0 - string-width: ^1.0.2 - which-module: ^1.0.0 - window-size: ^0.2.0 - y18n: ^3.2.1 - yargs-parser: ^3.2.0 - checksum: 478e9c8562c5cf5b9c2efc7c917e0b00c489cc50153d5d911ab68767c2cfddaf0b5bd4e04d6fefc00cb1d8f6263eab1d73df79a24d650ba95f5d45c819a1585a + yargs-parser: ^21.1.1 + checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a languageName: node linkType: hard @@ -43102,12 +43032,12 @@ __metadata: languageName: node linkType: hard -"zod-to-json-schema@npm:^3.20.4": - version: 3.20.4 - resolution: "zod-to-json-schema@npm:3.20.4" +"zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4": + version: 3.21.4 + resolution: "zod-to-json-schema@npm:3.21.4" peerDependencies: - zod: ^3.20.0 - checksum: 55bc649dbc82ce25fbfbfdd42ca530d883d83be5d02cb81e89f6401d54bca151fc6b8cc57999c75eb6a3dcaa3f000697315fbd4f505637472621570ed52784d8 + zod: ^3.21.4 + checksum: 899c1f461fb6547c0b08a265c82040c250be9b88d3f408f2f3ff77a418fdfad7549077e589d418fccb312c1f6d555c3c7217b199cc9072762e1fab20716dd2a6 languageName: node linkType: hard