diff --git a/.changeset/chilly-sheep-scream.md b/.changeset/chilly-sheep-scream.md new file mode 100644 index 0000000000..eb568ffd8f --- /dev/null +++ b/.changeset/chilly-sheep-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': minor +--- + +Added support of filtering based on system columns in catalog table diff --git a/.changeset/create-app-1742907012.md b/.changeset/create-app-1742907012.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1742907012.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/cuddly-kids-sell.md b/.changeset/cuddly-kids-sell.md new file mode 100644 index 0000000000..3b7cb16841 --- /dev/null +++ b/.changeset/cuddly-kids-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Bump @module-federation/enhanced ^0.9.0 to fix GHSA-593f-38f6-jp5m diff --git a/.changeset/cuddly-mirrors-ask.md b/.changeset/cuddly-mirrors-ask.md new file mode 100644 index 0000000000..4fe611d067 --- /dev/null +++ b/.changeset/cuddly-mirrors-ask.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Updated styles for the Menu component in Canon. diff --git a/.changeset/deep-flies-vanish.md b/.changeset/deep-flies-vanish.md new file mode 100644 index 0000000000..30aa10f290 --- /dev/null +++ b/.changeset/deep-flies-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +--- + +Added confirmation popup for delete entities in Catalog Unprocessed Entities plugin diff --git a/.changeset/early-feet-lay.md b/.changeset/early-feet-lay.md new file mode 100644 index 0000000000..71a557e5e5 --- /dev/null +++ b/.changeset/early-feet-lay.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-auth-backend-module-oidc-provider': patch +--- + +Added custom timeout setting for oidc provider + +Here is an example of how to use a custom timeout with the configuration: + +```yaml +auth: + oidc: + production: + clientId: ${AUTH_GOOGLE_CLIENT_ID} + clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET} + timeout: + seconds: 30 +``` diff --git a/.changeset/famous-dragons-bake.md b/.changeset/famous-dragons-bake.md new file mode 100644 index 0000000000..17b156de06 --- /dev/null +++ b/.changeset/famous-dragons-bake.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +add filter for repos by membership and topics diff --git a/.changeset/lemon-shoes-enter.md b/.changeset/lemon-shoes-enter.md new file mode 100644 index 0000000000..a950989d93 --- /dev/null +++ b/.changeset/lemon-shoes-enter.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add global CSS reset for anchor tags. diff --git a/.changeset/orange-queens-grin.md b/.changeset/orange-queens-grin.md new file mode 100644 index 0000000000..66955655d7 --- /dev/null +++ b/.changeset/orange-queens-grin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +add template-extensions scaffolder service endpoint diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..c7911415f8 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,234 @@ +{ + "mode": "pre", + "tag": "next", + "initialVersions": { + "example-app": "0.2.107", + "@backstage/app-defaults": "1.6.0", + "example-app-next": "0.0.21", + "app-next-example-plugin": "0.0.21", + "example-backend": "0.0.36", + "@backstage/backend-app-api": "1.2.1", + "@backstage/backend-defaults": "0.8.2", + "@backstage/backend-dev-utils": "0.1.5", + "@backstage/backend-dynamic-feature-service": "0.6.1", + "example-backend-legacy": "0.2.108", + "@backstage/backend-openapi-utils": "0.5.1", + "@backstage/backend-plugin-api": "1.2.1", + "@backstage/backend-test-utils": "1.3.1", + "@backstage/canon": "0.2.0", + "@backstage/catalog-client": "1.9.1", + "@backstage/catalog-model": "1.7.3", + "@backstage/cli": "0.31.0", + "@backstage/cli-common": "0.1.15", + "@backstage/cli-node": "0.2.13", + "@backstage/codemods": "0.1.52", + "@backstage/config": "1.3.2", + "@backstage/config-loader": "1.10.0", + "@backstage/core-app-api": "1.16.0", + "@backstage/core-compat-api": "0.4.0", + "@backstage/core-components": "0.17.0", + "@backstage/core-plugin-api": "1.10.5", + "@backstage/create-app": "0.6.0", + "@backstage/dev-utils": "1.1.8", + "e2e-test": "0.2.26", + "@backstage/e2e-test-utils": "0.1.1", + "@backstage/errors": "1.2.7", + "@backstage/eslint-plugin": "0.1.10", + "@backstage/frontend-app-api": "0.11.0", + "@backstage/frontend-defaults": "0.2.0", + "@internal/frontend": "0.0.7", + "@backstage/frontend-plugin-api": "0.10.0", + "@backstage/frontend-test-utils": "0.3.0", + "@backstage/integration": "1.16.2", + "@backstage/integration-aws-node": "0.1.15", + "@backstage/integration-react": "1.2.5", + "@internal/opaque": "0.0.1", + "@backstage/release-manifests": "0.0.12", + "@backstage/repo-tools": "0.13.1", + "@internal/scaffolder": "0.0.7", + "@techdocs/cli": "1.9.1", + "techdocs-cli-embedded-app": "0.2.106", + "@backstage/test-utils": "1.7.6", + "@backstage/theme": "0.6.4", + "@backstage/types": "1.2.1", + "@backstage/version-bridge": "1.0.11", + "yarn-plugin-backstage": "0.0.4", + "@backstage/plugin-api-docs": "0.12.5", + "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.9", + "@backstage/plugin-app": "0.1.7", + "@backstage/plugin-app-backend": "0.5.0", + "@backstage/plugin-app-node": "0.1.31", + "@backstage/plugin-app-visualizer": "0.1.17", + "@backstage/plugin-auth-backend": "0.24.4", + "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.1", + "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.1", + "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.1", + "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.6", + "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.1", + "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.1", + "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.1", + "@backstage/plugin-auth-backend-module-github-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-google-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-guest-provider": "0.2.6", + "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.1", + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.6", + "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.1", + "@backstage/plugin-auth-backend-module-okta-provider": "0.2.1", + "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.1", + "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.1", + "@backstage/plugin-auth-node": "0.6.1", + "@backstage/plugin-auth-react": "0.1.13", + "@backstage/plugin-bitbucket-cloud-common": "0.2.28", + "@backstage/plugin-catalog": "1.28.0", + "@backstage/plugin-catalog-backend": "1.32.0", + "@backstage/plugin-catalog-backend-module-aws": "0.4.9", + "@backstage/plugin-catalog-backend-module-azure": "0.3.3", + "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.0", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.4.6", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.3.3", + "@backstage/plugin-catalog-backend-module-gcp": "0.3.6", + "@backstage/plugin-catalog-backend-module-gerrit": "0.3.0", + "@backstage/plugin-catalog-backend-module-github": "0.7.11", + "@backstage/plugin-catalog-backend-module-github-org": "0.3.8", + "@backstage/plugin-catalog-backend-module-gitlab": "0.6.4", + "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.7", + "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.6.4", + "@backstage/plugin-catalog-backend-module-ldap": "0.11.3", + "@backstage/plugin-catalog-backend-module-logs": "0.1.8", + "@backstage/plugin-catalog-backend-module-msgraph": "0.6.8", + "@backstage/plugin-catalog-backend-module-openapi": "0.2.8", + "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.8", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.6", + "@backstage/plugin-catalog-backend-module-unprocessed": "0.5.6", + "@backstage/plugin-catalog-common": "1.1.3", + "@backstage/plugin-catalog-graph": "0.4.17", + "@backstage/plugin-catalog-import": "0.12.11", + "@backstage/plugin-catalog-node": "1.16.1", + "@backstage/plugin-catalog-react": "1.16.0", + "@backstage/plugin-catalog-unprocessed-entities": "0.2.15", + "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.7", + "@backstage/plugin-config-schema": "0.1.66", + "@backstage/plugin-devtools": "0.1.25", + "@backstage/plugin-devtools-backend": "0.5.3", + "@backstage/plugin-devtools-common": "0.1.15", + "@backstage/plugin-events-backend": "0.5.0", + "@backstage/plugin-events-backend-module-aws-sqs": "0.4.9", + "@backstage/plugin-events-backend-module-azure": "0.2.18", + "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.18", + "@backstage/plugin-events-backend-module-gerrit": "0.2.18", + "@backstage/plugin-events-backend-module-github": "0.2.18", + "@backstage/plugin-events-backend-module-gitlab": "0.2.18", + "@backstage/plugin-events-backend-test-utils": "0.1.42", + "@backstage/plugin-events-node": "0.4.9", + "@internal/plugin-todo-list": "1.0.37", + "@internal/plugin-todo-list-backend": "1.0.37", + "@internal/plugin-todo-list-common": "1.0.24", + "@backstage/plugin-home": "0.8.6", + "@backstage/plugin-home-react": "0.1.24", + "@backstage/plugin-kubernetes": "0.12.5", + "@backstage/plugin-kubernetes-backend": "0.19.4", + "@backstage/plugin-kubernetes-cluster": "0.0.23", + "@backstage/plugin-kubernetes-common": "0.9.4", + "@backstage/plugin-kubernetes-node": "0.2.4", + "@backstage/plugin-kubernetes-react": "0.5.5", + "@backstage/plugin-notifications": "0.5.3", + "@backstage/plugin-notifications-backend": "0.5.4", + "@backstage/plugin-notifications-backend-module-email": "0.3.7", + "@backstage/plugin-notifications-common": "0.0.8", + "@backstage/plugin-notifications-node": "0.2.13", + "@backstage/plugin-org": "0.6.37", + "@backstage/plugin-org-react": "0.1.36", + "@backstage/plugin-permission-backend": "0.5.55", + "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.6", + "@backstage/plugin-permission-common": "0.8.4", + "@backstage/plugin-permission-node": "0.9.0", + "@backstage/plugin-permission-react": "0.4.32", + "@backstage/plugin-proxy-backend": "0.6.0", + "@backstage/plugin-proxy-node": "0.1.2", + "@backstage/plugin-scaffolder": "1.29.0", + "@backstage/plugin-scaffolder-backend": "1.31.0", + "@backstage/plugin-scaffolder-backend-module-azure": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.8", + "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.7", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.8", + "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-github": "0.6.1", + "@backstage/plugin-scaffolder-backend-module-gitlab": "0.8.1", + "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.8", + "@backstage/plugin-scaffolder-backend-module-rails": "0.5.7", + "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.7", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.8", + "@backstage/plugin-scaffolder-common": "1.5.10", + "@backstage/plugin-scaffolder-node": "0.8.0", + "@backstage/plugin-scaffolder-node-test-utils": "0.2.0", + "@backstage/plugin-scaffolder-react": "1.14.6", + "@backstage/plugin-search": "1.4.24", + "@backstage/plugin-search-backend": "2.0.0", + "@backstage/plugin-search-backend-module-catalog": "0.3.2", + "@backstage/plugin-search-backend-module-elasticsearch": "1.7.0", + "@backstage/plugin-search-backend-module-explore": "0.3.0", + "@backstage/plugin-search-backend-module-pg": "0.5.42", + "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.7", + "@backstage/plugin-search-backend-module-techdocs": "0.4.0", + "@backstage/plugin-search-backend-node": "1.3.9", + "@backstage/plugin-search-common": "1.2.17", + "@backstage/plugin-search-react": "1.8.7", + "@backstage/plugin-signals": "0.0.17", + "@backstage/plugin-signals-backend": "0.3.2", + "@backstage/plugin-signals-node": "0.1.18", + "@backstage/plugin-signals-react": "0.0.11", + "@backstage/plugin-techdocs": "1.12.4", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.46", + "@backstage/plugin-techdocs-backend": "2.0.0", + "@backstage/plugin-techdocs-common": "0.1.0", + "@backstage/plugin-techdocs-module-addons-contrib": "1.1.22", + "@backstage/plugin-techdocs-node": "1.13.1", + "@backstage/plugin-techdocs-react": "1.2.15", + "@backstage/plugin-user-settings": "0.8.20", + "@backstage/plugin-user-settings-backend": "0.3.0", + "@backstage/plugin-user-settings-common": "0.0.1" + }, + "changesets": [ + "chilly-sheep-scream", + "clever-tomatoes-jump", + "create-app-1742907012", + "cuddly-dodos-allow", + "cuddly-kids-sell", + "cuddly-mirrors-ask", + "deep-flies-vanish", + "early-feet-lay", + "every-ties-wink", + "famous-dragons-bake", + "funny-hotels-cut", + "giant-donkeys-punch", + "honest-ties-worry", + "itchy-times-behave", + "large-kings-protect", + "lemon-shoes-enter", + "metal-animals-notice", + "odd-bobcats-hang", + "orange-queens-grin", + "sad-pots-try", + "silver-rocks-invite", + "spotty-towns-show", + "strange-planes-kneel", + "sweet-maps-invent", + "swift-boats-add", + "tame-sloths-brake", + "thin-snails-judge", + "tiny-swans-warn", + "twenty-forks-cheat", + "twenty-walls-join", + "wet-penguins-beg", + "wide-planets-camp", + "witty-planets-sing" + ] +} diff --git a/.changeset/strange-planes-kneel.md b/.changeset/strange-planes-kneel.md new file mode 100644 index 0000000000..8e57057aee --- /dev/null +++ b/.changeset/strange-planes-kneel.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-react': minor +'@backstage/plugin-scaffolder': minor +--- + +add api to retrieve template extensions info from scaffolder-backend diff --git a/.changeset/sweet-maps-invent.md b/.changeset/sweet-maps-invent.md new file mode 100644 index 0000000000..390ce57dc7 --- /dev/null +++ b/.changeset/sweet-maps-invent.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +--- + +Fixed the `convertTimeToLocalTimezone` function in the FailedEntities component to correctly parse ISO 8601 date strings and set the timezone to the current local timezone. diff --git a/.changeset/wide-planets-camp.md b/.changeset/wide-planets-camp.md new file mode 100644 index 0000000000..c36ed69dff --- /dev/null +++ b/.changeset/wide-planets-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +fixed misleading example location annotations in docs diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml index 91c0a43447..af625f67ab 100644 --- a/.github/workflows/api-breaking-changes-comment.yml +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -74,7 +74,7 @@ jobs: - name: Cache Comment if: ${{ steps.event.outputs.ACTION != 'closed' }} - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: comment.md key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }} @@ -103,7 +103,7 @@ jobs: - name: Fetch cached Manifests File id: cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 with: path: comment.md key: ${{ needs.setup.outputs.comment-cache-key }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1b5df56ef..76898bc8d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -239,7 +239,7 @@ jobs: # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore backstage-cli cache - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf # v4 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4 with: path: .cache/backstage-cli key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }} @@ -260,7 +260,7 @@ jobs: # Always save success cache even if there were failures, that way it can be used in re-triggered builds - name: save backstage-cli cache - uses: actions/cache/save@d4323d4df104b026a6aa633fdb11d772146be0bf # v4 + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4 if: always() with: path: .cache/backstage-cli diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 0387de0d97..e363fa4680 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -94,7 +94,7 @@ jobs: run: yarn backstage-cli config:check --lax - name: backstage-cli cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 with: path: .cache/backstage-cli key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }} diff --git a/canon-docs/package.json b/canon-docs/package.json index 2bb88bd744..acd20cb66a 100644 --- a/canon-docs/package.json +++ b/canon-docs/package.json @@ -18,7 +18,7 @@ "@uiw/codemirror-themes": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", "motion": "^12.4.1", - "next": "14.2.23", + "next": "14.2.25", "react": "^18.0.2", "react-dom": "^18.0.2", "react-frame-component": "^5.2.7", diff --git a/canon-docs/yarn.lock b/canon-docs/yarn.lock index b51042674f..8a0dd8d2f0 100644 --- a/canon-docs/yarn.lock +++ b/canon-docs/yarn.lock @@ -501,10 +501,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:14.2.23": - version: 14.2.23 - resolution: "@next/env@npm:14.2.23" - checksum: dad74ff7de68992a0753f728a5b926ed6eb0af8a62deece6bbb32ba254b1f7d870e256b61db8d4c40fa811695cfc5e69ba964339f3cb8b40237cf9c28dfe747f +"@next/env@npm:14.2.25": + version: 14.2.25 + resolution: "@next/env@npm:14.2.25" + checksum: c937d4e8b62c7ae9c74bd607888e791b78445e93d523d22d0838e816c3fbeb7b1b62766d110ef235229e4c4ca8587f493e6ec761eb0a2c774dabaee2b58f581b languageName: node linkType: hard @@ -534,65 +534,65 @@ __metadata: languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-darwin-arm64@npm:14.2.23" +"@next/swc-darwin-arm64@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-darwin-arm64@npm:14.2.25" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-darwin-x64@npm:14.2.23" +"@next/swc-darwin-x64@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-darwin-x64@npm:14.2.25" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-linux-arm64-gnu@npm:14.2.23" +"@next/swc-linux-arm64-gnu@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.25" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-linux-arm64-musl@npm:14.2.23" +"@next/swc-linux-arm64-musl@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.25" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-linux-x64-gnu@npm:14.2.23" +"@next/swc-linux-x64-gnu@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.25" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-linux-x64-musl@npm:14.2.23" +"@next/swc-linux-x64-musl@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-linux-x64-musl@npm:14.2.25" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-win32-arm64-msvc@npm:14.2.23" +"@next/swc-win32-arm64-msvc@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.25" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-ia32-msvc@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-win32-ia32-msvc@npm:14.2.23" +"@next/swc-win32-ia32-msvc@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.25" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:14.2.23": - version: 14.2.23 - resolution: "@next/swc-win32-x64-msvc@npm:14.2.23" +"@next/swc-win32-x64-msvc@npm:14.2.25": + version: 14.2.25 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.25" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1502,7 +1502,7 @@ __metadata: eslint-config-next: 14.2.23 lightningcss: ^1.28.2 motion: ^12.4.1 - next: 14.2.23 + next: 14.2.25 react: ^18.0.2 react-dom: ^18.0.2 react-frame-component: ^5.2.7 @@ -4176,20 +4176,20 @@ __metadata: languageName: node linkType: hard -"next@npm:14.2.23": - version: 14.2.23 - resolution: "next@npm:14.2.23" +"next@npm:14.2.25": + version: 14.2.25 + resolution: "next@npm:14.2.25" dependencies: - "@next/env": 14.2.23 - "@next/swc-darwin-arm64": 14.2.23 - "@next/swc-darwin-x64": 14.2.23 - "@next/swc-linux-arm64-gnu": 14.2.23 - "@next/swc-linux-arm64-musl": 14.2.23 - "@next/swc-linux-x64-gnu": 14.2.23 - "@next/swc-linux-x64-musl": 14.2.23 - "@next/swc-win32-arm64-msvc": 14.2.23 - "@next/swc-win32-ia32-msvc": 14.2.23 - "@next/swc-win32-x64-msvc": 14.2.23 + "@next/env": 14.2.25 + "@next/swc-darwin-arm64": 14.2.25 + "@next/swc-darwin-x64": 14.2.25 + "@next/swc-linux-arm64-gnu": 14.2.25 + "@next/swc-linux-arm64-musl": 14.2.25 + "@next/swc-linux-x64-gnu": 14.2.25 + "@next/swc-linux-x64-musl": 14.2.25 + "@next/swc-win32-arm64-msvc": 14.2.25 + "@next/swc-win32-ia32-msvc": 14.2.25 + "@next/swc-win32-x64-msvc": 14.2.25 "@swc/helpers": 0.5.5 busboy: 1.6.0 caniuse-lite: ^1.0.30001579 @@ -4230,7 +4230,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 4f9f744439fb860ba7c64ae1c61bc361bdd36e41d56760c52eabb404f4143c854b78ba9a79b7aefc322d93edbbd66d966cbdaf86772a88a8bf2e6b289987f14f + checksum: ca47d0f1d41351adaf75a37c8e8ce39dd76ebc62b054266ef84b5fc1ea41241ad5f16ab7a1ca47b6613b2c3e4c6f48b179f8cd3b0f087912ec7164c4e72981f0 languageName: node linkType: hard diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 343128bfbf..64468f7f41 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1157,13 +1157,21 @@ export class MyIncrementalEntityProvider The last step is to implement the actual `next` method that will accept the cursor, call the API, process the result and return the result. ```ts +import { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, +} from '@backstage/catalog-model'; +import { IncrementalEntityProvider } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; + export class MyIncrementalEntityProvider implements IncrementalEntityProvider { private readonly token: string; + private readonly mySource: string; - constructor(token: string) { + constructor(token: string, mySource: string) { this.token = token; + this.mySource = mySource; } getProviderName() { @@ -1181,6 +1189,7 @@ export class MyIncrementalEntityProvider cursor: Cursor = { page: 1 }, ): Promise> { const { apiClient } = context; + const location = `${this.getProviderName()}:${this.mySource}`; // call your API with the current cursor const data = await apiClient.getServices(cursor); @@ -1200,8 +1209,8 @@ export class MyIncrementalEntityProvider name: item.name, annotations: { // You need to define these, otherwise they'll fail validation - [ANNOTATION_LOCATION]: this.getProviderName(), - [ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(), + [ANNOTATION_LOCATION]: location, + [ANNOTATION_ORIGIN_LOCATION]: location, }, }, spec: { diff --git a/docs/notifications/index.md b/docs/notifications/index.md index 1156db45a7..2af8879ac0 100644 --- a/docs/notifications/index.md +++ b/docs/notifications/index.md @@ -8,7 +8,7 @@ The Backstage Notifications System provides a way for plugins and external servi These notifications are displayed in the dedicated page of the Backstage frontend UI or by frontend plugins per specific scenarios. Additionally, notifications can be sent to external channels (like email) via "processors" implemented within plugins. -Notifications can be optionally integrated with the signals (a push mechanism) to ensure users receive them immediately. +Notifications can be optionally extended with the signals plugin, which provides a push mechanism to ensure users receive notifications immediately. ### Upgrade to the latest version of Backstage @@ -27,8 +27,8 @@ There are two basic types of notifications: Example of use-cases: - System-wide announcements or alerts -- Notifications for component owners: e.g., build failures, successful deployments, new vulnerabilities -- Notifications for individuals: e.g., updates you have subscribed to, new required training courses +- Notifications for component owners, e.g. build failures, successful deployments, new vulnerabilities +- Notifications for individuals, e.g. updates you have subscribed to, new required training courses - Notifications pertaining to a particular entity in the catalog: A notification might apply to an entity and the owning team. ## Installation @@ -87,17 +87,17 @@ import { NotificationsPage } from '@backstage/plugin-notifications'; ### Optional: Add Signals -The use of signals is optional but improves user experience. +The use of signals is optional but improves the user experience. #### Optional: Add Signals Backend -Add Signals to your backend by first adding the backend package: +Add signals to your backend by first adding the backend package: ```bash title="From your Backstage root directory" yarn --cwd packages/backend add @backstage/plugin-signals-backend ``` -The add the signals to your backend: +Then add the signals plugin to your backend: ```ts title="packages/backend/src/index.ts" const backend = createBackend(); @@ -156,19 +156,19 @@ add the `UserNotificationSettingsCard` to your frontend. You can customize the origin names shown in the UI by passing an object where the keys are the origins and the values are the names you want to show in the UI. -Each notification processor will receive its own column in the settings page, where the user can enable or disable notifications from that processor. +Each notification processor will receive its own row in the settings page, where the user can enable or disable notifications from that processor. ## Additional info -An example of a backend plugin sending notifications can be found in https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend-module-notifications. +An example of a backend plugin sending notifications can be found in the [`@backstage/plugin-scaffolder-backend-module-notifications` package](https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend-module-notifications). -Sources of the notifications and signal plugins: +Sources of the notifications and signals plugins: -- https://github.com/backstage/backstage/blob/master/plugins/notifications -- https://github.com/backstage/backstage/blob/master/plugins/notifications-backend -- https://github.com/backstage/backstage/blob/master/plugins/notifications-common -- https://github.com/backstage/backstage/blob/master/plugins/notifications-node -- https://github.com/backstage/backstage/blob/master/plugins/signals-backend -- https://github.com/backstage/backstage/blob/master/plugins/signals -- https://github.com/backstage/backstage/blob/master/plugins/signals-node -- https://github.com/backstage/backstage/blob/master/plugins/signals-react +- [notifications](https://github.com/backstage/backstage/blob/master/plugins/notifications) +- [notifications-backend](https://github.com/backstage/backstage/blob/master/plugins/notifications-backend) +- [notifications-common](https://github.com/backstage/backstage/blob/master/plugins/notifications-common) +- [notifications-node](https://github.com/backstage/backstage/blob/master/plugins/notifications-node) +- [signals-backend](https://github.com/backstage/backstage/blob/master/plugins/signals-backend) +- [signals](https://github.com/backstage/backstage/blob/master/plugins/signals) +- [signals-node](https://github.com/backstage/backstage/blob/master/plugins/signals-node) +- [signals-react](https://github.com/backstage/backstage/blob/master/plugins/signals-react) diff --git a/docs/notifications/usage.md b/docs/notifications/usage.md index 59fbd83e3b..358426dc21 100644 --- a/docs/notifications/usage.md +++ b/docs/notifications/usage.md @@ -6,7 +6,7 @@ description: How to use the notifications and signals ## Notifications Backend -The Notifications backend plugin provides an API to create notifications, list notifications per logged-in user, and search based on parameters. +The notifications backend plugin provides an API to create notifications, list notifications per logged-in user, and search based on parameters. The plugin uses a relational [database](https://backstage.io/docs/getting-started/config/database) for persistence; no specifics are introduced in this context. @@ -26,7 +26,7 @@ In the `packages/app/src/components/Root/Root.tsx`, tweak the [properties](https ## Usage -New notifications can be sent either by a backend plugin or an external service through the REST API. +New notifications can be sent either by a backend plugin or by an external service through the REST API. ## Backend @@ -71,7 +71,7 @@ await notificationService.send({ }); ``` -Refer the [API documentation](https://github.com/backstage/backstage/blob/master/plugins/notifications-node/report.api.md) for further details. +Consult the [API documentation](https://github.com/backstage/backstage/blob/master/plugins/notifications-node/report.api.md) for further details. ### External Services diff --git a/docs/plugins/observability.md b/docs/plugins/observability.md index 3bc625e7c6..c294001606 100644 --- a/docs/plugins/observability.md +++ b/docs/plugins/observability.md @@ -21,7 +21,7 @@ The backend supplies a central logging service, [`rootLogger`](../backend-system/core-services/root-logger.md), as well as a plugin based logger, [`logger`](../backend-system/core-services/logger.md) from `coreServices`. To add additional granularity to your logs, you can create children from the plugin -based logger, using the `.child()` method and provide is with JSON data. For example, +based logger, using the `.child()` method and provide it with JSON data. For example, if you wanted to log items for a specific span in your plugin, you could do ```ts diff --git a/docs/releases/v1.38.0-next.0-changelog.md b/docs/releases/v1.38.0-next.0-changelog.md new file mode 100644 index 0000000000..9adf106e6f --- /dev/null +++ b/docs/releases/v1.38.0-next.0-changelog.md @@ -0,0 +1,1156 @@ +# Release v1.38.0-next.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.38.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.38.0-next.0) + +## @backstage/backend-defaults@0.9.0-next.0 + +### Minor Changes + +- 01edf6e: Allow pass through of redis client and cluster options to Cache core service + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@1.2.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.9 + +## @backstage/cli@0.32.0-next.0 + +### Minor Changes + +- c7254ae: Internal update to move the `clean`, `pre/postpack` and `fix` commands into their own separate module. + +### Patch Changes + +- 4ea76f7: Bump @module-federation/enhanced ^0.9.0 to fix GHSA-593f-38f6-jp5m +- 87a5cb4: Fixed an issue causing the `repo lint` command to fail when the `--max-warnings` option was used. +- d83f3f4: Resolved a problem where the `start` command did not correctly handle multiple `--require` flags, ensuring all specified modules are now properly loaded. +- Updated dependencies + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/integration@1.16.2 + - @backstage/release-manifests@0.0.12 + - @backstage/types@1.2.1 + +## @backstage/plugin-catalog@1.29.0-next.0 + +### Minor Changes + +- 9454ef9: Added support of filtering based on system columns in catalog table + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration-react@1.2.5 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-scaffolder-common@1.5.10 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + +## @backstage/plugin-scaffolder@1.30.0-next.0 + +### Minor Changes + +- 5890016: add api to retrieve template extensions info from scaffolder-backend + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration@1.16.2 + - @backstage/integration-react@1.2.5 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-scaffolder-common@1.5.10 + +## @backstage/plugin-scaffolder-backend@1.32.0-next.0 + +### Minor Changes + +- 75e4db4: add template-extensions scaffolder service endpoint + +### Patch Changes + +- 497d47a: Document the internal built-in filters, and ensure that the types are validated when using `createTemplateFilter` and `createTemplateGlobalFunction` from the `zod` schema. +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.9-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.6.2-next.0 + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.8-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/types@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.28 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.10 + +## @backstage/plugin-scaffolder-react@1.15.0-next.0 + +### Minor Changes + +- 5890016: add api to retrieve template extensions info from scaffolder-backend + +### Patch Changes + +- 6ed42b7: Scaffolding - Template card - button to show template entity detail +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-scaffolder-common@1.5.10 + +## @backstage/backend-dynamic-feature-service@0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/plugin-events-backend@0.5.0 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.31 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/backend-test-utils@1.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/backend-app-api@1.2.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.9 + +## @backstage/canon@0.2.1-next.0 + +### Patch Changes + +- 6af7b16: Updated styles for the Menu component in Canon. +- 513477f: Add global CSS reset for anchor tags. +- 05a5003: Fix the Icon component when the name is not found to return null instead of an empty SVG. + +## @backstage/core-compat-api@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/version-bridge@1.0.11 + +## @backstage/create-app@0.6.1-next.0 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## @backstage/dev-utils@1.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + +## @techdocs/cli@1.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## @backstage/plugin-api-docs@0.12.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.32 + +## @backstage/plugin-auth-backend@0.24.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend-module-oidc-provider@0.4.2-next.0 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.4.1 + - @backstage/plugin-auth-backend-module-auth0-provider@0.2.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.1 + - @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.3.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.3.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.4.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.2.1 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.3.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.2-next.0 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.6 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.1 + - @backstage/plugin-auth-backend-module-google-provider@0.3.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.6 + - @backstage/plugin-catalog-node@1.16.1 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.2-next.0 + +### Patch Changes + +- 7495edf: Added custom timeout setting for oidc provider + + Here is an example of how to use a custom timeout with the configuration: + + ```yaml + auth: + oidc: + production: + clientId: ${AUTH_GOOGLE_CLIENT_ID} + clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET} + timeout: + seconds: 30 + ``` + +- Updated dependencies + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-catalog-backend-module-aws@0.4.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-kubernetes-common@0.9.4 + +## @backstage/plugin-catalog-backend-module-github@0.7.12-next.0 + +### Patch Changes + +- 16648ef: Added `validateLocationsExist` to the config definition where it was missing. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.7.12-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + +## @backstage/plugin-catalog-backend-module-gitlab@0.6.5-next.0 + +### Patch Changes + +- a568cda: add filter for repos by membership and topics +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.6.5-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.6.5-next.0 + +### Patch Changes + +- 27d1031: fixed misleading example location annotations in docs +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-catalog-graph@0.4.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/types@1.2.1 + +## @backstage/plugin-catalog-import@0.12.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration@1.16.2 + - @backstage/integration-react@1.2.5 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-react@1.16.1-next.0 + +### Patch Changes + +- 186d016: Add `operation` to alpha `defaultEntityContentGroups`. +- 221ddba: Fix offset pagination to reset when updating filters in `useEntityList` +- Updated dependencies + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/frontend-test-utils@0.3.0 + - @backstage/integration-react@1.2.5 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.32 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.16-next.0 + +### Patch Changes + +- ba88bfa: Added confirmation popup for delete entities in Catalog Unprocessed Entities plugin +- 2479827: Fixed the `convertTimeToLocalTimezone` function in the FailedEntities component to correctly parse ISO 8601 date strings and set the timezone to the current local timezone. +- Updated dependencies + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + +## @backstage/plugin-devtools@0.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-react@0.4.32 + +## @backstage/plugin-devtools-backend@0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-home@0.8.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-home-react@0.1.25-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/theme@0.6.4 + +## @backstage/plugin-home-react@0.1.25-next.0 + +### Patch Changes + +- 2e4cb15: Fixes auto-hiding of content divider when title not specified +- Updated dependencies + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + +## @backstage/plugin-kubernetes@0.12.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/plugin-kubernetes-common@0.9.4 + - @backstage/plugin-kubernetes-react@0.5.5 + - @backstage/plugin-permission-react@0.4.32 + +## @backstage/plugin-kubernetes-cluster@0.0.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/plugin-kubernetes-common@0.9.4 + - @backstage/plugin-kubernetes-react@0.5.5 + - @backstage/plugin-permission-react@0.4.32 + +## @backstage/plugin-notifications@0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-react@0.0.11 + +## @backstage/plugin-org@0.6.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-org-react@0.1.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.8-next.0 + +### Patch Changes + +- 2bd41ce: Made "publish:azure" action idempotent +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.9-next.0 + +### Patch Changes + +- adfceee: Made "publish:bitbucket" action idempotent +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.8-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.8-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.8-next.0 + +### Patch Changes + +- 5266f1c: Made "publish:bitbucketCloud" action idempotent +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/plugin-bitbucket-cloud-common@0.2.28 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-github@0.6.2-next.0 + +### Patch Changes + +- 1af427a: Made "github:autolinks:create" action idempotent +- 180ea6e: Made "github:branch-protection:create" action idempotent +- 0be1a1e: Made "publish:github" action idempotent +- a833f0f: Made "github:actions:dispatch" action idempotent +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.8.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.9-next.0 + +### Patch Changes + +- 25a7675: Made "notification:send" action idempotent +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.8-next.0 + +### Patch Changes + +- dcda66a: Made "sentry:project:create" action idempotent +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-node-test-utils@0.2.1-next.0 + +## @backstage/plugin-scaffolder-node@0.8.1-next.0 + +### Patch Changes + +- 497d47a: Document the internal built-in filters, and ensure that the types are validated when using `createTemplateFilter` and `createTemplateGlobalFunction` from the `zod` schema. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.10 + +## @backstage/plugin-scaffolder-node-test-utils@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-test-utils@1.3.2-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/types@1.2.1 + +## @backstage/plugin-search@1.4.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + +## @backstage/plugin-search-backend@2.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/backend-openapi-utils@0.5.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-techdocs@1.12.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration@1.16.2 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.15 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.47-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/core-app-api@1.16.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration-react@1.2.5 + - @backstage/test-utils@1.7.6 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-techdocs-react@1.2.15 + +## @backstage/plugin-techdocs-backend@2.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-node@1.13.2-next.0 + +### Patch Changes + +- 304a422: Update AWS file retrieval logic from storing file in buffer array to piping to res for better memory efficiency. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-user-settings@0.8.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.11 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.18 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.108-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/cli@0.32.0-next.0 + - @backstage/canon@0.2.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.16-next.0 + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-scaffolder@1.30.0-next.0 + - @backstage/plugin-api-docs@0.12.6-next.0 + - @backstage/plugin-catalog-graph@0.4.18-next.0 + - @backstage/plugin-catalog-import@0.12.12-next.0 + - @backstage/plugin-org@0.6.38-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/plugin-user-settings@0.8.21-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-app-api@0.11.0 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-devtools@0.1.26-next.0 + - @backstage/plugin-home@0.8.7-next.0 + - @backstage/plugin-kubernetes@0.12.6-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.24-next.0 + - @backstage/plugin-notifications@0.5.4-next.0 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-search@1.4.25-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-signals@0.0.17 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22 + - @backstage/plugin-techdocs-react@1.2.15 + +## example-app-next@0.0.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/cli@0.32.0-next.0 + - @backstage/canon@0.2.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.16-next.0 + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-scaffolder@1.30.0-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/plugin-api-docs@0.12.6-next.0 + - @backstage/plugin-catalog-graph@0.4.18-next.0 + - @backstage/plugin-catalog-import@0.12.12-next.0 + - @backstage/plugin-org@0.6.38-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/plugin-user-settings@0.8.21-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-app-api@0.11.0 + - @backstage/frontend-defaults@0.2.0 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-app@0.1.7 + - @backstage/plugin-app-visualizer@0.1.17 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-home@0.8.7-next.0 + - @backstage/plugin-kubernetes@0.12.6-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.24-next.0 + - @backstage/plugin-notifications@0.5.4-next.0 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-search@1.4.25-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-signals@0.0.17 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22 + - @backstage/plugin-techdocs-react@1.2.15 + +## example-backend@0.0.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.6.2-next.0 + - @backstage/plugin-scaffolder-backend@1.32.0-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.9-next.0 + - @backstage/plugin-app-backend@0.5.0 + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/plugin-devtools-backend@0.5.4-next.0 + - @backstage/plugin-events-backend@0.5.0 + - @backstage/plugin-kubernetes-backend@0.19.4 + - @backstage/plugin-notifications-backend@0.5.4 + - @backstage/plugin-permission-backend@0.5.55 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-proxy-backend@0.6.0 + - @backstage/plugin-search-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/plugin-signals-backend@0.3.2 + - @backstage/plugin-techdocs-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.6 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.8 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.6 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-backend-module-catalog@0.3.2 + - @backstage/plugin-search-backend-module-explore@0.3.0 + +## example-backend-legacy@0.2.109-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend@1.32.0-next.0 + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/plugin-events-backend@0.5.0 + - @backstage/plugin-kubernetes-backend@0.19.4 + - @backstage/plugin-permission-backend@0.5.55 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-search-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/plugin-signals-backend@0.3.2 + - @backstage/plugin-techdocs-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.2-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.8-next.0 + - @backstage/plugin-search-backend-module-catalog@0.3.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.7.0 + - @backstage/plugin-search-backend-module-explore@0.3.0 + - @backstage/plugin-search-backend-module-pg@0.5.42 + - @backstage/plugin-signals-node@0.1.18 + +## e2e-test@0.2.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.6.1-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @internal/scaffolder@0.0.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/frontend-plugin-api@0.10.0 + +## techdocs-cli-embedded-app@0.2.107-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/cli@0.32.0-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration-react@1.2.5 + - @backstage/test-utils@1.7.6 + - @backstage/theme@0.6.4 + - @backstage/plugin-techdocs-react@1.2.15 diff --git a/microsite/blog/2023-07-08-stash-adopter-post.mdx b/microsite/blog/2023-07-08-stash-adopter-post.mdx index dbf85f9e25..6c1edea764 100644 --- a/microsite/blog/2023-07-08-stash-adopter-post.mdx +++ b/microsite/blog/2023-07-08-stash-adopter-post.mdx @@ -10,7 +10,7 @@ author: Taylor Webber, Staff Engineer, Stash {/* truncate */} -At [Stash](stash.com), our mission is to give people the confidence to invest for a better life. Our app simplifies investing for the middle class who may be less experienced with traditional investing and retirement planning. Our customers trust us with their money, and with that, rely on us to provide a responsive and quality experience that works every time. +At [Stash](https://www.stash.com), our mission is to give people the confidence to invest for a better life. Our app simplifies investing for the middle class who may be less experienced with traditional investing and retirement planning. Our customers trust us with their money, and with that, rely on us to provide a responsive and quality experience that works every time. It isn't revolutionary to say that monitoring the health of a software application is critical for ensuring its reliability and performance in service of customers' needs. However, in a distributed architecture like microservices, tracking ownership and monitoring application health across the system quickly becomes complex. The difficulties are further compounded when leveraging multiple SaaS products, which is common in modern development but specifically so for fintechs like Stash that integrate best-of-breed software in novel ways, as we do across our platform. diff --git a/package.json b/package.json index 083c70e483..aed62a8c2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.37.0", + "version": "1.38.0-next.0", "backstage": { "cli": { "new": { diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index d7bdd7fa17..acf3fa9f56 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,51 @@ # example-app-next +## 0.0.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/cli@0.32.0-next.0 + - @backstage/canon@0.2.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.16-next.0 + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-scaffolder@1.30.0-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/plugin-api-docs@0.12.6-next.0 + - @backstage/plugin-catalog-graph@0.4.18-next.0 + - @backstage/plugin-catalog-import@0.12.12-next.0 + - @backstage/plugin-org@0.6.38-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/plugin-user-settings@0.8.21-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-app-api@0.11.0 + - @backstage/frontend-defaults@0.2.0 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-app@0.1.7 + - @backstage/plugin-app-visualizer@0.1.17 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-home@0.8.7-next.0 + - @backstage/plugin-kubernetes@0.12.6-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.24-next.0 + - @backstage/plugin-notifications@0.5.4-next.0 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-search@1.4.25-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-signals@0.0.17 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22 + - @backstage/plugin-techdocs-react@1.2.15 + ## 0.0.21 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index fd78b59760..84067d4988 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.21", + "version": "0.0.22-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index a7fbce0cf7..99c3eb234d 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,47 @@ # example-app +## 0.2.108-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/cli@0.32.0-next.0 + - @backstage/canon@0.2.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.16-next.0 + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-scaffolder@1.30.0-next.0 + - @backstage/plugin-api-docs@0.12.6-next.0 + - @backstage/plugin-catalog-graph@0.4.18-next.0 + - @backstage/plugin-catalog-import@0.12.12-next.0 + - @backstage/plugin-org@0.6.38-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/plugin-user-settings@0.8.21-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-app-api@0.11.0 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-devtools@0.1.26-next.0 + - @backstage/plugin-home@0.8.7-next.0 + - @backstage/plugin-kubernetes@0.12.6-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.24-next.0 + - @backstage/plugin-notifications@0.5.4-next.0 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-search@1.4.25-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-signals@0.0.17 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.22 + - @backstage/plugin-techdocs-react@1.2.15 + ## 0.2.107 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index ce8f4b8482..357de42061 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.107", + "version": "0.2.108-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 81b70c541e..72607fd320 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-defaults +## 0.9.0-next.0 + +### Minor Changes + +- 01edf6e: Allow pass through of redis client and cluster options to Cache core service + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@1.2.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.9 + ## 0.8.2 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 1318d57d42..5d2ae323c6 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.8.2", + "version": "0.9.0-next.0", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index 59e4342757..39a1fb1795 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/backend-dynamic-feature-service +## 0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/plugin-events-backend@0.5.0 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.31 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 0.6.1 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 33cc595286..2b97805a2e 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.6.1", + "version": "0.6.2-next.0", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-legacy/CHANGELOG.md b/packages/backend-legacy/CHANGELOG.md index 5dc4eced46..1ca1f34ebb 100644 --- a/packages/backend-legacy/CHANGELOG.md +++ b/packages/backend-legacy/CHANGELOG.md @@ -1,5 +1,43 @@ # example-backend-legacy +## 0.2.109-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend@1.32.0-next.0 + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/plugin-events-backend@0.5.0 + - @backstage/plugin-kubernetes-backend@0.19.4 + - @backstage/plugin-permission-backend@0.5.55 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-search-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/plugin-signals-backend@0.3.2 + - @backstage/plugin-techdocs-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.8-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.2-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.8-next.0 + - @backstage/plugin-search-backend-module-catalog@0.3.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.7.0 + - @backstage/plugin-search-backend-module-explore@0.3.0 + - @backstage/plugin-search-backend-module-pg@0.5.42 + - @backstage/plugin-signals-node@0.1.18 + ## 0.2.108 ### Patch Changes diff --git a/packages/backend-legacy/package.json b/packages/backend-legacy/package.json index 90430aa4a8..e4a415c93d 100644 --- a/packages/backend-legacy/package.json +++ b/packages/backend-legacy/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-legacy", - "version": "0.2.108", + "version": "0.2.109-next.0", "backstage": { "role": "backend" }, diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 793a0ecb4d..3804c1afb8 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/backend-test-utils +## 1.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/backend-app-api@1.2.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.9 + ## 1.3.1 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 17f5d8fad1..54ce0ef6d2 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "1.3.1", + "version": "1.3.2-next.0", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index d60080901c..dd57069564 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,43 @@ # example-backend +## 0.0.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.6.2-next.0 + - @backstage/plugin-scaffolder-backend@1.32.0-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.9-next.0 + - @backstage/plugin-app-backend@0.5.0 + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/plugin-devtools-backend@0.5.4-next.0 + - @backstage/plugin-events-backend@0.5.0 + - @backstage/plugin-kubernetes-backend@0.19.4 + - @backstage/plugin-notifications-backend@0.5.4 + - @backstage/plugin-permission-backend@0.5.55 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-proxy-backend@0.6.0 + - @backstage/plugin-search-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/plugin-signals-backend@0.3.2 + - @backstage/plugin-techdocs-backend@2.0.1-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.6 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.8 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.6 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.6 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.6 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-backend-module-catalog@0.3.2 + - @backstage/plugin-search-backend-module-explore@0.3.0 + ## 0.0.36 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 47ffbf0ece..454bfc2066 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.36", + "version": "0.0.37-next.0", "backstage": { "role": "backend" }, diff --git a/packages/canon/CHANGELOG.md b/packages/canon/CHANGELOG.md index 87a8dd4265..8281606041 100644 --- a/packages/canon/CHANGELOG.md +++ b/packages/canon/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/canon +## 0.2.1-next.0 + +### Patch Changes + +- 6af7b16: Updated styles for the Menu component in Canon. +- 513477f: Add global CSS reset for anchor tags. +- 05a5003: Fix the Icon component when the name is not found to return null instead of an empty SVG. + ## 0.2.0 ### Minor Changes diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index cb376e64ed..1713468367 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -481,101 +481,50 @@ margin: 0; } -.canon-MenuTrigger { - box-sizing: border-box; - border: 1px solid var(--color-gray-200); - background-color: var(--color-gray-50); - height: 2.5rem; - color: var(--color-gray-900); - user-select: none; - border-radius: .375rem; - outline: 0; - justify-content: center; - align-items: center; - gap: .375rem; - margin: 0; - padding: 0 .875rem; - font-family: inherit; - font-size: 1rem; - font-weight: 500; - line-height: 1.5rem; - display: flex; - - @media (hover: hover) { - &:hover { - background-color: var(--color-gray-100); - } - } - - &:active, &[data-popup-open] { - background-color: var(--color-gray-100); - } - - &:focus-visible { - outline: 2px solid var(--color-blue); - outline-offset: -1px; - } -} - .canon-MenuPositioner { outline: 0; } .canon-MenuPopup { - box-sizing: border-box; - background-color: var(--canon-bg-elevated); - color: var(--color-fg-primary); + padding: var(--canon-space-2); + background-color: var(--canon-bg-surface-1); + border: 1px solid var(--canon-border); + color: var(--canon-fg-primary); transform-origin: var(--transform-origin); border-radius: .375rem; - padding-block: .25rem; + outline: none; + max-width: 340px; transition: transform .15s, opacity .15s; + overflow: hidden; &[data-starting-style], &[data-ending-style] { opacity: 0; transform: scale(.9); } - - @media (prefers-color-scheme: light) { - & { - outline: 1px solid var(--color-gray-200); - box-shadow: 0px 10px 15px -3px var(--color-gray-200), 0px 4px 6px -4px var(--color-gray-200); - } - } - - @media (prefers-color-scheme: dark) { - & { - outline: 1px solid var(--color-gray-300); - outline-offset: -1px; - } - } } .canon-MenuItem { cursor: default; user-select: none; - padding-block: .5rem; + gap: var(--canon-space-2); + color: var(--canon-fg-primary); + border-radius: var(--canon-radius-2); + padding: var(--canon-space-2) var(--canon-space-2); + padding-right: var(--canon-space-4); + cursor: pointer; outline: 0; - padding-left: 1rem; - padding-right: 2rem; - font-size: .875rem; - line-height: 1rem; + text-decoration: none; display: flex; + &:last-child { + border-bottom: none; + } + &[data-highlighted] { z-index: 0; - color: var(--color-gray-50); + background-color: var(--canon-bg-tint-hover); position: relative; } - - &[data-highlighted]:before { - content: ""; - z-index: -1; - background-color: var(--color-gray-900); - border-radius: .25rem; - position: absolute; - inset-block: 0; - inset-inline: .25rem; - } } .canon-MenuSeparator { diff --git a/packages/canon/css/core.css b/packages/canon/css/core.css index 34e447147e..b4583ea35d 100644 --- a/packages/canon/css/core.css +++ b/packages/canon/css/core.css @@ -9083,6 +9083,11 @@ } } +:where(a) { + color: inherit; + text-decoration: none; +} + :root { --canon-font-regular: system-ui; --canon-font-monospace: ui-monospace, "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace; diff --git a/packages/canon/css/menu.css b/packages/canon/css/menu.css index 21192076b4..de811cde77 100644 --- a/packages/canon/css/menu.css +++ b/packages/canon/css/menu.css @@ -1,98 +1,47 @@ -.canon-MenuTrigger { - box-sizing: border-box; - border: 1px solid var(--color-gray-200); - background-color: var(--color-gray-50); - height: 2.5rem; - color: var(--color-gray-900); - user-select: none; - border-radius: .375rem; - outline: 0; - justify-content: center; - align-items: center; - gap: .375rem; - margin: 0; - padding: 0 .875rem; - font-family: inherit; - font-size: 1rem; - font-weight: 500; - line-height: 1.5rem; - display: flex; - - @media (hover: hover) { - &:hover { - background-color: var(--color-gray-100); - } - } - - &:active, &[data-popup-open] { - background-color: var(--color-gray-100); - } - - &:focus-visible { - outline: 2px solid var(--color-blue); - outline-offset: -1px; - } -} - .canon-MenuPositioner { outline: 0; } .canon-MenuPopup { - box-sizing: border-box; - background-color: var(--canon-bg-elevated); - color: var(--color-fg-primary); + padding: var(--canon-space-2); + background-color: var(--canon-bg-surface-1); + border: 1px solid var(--canon-border); + color: var(--canon-fg-primary); transform-origin: var(--transform-origin); border-radius: .375rem; - padding-block: .25rem; + outline: none; + max-width: 340px; transition: transform .15s, opacity .15s; + overflow: hidden; &[data-starting-style], &[data-ending-style] { opacity: 0; transform: scale(.9); } - - @media (prefers-color-scheme: light) { - & { - outline: 1px solid var(--color-gray-200); - box-shadow: 0px 10px 15px -3px var(--color-gray-200), 0px 4px 6px -4px var(--color-gray-200); - } - } - - @media (prefers-color-scheme: dark) { - & { - outline: 1px solid var(--color-gray-300); - outline-offset: -1px; - } - } } .canon-MenuItem { cursor: default; user-select: none; - padding-block: .5rem; + gap: var(--canon-space-2); + color: var(--canon-fg-primary); + border-radius: var(--canon-radius-2); + padding: var(--canon-space-2) var(--canon-space-2); + padding-right: var(--canon-space-4); + cursor: pointer; outline: 0; - padding-left: 1rem; - padding-right: 2rem; - font-size: .875rem; - line-height: 1rem; + text-decoration: none; display: flex; + &:last-child { + border-bottom: none; + } + &[data-highlighted] { z-index: 0; - color: var(--color-gray-50); + background-color: var(--canon-bg-tint-hover); position: relative; } - - &[data-highlighted]:before { - content: ""; - z-index: -1; - background-color: var(--color-gray-900); - border-radius: .25rem; - position: absolute; - inset-block: 0; - inset-inline: .25rem; - } } .canon-MenuSeparator { diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index 3b312c220e..7289562f9d 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9083,6 +9083,11 @@ } } +:where(a) { + color: inherit; + text-decoration: none; +} + :root { --canon-font-regular: system-ui; --canon-font-monospace: ui-monospace, "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace; @@ -9682,101 +9687,50 @@ margin: 0; } -.canon-MenuTrigger { - box-sizing: border-box; - border: 1px solid var(--color-gray-200); - background-color: var(--color-gray-50); - height: 2.5rem; - color: var(--color-gray-900); - user-select: none; - border-radius: .375rem; - outline: 0; - justify-content: center; - align-items: center; - gap: .375rem; - margin: 0; - padding: 0 .875rem; - font-family: inherit; - font-size: 1rem; - font-weight: 500; - line-height: 1.5rem; - display: flex; - - @media (hover: hover) { - &:hover { - background-color: var(--color-gray-100); - } - } - - &:active, &[data-popup-open] { - background-color: var(--color-gray-100); - } - - &:focus-visible { - outline: 2px solid var(--color-blue); - outline-offset: -1px; - } -} - .canon-MenuPositioner { outline: 0; } .canon-MenuPopup { - box-sizing: border-box; - background-color: var(--canon-bg-elevated); - color: var(--color-fg-primary); + padding: var(--canon-space-2); + background-color: var(--canon-bg-surface-1); + border: 1px solid var(--canon-border); + color: var(--canon-fg-primary); transform-origin: var(--transform-origin); border-radius: .375rem; - padding-block: .25rem; + outline: none; + max-width: 340px; transition: transform .15s, opacity .15s; + overflow: hidden; &[data-starting-style], &[data-ending-style] { opacity: 0; transform: scale(.9); } - - @media (prefers-color-scheme: light) { - & { - outline: 1px solid var(--color-gray-200); - box-shadow: 0px 10px 15px -3px var(--color-gray-200), 0px 4px 6px -4px var(--color-gray-200); - } - } - - @media (prefers-color-scheme: dark) { - & { - outline: 1px solid var(--color-gray-300); - outline-offset: -1px; - } - } } .canon-MenuItem { cursor: default; user-select: none; - padding-block: .5rem; + gap: var(--canon-space-2); + color: var(--canon-fg-primary); + border-radius: var(--canon-radius-2); + padding: var(--canon-space-2) var(--canon-space-2); + padding-right: var(--canon-space-4); + cursor: pointer; outline: 0; - padding-left: 1rem; - padding-right: 2rem; - font-size: .875rem; - line-height: 1rem; + text-decoration: none; display: flex; + &:last-child { + border-bottom: none; + } + &[data-highlighted] { z-index: 0; - color: var(--color-gray-50); + background-color: var(--canon-bg-tint-hover); position: relative; } - - &[data-highlighted]:before { - content: ""; - z-index: -1; - background-color: var(--color-gray-900); - border-radius: .25rem; - position: absolute; - inset-block: 0; - inset-inline: .25rem; - } } .canon-MenuSeparator { diff --git a/packages/canon/package.json b/packages/canon/package.json index 35b55637fe..b0aa479f25 100644 --- a/packages/canon/package.json +++ b/packages/canon/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/canon", - "version": "0.2.0", + "version": "0.2.1-next.0", "backstage": { "role": "web-library" }, @@ -36,7 +36,7 @@ "lint": "backstage-cli package lint", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "start": "yarn build:css:watch && yarn storybook", + "start": "concurrently \"yarn build:css:watch\" \"yarn storybook\"", "storybook": "storybook dev -p 6006", "test": "backstage-cli package test" }, @@ -60,7 +60,7 @@ "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "chalk": "^5.4.1", - "eslint-plugin-storybook": "^0.11.6", + "eslint-plugin-storybook": "^0.11.4", "glob": "^11.0.1", "globals": "^15.11.0", "lightningcss": "^1.29.1", diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx index 4b1d06aebd..d568bcaf7f 100644 --- a/packages/canon/src/components/Heading/Heading.stories.tsx +++ b/packages/canon/src/components/Heading/Heading.stories.tsx @@ -67,6 +67,19 @@ export const CustomTag: Story = { }, }; +export const WrappedInLink: Story = { + args: { + ...Default.args, + }, + decorators: [ + Story => ( + + + + ), + ], +}; + export const Playground: Story = { render: () => ( diff --git a/packages/canon/src/components/Menu/Menu.stories.tsx b/packages/canon/src/components/Menu/Menu.stories.tsx index c971483b16..58284052af 100644 --- a/packages/canon/src/components/Menu/Menu.stories.tsx +++ b/packages/canon/src/components/Menu/Menu.stories.tsx @@ -17,6 +17,7 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { Menu } from './Menu'; +import { Button } from '../Button'; const meta = { title: 'Components/Menu', @@ -30,9 +31,15 @@ export const Default: Story = { args: { children: ( <> - Menu + ( + + )} + /> - + Item 1 Item 2 @@ -44,3 +51,10 @@ export const Default: Story = { ), }, }; + +export const Open: Story = { + args: { + ...Default.args, + open: true, + }, +}; diff --git a/packages/canon/src/components/Menu/Menu.styles.css b/packages/canon/src/components/Menu/Menu.styles.css index 4f76e5552f..403d34294e 100644 --- a/packages/canon/src/components/Menu/Menu.styles.css +++ b/packages/canon/src/components/Menu/Menu.styles.css @@ -1,54 +1,17 @@ -.canon-MenuTrigger { - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; - gap: 0.375rem; - height: 2.5rem; - padding: 0 0.875rem; - margin: 0; - outline: 0; - border: 1px solid var(--color-gray-200); - border-radius: 0.375rem; - background-color: var(--color-gray-50); - font-family: inherit; - font-size: 1rem; - font-weight: 500; - line-height: 1.5rem; - color: var(--color-gray-900); - user-select: none; - - @media (hover: hover) { - &:hover { - background-color: var(--color-gray-100); - } - } - - &:active { - background-color: var(--color-gray-100); - } - - &[data-popup-open] { - background-color: var(--color-gray-100); - } - - &:focus-visible { - outline: 2px solid var(--color-blue); - outline-offset: -1px; - } -} - .canon-MenuPositioner { outline: 0; } .canon-MenuPopup { - box-sizing: border-box; - padding-block: 0.25rem; + padding: var(--canon-space-2); border-radius: 0.375rem; - background-color: var(--canon-bg-elevated); - color: var(--color-fg-primary); + background-color: var(--canon-bg-surface-1); + border: 1px solid var(--canon-border); + color: var(--canon-fg-primary); + outline: none; + overflow: hidden; transform-origin: var(--transform-origin); + max-width: 340px; transition: transform 150ms, opacity 150ms; &[data-starting-style], @@ -56,44 +19,29 @@ opacity: 0; transform: scale(0.9); } - - @media (prefers-color-scheme: light) { - outline: 1px solid var(--color-gray-200); - box-shadow: 0px 10px 15px -3px var(--color-gray-200), - 0px 4px 6px -4px var(--color-gray-200); - } - - @media (prefers-color-scheme: dark) { - outline: 1px solid var(--color-gray-300); - outline-offset: -1px; - } } .canon-MenuItem { outline: 0; cursor: default; user-select: none; - padding-block: 0.5rem; - padding-left: 1rem; - padding-right: 2rem; display: flex; - font-size: 0.875rem; - line-height: 1rem; + gap: var(--canon-space-2); + color: var(--canon-fg-primary); + text-decoration: none; + border-radius: var(--canon-radius-2); + padding: var(--canon-space-2) var(--canon-space-2); + padding-right: var(--canon-space-4); + cursor: pointer; + + &:last-child { + border-bottom: none; + } &[data-highlighted] { z-index: 0; position: relative; - color: var(--color-gray-50); - } - - &[data-highlighted]::before { - content: ''; - z-index: -1; - position: absolute; - inset-block: 0; - inset-inline: 0.25rem; - border-radius: 0.25rem; - background-color: var(--color-gray-900); + background-color: var(--canon-bg-tint-hover); } } diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index 9cf2a73f6e..ab904f524c 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -89,6 +89,19 @@ export const Responsive: Story = { }, }; +export const WrappedInLink: Story = { + args: { + ...Default.args, + }, + decorators: [ + Story => ( + + + + ), + ], +}; + export const Playground: Story = { render: () => ( diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index 8efb066b70..d0686e94f7 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -18,8 +18,13 @@ @import './normalize.css'; @import './utilities.css'; -/* Light theme tokens */ +/* Global reset */ +:where(a) { + color: inherit; + text-decoration: none; +} +/* Light theme tokens */ :root { /* Font families */ --canon-font-regular: system-ui; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 0a4c7f2e77..6fb3d27120 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/cli +## 0.32.0-next.0 + +### Minor Changes + +- c7254ae: Internal update to move the `clean`, `pre/postpack` and `fix` commands into their own separate module. + +### Patch Changes + +- 4ea76f7: Bump @module-federation/enhanced ^0.9.0 to fix GHSA-593f-38f6-jp5m +- 87a5cb4: Fixed an issue causing the `repo lint` command to fail when the `--max-warnings` option was used. +- d83f3f4: Resolved a problem where the `start` command did not correctly handle multiple `--require` flags, ensuring all specified modules are now properly loaded. +- Updated dependencies + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.0 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/integration@1.16.2 + - @backstage/release-manifests@0.0.12 + - @backstage/types@1.2.1 + ## 0.31.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index f5bd1a02af..aeb6eb47c3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.31.0", + "version": "0.32.0-next.0", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" @@ -58,7 +58,7 @@ "@backstage/release-manifests": "workspace:^", "@backstage/types": "workspace:^", "@manypkg/get-packages": "^1.1.3", - "@module-federation/enhanced": "^0.8.0", + "@module-federation/enhanced": "^0.9.0", "@octokit/graphql": "^5.0.0", "@octokit/graphql-schema": "^13.7.0", "@octokit/oauth-app": "^4.2.0", diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 4884e0a184..8d425843d4 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-compat-api +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/version-bridge@1.0.11 + ## 0.4.0 ### Minor Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index eb1fd98ccc..d512c66c18 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.4.0", + "version": "0.4.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 375b0bde56..bed7ee413f 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/create-app +## 0.6.1-next.0 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + ## 0.6.0 ### Minor Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 0d9fb381a3..fdf01776d8 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.6.0", + "version": "0.6.1-next.0", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 82330fd76c..21e4401525 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + ## 1.1.8 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 0bd88f0a26..f026fba9a1 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.8", + "version": "1.1.9-next.0", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 423070e648..014ea8e321 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.6.1-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.2.26 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index f76a5770d3..ae5ed05af8 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.26", + "version": "0.2.27-next.0", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index 2339472b24..b8378e4996 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/scaffolder +## 0.0.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/frontend-plugin-api@0.10.0 + ## 0.0.7 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index f57a049022..20d6a7ef72 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.7", + "version": "0.0.8-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 571d1e31a9..0057a5ddcb 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,24 @@ # techdocs-cli-embedded-app +## 0.2.107-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/cli@0.32.0-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/app-defaults@1.6.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration-react@1.2.5 + - @backstage/test-utils@1.7.6 + - @backstage/theme@0.6.4 + - @backstage/plugin-techdocs-react@1.2.15 + ## 0.2.106 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 971746e720..bcd30c4a00 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.106", + "version": "0.2.107-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 99ebf4f87f..cf0e24c487 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + ## 1.9.1 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 683c73ff3f..febbe38922 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.1", + "version": "1.9.2-next.0", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 176f63364f..a30cc08a1d 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-api-docs +## 0.12.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.32 + ## 0.12.5 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 1fcdf205b2..5df57448b2 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.12.5", + "version": "0.12.6-next.0", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index 858ba95b83..cbd6118a9c 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/errors@1.2.7 + ## 0.4.1 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index cafbc0b5c1..c56cd0e2c9 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", - "version": "0.4.1", + "version": "0.4.2-next.0", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 0441c68756..9864be61f9 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.4.2-next.0 + +### Patch Changes + +- 7495edf: Added custom timeout setting for oidc provider + + Here is an example of how to use a custom timeout with the configuration: + + ```yaml + auth: + oidc: + production: + clientId: ${AUTH_GOOGLE_CLIENT_ID} + clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET} + timeout: + seconds: 30 + ``` + +- Updated dependencies + - @backstage/plugin-auth-backend@0.24.5-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.4.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index 409c658c23..8a1df804e6 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { HumanDuration } from '@backstage/types'; export interface Config { @@ -33,6 +32,7 @@ export interface Config { tokenSignedResponseAlg?: string; additionalScopes?: string | string[]; prompt?: string; + timeout?: HumanDuration | string; signIn?: { resolvers: Array< | { diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index e163ee8d47..0e212409fa 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", - "version": "0.4.1", + "version": "0.4.2-next.0", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -35,8 +35,10 @@ }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", + "@backstage/config": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", + "@backstage/types": "workspace:^", "express": "^4.18.2", "openid-client": "^5.5.0", "passport": "^0.7.0" diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index 38e134bcd0..c327d4ecc5 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -28,6 +28,7 @@ import { ConfigReader } from '@backstage/config'; import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; import { rest } from 'msw'; import express from 'express'; +import { custom } from 'openid-client'; describe('oidcAuthenticator', () => { let implementation: any; @@ -169,6 +170,77 @@ describe('oidcAuthenticator', () => { jest.clearAllMocks(); }); + describe('timeout configuration', () => { + const TEST_URL = new URL('https://test.com'); + + it('should use default timeout when no timeout is configured', async () => { + const { promise } = oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + }), + }); + const { client } = await promise; + + const timeout = client[custom.http_options](TEST_URL, {}).timeout; + + // Check if the HTTP timeout is set to the default value + expect(timeout).toBeDefined(); + expect(timeout).toBe(10000); + }); + + it('should use configured timeout when provided in the config', async () => { + const { promise } = oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + timeout: { + seconds: 30, + }, + }), + }); + const { client } = await promise; + + const timeout = client[custom.http_options](TEST_URL, {}).timeout; + + // Check if the HTTP timeout is set to the configured value (30 seconds) + expect(timeout).toBeDefined(); + expect(timeout).toBe(30000); + }); + + it('should handle invalid timeout configuration gracefully', async () => { + expect(() => { + oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + timeout: 123, // Invalid: should be a duration object + }), + }); + }).toThrow(); + + expect(() => { + oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + timeout: { + invalid: 'value', + }, + }), + }); + }).toThrow(); + }); + }); + describe('#start', () => { let fakeSession: Record; let startRequest: OAuthAuthenticatorStartInput; diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index c396ea7034..6e7022f7cf 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -32,14 +32,18 @@ import { PassportOAuthAuthenticatorHelper, PassportOAuthPrivateInfo, } from '@backstage/plugin-auth-node'; +import { durationToMilliseconds } from '@backstage/types'; +import { readDurationFromConfig } from '@backstage/config'; const HTTP_OPTION_TIMEOUT = 10000; -const httpOptionsProvider: CustomHttpOptionsProvider = (_url, options) => { - return { - ...options, - timeout: HTTP_OPTION_TIMEOUT, +const createHttpOptionsProvider = + ({ timeout }: { timeout?: number }): CustomHttpOptionsProvider => + (_url, options) => { + return { + ...options, + timeout: timeout ?? HTTP_OPTION_TIMEOUT, + }; }; -}; /** * authentication result for the OIDC which includes the token set and user @@ -85,6 +89,15 @@ export const oidcAuthenticator = createOAuthAuthenticator({ ); } + const timeoutMilliseconds = config.has('timeout') + ? durationToMilliseconds( + readDurationFromConfig(config, { key: 'timeout' }), + ) + : undefined; + const httpOptionsProvider = createHttpOptionsProvider({ + timeout: timeoutMilliseconds, + }); + Issuer[custom.http_options] = httpOptionsProvider; const promise = Issuer.discover(metadataUrl).then(issuer => { issuer[custom.http_options] = httpOptionsProvider; diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 73cc1effcb..22df305701 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-auth-backend +## 0.24.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend-module-oidc-provider@0.4.2-next.0 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.4.1 + - @backstage/plugin-auth-backend-module-auth0-provider@0.2.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.1 + - @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.3.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.3.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.4.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.2.1 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.3.1 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.2-next.0 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.6 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.1 + - @backstage/plugin-auth-backend-module-google-provider@0.3.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.6 + - @backstage/plugin-catalog-node@1.16.1 + ## 0.24.4 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 2173e8a3e1..1dabc3b306 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.24.4", + "version": "0.24.5-next.0", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index ea21282077..a80f3cded4 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-kubernetes-common@0.9.4 + ## 0.4.9 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index dfaa2ff914..1f7ce93c97 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.9", + "version": "0.4.10-next.0", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index d0647ba4e1..5e07707e42 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.7.12-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + ## 0.3.8 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index cb68676e62..f8b9f94205 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.3.8", + "version": "0.3.9-next.0", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 629a5bfe94..3fb473203a 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-github +## 0.7.12-next.0 + +### Patch Changes + +- 16648ef: Added `validateLocationsExist` to the config definition where it was missing. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + ## 0.7.11 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 240571df53..2550eaf757 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.7.11", + "version": "0.7.12-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 87843783a9..17de05e67d 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.6.5-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + ## 0.2.7 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 883360efd2..40ebc68b8e 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.2.7", + "version": "0.2.8-next.0", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 0893e2c8ad..14345708ae 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.6.5-next.0 + +### Patch Changes + +- a568cda: add filter for repos by membership and topics +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + ## 0.6.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/config.d.ts b/plugins/catalog-backend-module-gitlab/config.d.ts index ce1cda50e7..e41de67208 100644 --- a/plugins/catalog-backend-module-gitlab/config.d.ts +++ b/plugins/catalog-backend-module-gitlab/config.d.ts @@ -72,6 +72,18 @@ export interface Config { * Should be in the format group/subgroup/repo, with no leading or trailing slashes. */ excludeRepos?: string[]; + + /** + * (Optional) If true, limit by repositories that the current user is a member of. + * See: https://docs.gitlab.com/api/projects/#list-projects + */ + membership?: boolean; + + /** + * (Optional) List of topic names. Limit results to repositories that match all of given topics. + * See: https://docs.gitlab.com/api/projects/#list-projects + */ + topics?: string; }; }; }; diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index daa63096b7..6b78d7ecd9 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.6.4", + "version": "0.6.5-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/report.api.md b/plugins/catalog-backend-module-gitlab/report.api.md index e2577553d3..5b7bfadf1d 100644 --- a/plugins/catalog-backend-module-gitlab/report.api.md +++ b/plugins/catalog-backend-module-gitlab/report.api.md @@ -120,6 +120,8 @@ export type GitlabProviderConfig = { includeArchivedRepos?: boolean; excludeRepos?: string[]; includeUsersWithoutSeat?: boolean; + membership?: boolean; + topics?: string; }; // @public diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index f5ed760b21..9053d68554 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -41,6 +41,8 @@ export type CommonListOptions = { interface ListProjectOptions extends CommonListOptions { archived?: boolean; group?: string; + membership?: boolean; + topics?: string; } interface UserListOptions extends CommonListOptions { diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index e25f9bacf4..864eca7dfe 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -248,6 +248,18 @@ export type GitlabProviderConfig = { * Defaults to `false` */ includeUsersWithoutSeat?: boolean; + + /** + * If true, the membership parameter is set to true in the GitLab API request. + * See: https://docs.gitlab.com/api/projects/#list-projects + */ + membership?: boolean; + + /** + * Optional comma seperated list of topics to filter projects by, as specified in the GitLab API documentation: + * https://docs.gitlab.com/api/projects/#list-projects + */ + topics?: string; }; /** diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 66a6247295..8bf749c275 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -214,6 +214,8 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { page: 1, per_page: 50, ...(!this.config.includeArchivedRepos && { archived: false }), + ...(this.config.membership && { membership: true }), + ...(this.config.topics && { topics: this.config.topics }), }, ); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index e1798df184..8790a3fe53 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -65,6 +65,8 @@ describe('config', () => { excludeRepos: [], restrictUsersToGroup: false, includeUsersWithoutSeat: false, + membership: undefined, + topics: undefined, }), ); }); @@ -109,6 +111,8 @@ describe('config', () => { excludeRepos: [], restrictUsersToGroup: false, includeUsersWithoutSeat: true, + membership: undefined, + topics: undefined, }), ); }); @@ -153,6 +157,8 @@ describe('config', () => { skipForkedRepos: true, includeArchivedRepos: false, includeUsersWithoutSeat: false, + membership: undefined, + topics: undefined, }), ); }); @@ -197,6 +203,8 @@ describe('config', () => { skipForkedRepos: false, includeArchivedRepos: true, includeUsersWithoutSeat: false, + membership: undefined, + topics: undefined, }), ); }); @@ -242,6 +250,8 @@ describe('config', () => { includeArchivedRepos: false, excludeRepos: ['foo/bar', 'quz/qux'], includeUsersWithoutSeat: false, + membership: undefined, + topics: undefined, }), ); }); @@ -287,6 +297,8 @@ describe('config', () => { restrictUsersToGroup: false, excludeRepos: [], includeUsersWithoutSeat: false, + membership: undefined, + topics: undefined, schedule: { frequency: { minutes: 30 }, timeout: { @@ -336,4 +348,188 @@ describe('config', () => { expect(result).toHaveLength(1); expect(result[0].group).toEqual(''); }); + + it('valid config with membership', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + branch: 'not-master', + fallbackBranch: 'main', + entityFilename: 'custom-file.yaml', + membership: true, + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'not-master', + fallbackBranch: 'main', + host: 'host', + catalogFile: 'custom-file.yaml', + projectPattern: /[\s\S]*/, + groupPattern: /[\s\S]*/, + userPattern: /[\s\S]*/, + orgEnabled: false, + allowInherited: false, + relations: [], + schedule: undefined, + restrictUsersToGroup: false, + excludeRepos: [], + skipForkedRepos: false, + includeUsersWithoutSeat: false, + includeArchivedRepos: false, + membership: true, + topics: undefined, + }), + ); + }); + + it('valid config with empyt topics', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + branch: 'not-master', + fallbackBranch: 'main', + entityFilename: 'custom-file.yaml', + topics: [], + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'not-master', + fallbackBranch: 'main', + host: 'host', + catalogFile: 'custom-file.yaml', + projectPattern: /[\s\S]*/, + groupPattern: /[\s\S]*/, + userPattern: /[\s\S]*/, + orgEnabled: false, + allowInherited: false, + relations: [], + schedule: undefined, + restrictUsersToGroup: false, + excludeRepos: [], + skipForkedRepos: false, + includeUsersWithoutSeat: false, + includeArchivedRepos: false, + membership: undefined, + topics: undefined, + }), + ); + }); + + it('valid config with single topics', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + branch: 'not-master', + fallbackBranch: 'main', + entityFilename: 'custom-file.yaml', + topics: ['topic1'], + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'not-master', + fallbackBranch: 'main', + host: 'host', + catalogFile: 'custom-file.yaml', + projectPattern: /[\s\S]*/, + groupPattern: /[\s\S]*/, + userPattern: /[\s\S]*/, + orgEnabled: false, + allowInherited: false, + relations: [], + schedule: undefined, + restrictUsersToGroup: false, + excludeRepos: [], + skipForkedRepos: false, + includeUsersWithoutSeat: false, + includeArchivedRepos: false, + membership: undefined, + topics: 'topic1', + }), + ); + }); + + it('valid config with multiple topics', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + branch: 'not-master', + fallbackBranch: 'main', + entityFilename: 'custom-file.yaml', + topics: ['topic1', 'topic2', 'topic3'], + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'not-master', + fallbackBranch: 'main', + host: 'host', + catalogFile: 'custom-file.yaml', + projectPattern: /[\s\S]*/, + groupPattern: /[\s\S]*/, + userPattern: /[\s\S]*/, + orgEnabled: false, + allowInherited: false, + relations: [], + schedule: undefined, + restrictUsersToGroup: false, + excludeRepos: [], + skipForkedRepos: false, + includeUsersWithoutSeat: false, + includeArchivedRepos: false, + membership: undefined, + topics: 'topic1,topic2,topic3', + }), + ); + }); }); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts index bf59bb0d36..5b50fce5b1 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -66,6 +66,11 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { const includeUsersWithoutSeat = config.getOptionalBoolean('includeUsersWithoutSeat') ?? false; + const membership = config.getOptionalBoolean('membership'); + + const topicsArray = config.getOptionalStringArray('topics'); + const topics = topicsArray?.length ? topicsArray.join(',') : undefined; + return { id, group, @@ -85,6 +90,8 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { excludeRepos, restrictUsersToGroup, includeUsersWithoutSeat, + membership, + topics, }; } diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index cb0b578d80..fea3252df5 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.6.5-next.0 + +### Patch Changes + +- 27d1031: fixed misleading example location annotations in docs +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-catalog-backend@1.32.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-events-node@0.4.9 + - @backstage/plugin-permission-common@0.8.4 + ## 0.6.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/README.md b/plugins/catalog-backend-module-incremental-ingestion/README.md index 2535fbe8e3..7673ca08ab 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/README.md +++ b/plugins/catalog-backend-module-incremental-ingestion/README.md @@ -208,12 +208,20 @@ export class MyIncrementalEntityProvider The last step is to implement the actual `next` method that will accept the cursor, call the API, process the result and return the result. ```ts +import { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, +} from '@backstage/catalog-model'; +import { IncrementalEntityProvider } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; + export class MyIncrementalEntityProvider implements IncrementalEntityProvider { token: string; + mySource: string; - constructor(token: string) { + constructor(token: string, mySource: string) { this.token = token; + this.mySource = mySource; } getProviderName() { @@ -230,6 +238,7 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider> { const { apiClient } = context; + const location = `${this.getProviderName()}:${this.mySource}`; // call your API with the current cursor const data = await apiClient.getServices(cursor); @@ -249,8 +258,8 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider any; + onConfirm: () => any; +} + +export function DeleteEntityConfirmationDialog( + props: DeleteEntityConfirmationProps, +) { + const { open, onClose, onConfirm } = props; + const [busy, setBusy] = useState(false); + const onDelete = async () => { + setBusy(true); + try { + onConfirm(); + } catch (err) { + assertError(err); + } finally { + setBusy(false); + } + }; + + return ( + + + Are you sure you want to delete this entity? + + + + + + + ); +} diff --git a/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx b/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx index 65e6eb731e..32a7b42eb4 100644 --- a/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx +++ b/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx @@ -34,6 +34,7 @@ import { EntityDialog } from './EntityDialog'; import { catalogUnprocessedEntitiesApiRef } from '../api'; import useAsync from 'react-use/esm/useAsync'; import DeleteIcon from '@material-ui/icons/Delete'; +import { DeleteEntityConfirmationDialog } from './DeleteEntityConfirmationDialog'; const useStyles = makeStyles((theme: Theme) => ({ errorBox: { @@ -92,18 +93,15 @@ const RenderErrorContext = ({ * Converts input datetime which lacks timezone info into user's local time so that they can * easily understand the times. */ -const convertTimeToLocalTimezone = (strDateTime: string | Date) => { - const dateTime = DateTime.fromFormat( - strDateTime.toLocaleString(), - 'yyyy-MM-dd hh:mm:ss', - { - zone: 'UTC', - }, - ); +export const convertTimeToLocalTimezone = (dateTime: string | Date) => { + const isoDateTime = + typeof dateTime === 'string' ? dateTime : dateTime.toISOString(); - const dateTimeLocalTz = dateTime.setZone(DateTime.local().zoneName); + const strDateTime = DateTime.fromISO(isoDateTime, { + zone: DateTime.local().zoneName, + }); - return dateTimeLocalTz.toFormat('yyyy-MM-dd hh:mm:ss ZZZZ'); + return strDateTime.toFormat('yyyy-MM-dd hh:mm:ss ZZZZ'); }; export const FailedEntities = () => { @@ -117,6 +115,13 @@ export const FailedEntities = () => { const [, setSelectedSearchTerm] = useState(''); const unprocessedEntityApi = useApi(catalogUnprocessedEntitiesApiRef); const alertApi = useApi(alertApiRef); + const [selectedEntityId, setSelectedEntityId] = useState( + undefined, + ); + const [selectedEntityRef, setSelectedEntityRef] = useState< + string | undefined + >(undefined); + const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); if (loading) { return ; @@ -125,25 +130,34 @@ export const FailedEntities = () => { return ; } - const handleDelete = async ({ + const handleDelete = ({ entityId, entityRef, }: { entityId: string; entityRef: string; }) => { + setSelectedEntityId(entityId); + setSelectedEntityRef(entityRef); + setConfirmationDialogOpen(true); + }; + + const cleanUpAfterRemoval = async () => { try { - await unprocessedEntityApi.delete(entityId); - alertApi.post({ - message: `Entity ${entityRef} has been deleted`, - severity: 'success', - }); + if (selectedEntityId) { + await unprocessedEntityApi.delete(selectedEntityId); + alertApi.post({ + message: `Entity ${selectedEntityRef} has been deleted`, + severity: 'success', + }); + } } catch (e) { alertApi.post({ - message: `Ran into an issue when deleting ${entityRef}. Please try again later.`, + message: `Ran into an issue when deleting ${selectedEntityRef}. Please try again later.`, severity: 'error', }); } + setConfirmationDialogOpen(false); }; const columns: TableColumn[] = [ @@ -213,8 +227,8 @@ export const FailedEntities = () => { return ( - await handleDelete({ + onClick={() => + handleDelete({ entityId: entity_id, entityRef: entity_ref, }) @@ -228,37 +242,46 @@ export const FailedEntities = () => { ]; return ( - - No failed entities found - - } - onSearchChange={(searchTerm: string) => setSelectedSearchTerm(searchTerm)} - detailPanel={({ rowData }) => { - const errors = (rowData as UnprocessedEntity).errors; - return ( - <> - {errors?.map((e, idx) => { - return ( - - - {e.name} - - - - - ); - })} - - ); - }} - /> + <> +
+ No failed entities found + + } + onSearchChange={(searchTerm: string) => + setSelectedSearchTerm(searchTerm) + } + detailPanel={({ rowData }) => { + const errors = (rowData as UnprocessedEntity).errors; + return ( + <> + {errors?.map((e, idx) => { + return ( + + + {e.name} + + + + + ); + })} + + ); + }} + /> + setConfirmationDialogOpen(false)} + onConfirm={cleanUpAfterRemoval} + /> + ); }; diff --git a/plugins/catalog-unprocessed-entities/src/plugin.test.ts b/plugins/catalog-unprocessed-entities/src/plugin.test.ts index add3d677f0..ebeb24f4ab 100644 --- a/plugins/catalog-unprocessed-entities/src/plugin.test.ts +++ b/plugins/catalog-unprocessed-entities/src/plugin.test.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { convertTimeToLocalTimezone } from './components/FailedEntities'; import { catalogUnprocessedEntitiesPlugin } from './plugin'; describe('catalog-unprocessed-entities', () => { @@ -20,3 +21,29 @@ describe('catalog-unprocessed-entities', () => { expect(catalogUnprocessedEntitiesPlugin).toBeDefined(); }); }); + +describe('components/FailedEntities/convertTimeToLocalTimezone', () => { + it('should correctly a UTC ISO string to local time', () => { + const utcTime = '2024-09-03T08:15:08.088Z'; + const localTime = convertTimeToLocalTimezone(utcTime); + expect(localTime).toBe('2024-09-03 08:15:08 UTC'); + }); + + it('should correctly convert a UTC Date object to local time', () => { + const utcTime = new Date('2024-09-03T08:15:08.088Z'); + const localTime = convertTimeToLocalTimezone(utcTime); + expect(localTime).toBe('2024-09-03 08:15:08 UTC'); + }); + + it('should return "Invalid Date" for an invalid date string', () => { + const invalidTime = 'invalid-date-string'; + const localTime = convertTimeToLocalTimezone(invalidTime); + expect(localTime).toBe('Invalid DateTime'); + }); + + it('should handle empty string input', () => { + const emptyString = ''; + const localTime = convertTimeToLocalTimezone(emptyString); + expect(localTime).toBe('Invalid DateTime'); + }); +}); diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index b4bafe3e9f..bc6ce467f4 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog +## 1.29.0-next.0 + +### Minor Changes + +- 9454ef9: Added support of filtering based on system columns in catalog table + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration-react@1.2.5 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-scaffolder-common@1.5.10 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + ## 1.28.0 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index e8605e0db4..752ba744e3 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.28.0", + "version": "1.29.0-next.0", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog/src/components/CatalogTable/columns.tsx b/plugins/catalog/src/components/CatalogTable/columns.tsx index d5362badda..87de8bfc91 100644 --- a/plugins/catalog/src/components/CatalogTable/columns.tsx +++ b/plugins/catalog/src/components/CatalogTable/columns.tsx @@ -62,6 +62,18 @@ export const columnFactories = Object.freeze({ return { title: 'System', field: 'resolved.partOfSystemRelationTitle', + customFilterAndSearch: (query, row) => { + if (!row.resolved.partOfSystemRelations) { + return false; + } + + const systemNames = row.resolved.partOfSystemRelations.map( + ref => ref.name, + ); // Extract system names from entityRefs + + const searchText = systemNames.join(', ').toLocaleUpperCase('en-US'); + return searchText.includes(query.toLocaleUpperCase('en-US')); + }, render: ({ resolved }) => ( { - let app: express.Express; - let loggerSpy: jest.SpyInstance; - let taskBroker: TaskBroker; - const catalogClient = catalogServiceMock.mock(); - const permissionApi = { - authorize: jest.fn(), - authorizeConditional: jest.fn(), - } as unknown as PermissionEvaluator; - const auth = mockServices.auth(); - const httpAuth = mockServices.httpAuth(); - const discovery = mockServices.discovery(); - const events = { - publish: jest.fn(), - } as unknown as EventsService; +describe.each([ + { + desc: 'no template filters/globals', + }, + { + desc: 'legacy template filters', + additionalTemplateFilters: { + foo: (s: any) => s, + bar: (bar: any) => !!bar, + baz: (what: string, ever: string) => what + ever, + } as Record, + }, + { + desc: 'created template filters', + additionalTemplateFilters: [ + createTemplateFilter({ + id: 'foo', + schema: z => + z + .function() + .args(z.any().describe('a value')) + .returns(z.any().describe('same value')), + filter: s => s, + }), + createTemplateFilter({ + id: 'bar', + filter: bar => !!bar, + }), + createTemplateFilter({ + id: 'baz', + description: 'append the argument to the incoming value', + schema: z => + z + .function() + .args(z.string(), z.string().describe('value to append to input')) + .returns(z.string().describe('input+suffix')), + filter: (what, ever) => what + ever, + }), + createTemplateFilter({ + id: 'blah', + schema: z => + z + .function() + .args( + z.number(), + z.number().describe('factor by which to multiply input'), + z.number().describe('addend by which to increase input * factor'), + ), + filter: (base, factor, addend) => base * factor + addend, + }), + ], + }, + { + desc: 'legacy template globals', + additionalTemplateGlobals: { + nul: null, + nop: x => x, + } as Record, + }, + { + desc: 'created template globals', + additionalTemplateGlobals: [ + createTemplateGlobalValue({ + id: 'nul', + description: 'null value', + value: null, + }), + createTemplateGlobalFunction({ + id: 'nop', + description: 'nop function', + schema: z => + z + .function() + .args(z.any().describe('input')) + .returns(z.any().describe('output')), + fn: x => x, + }), + ], + }, +])( + 'createRouter, $desc', + ({ additionalTemplateFilters, additionalTemplateGlobals }) => { + let app: express.Express; + let loggerSpy: jest.SpyInstance; + let taskBroker: TaskBroker; + const catalogClient = catalogServiceMock.mock(); + const permissionApi = { + authorize: jest.fn(), + authorizeConditional: jest.fn(), + } as unknown as PermissionEvaluator; + const auth = mockServices.auth(); + const httpAuth = mockServices.httpAuth(); + const discovery = mockServices.discovery(); + const events = { + publish: jest.fn(), + } as unknown as EventsService; - const credentials = mockCredentials.user(); - const token = mockCredentials.service.token({ - onBehalfOf: credentials, - targetPluginId: 'catalog', - }); + const credentials = mockCredentials.user(); + const token = mockCredentials.service.token({ + onBehalfOf: credentials, + targetPluginId: 'catalog', + }); - const getMockTemplate = (): TemplateEntityV1beta3 => ({ - apiVersion: 'scaffolder.backstage.io/v1beta3', - kind: 'Template', - metadata: { - description: 'Create a new CRA website project', - name: 'create-react-app-template', - tags: ['experimental', 'react', 'cra'], - title: 'Create React App Template', - annotations: { - 'backstage.io/managed-by-location': 'url:https://dev.azure.com', + const getMockTemplate = (): TemplateEntityV1beta3 => ({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + description: 'Create a new CRA website project', + name: 'create-react-app-template', + tags: ['experimental', 'react', 'cra'], + title: 'Create React App Template', + annotations: { + 'backstage.io/managed-by-location': 'url:https://dev.azure.com', + }, }, - }, - spec: { - owner: 'web@example.com', - type: 'website', - steps: [ - { - id: 'step-one', - name: 'First log', - action: 'debug:log', - input: { - message: 'hello', - }, - }, - { - id: 'step-two', - name: 'Second log', - action: 'debug:log', - input: { - message: 'world', - }, - 'backstage:permissions': { - tags: ['steps-tag'], - }, - }, - ], - parameters: [ - { - type: 'object', - required: ['requiredParameter1'], - properties: { - requiredParameter1: { - type: 'string', - description: 'Required parameter 1', + spec: { + owner: 'web@example.com', + type: 'website', + steps: [ + { + id: 'step-one', + name: 'First log', + action: 'debug:log', + input: { + message: 'hello', }, }, - }, - { - type: 'object', - required: ['requiredParameter2'], - 'backstage:permissions': { - tags: ['parameters-tag'], - }, - properties: { - requiredParameter2: { - type: 'string', - description: 'Required parameter 2', + { + id: 'step-two', + name: 'Second log', + action: 'debug:log', + input: { + message: 'world', + }, + 'backstage:permissions': { + tags: ['steps-tag'], }, }, - }, - ], - }, - }); - - const mockUser: UserEntity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'User', - metadata: { - name: 'guest', - annotations: { - 'google.com/email': 'bobby@tables.com', + ], + parameters: [ + { + type: 'object', + required: ['requiredParameter1'], + properties: { + requiredParameter1: { + type: 'string', + description: 'Required parameter 1', + }, + }, + }, + { + type: 'object', + required: ['requiredParameter2'], + 'backstage:permissions': { + tags: ['parameters-tag'], + }, + properties: { + requiredParameter2: { + type: 'string', + description: 'Required parameter 2', + }, + }, + }, + ], }, - }, - spec: { - profile: { - displayName: 'Robert Tables of the North', + }); + + const mockUser: UserEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + name: 'guest', + annotations: { + 'google.com/email': 'bobby@tables.com', + }, }, - }, - }; - - describe('not providing an identity api', () => { - beforeEach(async () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - const databaseTaskStore = await DatabaseTaskStore.create({ - database: createDatabase(), - }); - taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); - - jest.spyOn(taskBroker, 'dispatch'); - jest.spyOn(taskBroker, 'get'); - jest.spyOn(taskBroker, 'list'); - jest.spyOn(taskBroker, 'event$'); - loggerSpy = jest.spyOn(logger, 'info'); - - const router = await createRouter({ - logger: logger, - config: new ConfigReader({}), - database: createDatabase(), - catalogClient, - reader: mockUrlReader, - taskBroker, - permissions: permissionApi, - auth, - httpAuth, - discovery, - events, - }); - app = express().use(router); - - catalogClient.getEntityByRef.mockImplementation(async ref => { - const { kind } = parseEntityRef(ref); - - if (kind.toLocaleLowerCase() === 'template') { - return getMockTemplate(); - } - - if (kind.toLocaleLowerCase() === 'user') { - return mockUser; - } - - throw new Error(`no mock found for kind: ${kind}`); - }); - - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - jest.spyOn(permissionApi, 'authorize').mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, + spec: { + profile: { + displayName: 'Robert Tables of the North', }, - ]); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('GET /v2/actions', () => { - it('lists available actions', async () => { - const response = await request(app).get('/v2/actions').send(); - expect(response.status).toEqual(200); - expect(response.body[0].id).toBeDefined(); - expect(response.body.length).toBeGreaterThan(8); - }); - }); - - describe('POST /v2/tasks', () => { - it('rejects template values which do not match the template schema definition', async () => { - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - storePath: 'https://github.com/backstage/backstage', - }, - }); - - expect(response.status).toEqual(400); - }); - - it('return the template id', async () => { - const broker = - taskBroker.dispatch as jest.Mocked['dispatch']; - broker.mockResolvedValue({ - taskId: 'a-random-id', - }); - - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(response.status).toEqual(201); - expect(response.body.id).toBe('a-random-id'); - }); - - it('should call the broker with a correct spec', async () => { - const broker = - taskBroker.dispatch as jest.Mocked['dispatch']; - const mockToken = mockCredentials.user.token(); - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/tasks') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - backstageToken: token, - __initiatorCredentials: JSON.stringify(credentials), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: mockTemplate.spec.steps.map((step, index) => ({ - ...step, - id: step.id ?? `step-${index + 1}`, - name: step.name ?? step.action, - })), - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('should emit auditlog containing user identifier when backstage auth is passed', async () => { - const mockToken = mockCredentials.user.token(); - - await request(app) - .post('/v2/tasks') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(loggerSpy).toHaveBeenCalledTimes(1); - expect(loggerSpy).toHaveBeenCalledWith( - 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', - ); - }); - }); - - describe('GET /v2/tasks', () => { - it('return all tasks', async () => { - ( - taskBroker.list as jest.Mocked>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get(`/v2/tasks`); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: {}, - pagination: {}, - }); - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - }); - - it('return filtered tasks', async () => { - ( - taskBroker.list as jest.Mocked>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get( - `/v2/tasks?createdBy=user:default/foo&createdBy=user:default/bar&status=completed&status=open&limit=1&offset=0&order=desc:created_at`, - ); - - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: { - createdBy: ['user:default/foo', 'user:default/bar'], - status: ['completed', 'open'], - }, - pagination: { - limit: 1, - offset: 0, - }, - order: [{ order: 'desc', field: 'created_at' }], - }); - }); - }); - - describe('GET /v2/tasks/:taskId', () => { - it('does not divulge secrets', async () => { - (taskBroker.get as jest.Mocked['get']).mockResolvedValue({ - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - secrets: { - backstageToken: token, - __initiatorCredentials: JSON.stringify(credentials), - }, - createdBy: '', - }); - - const response = await request(app).get(`/v2/tasks/a-random-id`); - expect(response.status).toEqual(200); - expect(response.body.status).toBe('completed'); - expect(response.body.secrets).toBeUndefined(); - }); - }); - - describe('GET /v2/tasks/:taskId/eventstream', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ], - }); - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - // emit after this function returned - }); - - let statusCode: any = undefined; - let headers: any = {}; - const responseDataFn = jest.fn(); - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', chunk => { - responseDataFn(chunk.toString()); - - // the server expects the client to abort the request - if (chunk.includes('completion')) { - req.abort(); - } - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - expect(responseDataFn).toHaveBeenCalledTimes(2); - expect(responseDataFn).toHaveBeenCalledWith(`event: log -data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} - -`); - expect(responseDataFn).toHaveBeenCalledWith(`event: completion -data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} - -`); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); - }); - - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - }); - - let statusCode: any = undefined; - let headers: any = {}; - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .query({ after: 10 }) - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', () => { - // close immediately - req.abort(); - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('GET /v2/tasks/:taskId/events', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - - const response = await request(app).get('/v2/tasks/a-random-id/events'); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { - id: 0, - taskId: 'a-random-id', - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId: 'a-random-id', - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); - }); - - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(() => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ events: [] }); - }); - }); - - const response = await request(app) - .get('/v2/tasks/a-random-id/events') - .query({ after: 10 }); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('POST /v2/dry-run', () => { - it('should get user entity', async () => { - const mockToken = mockCredentials.user.token(); - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/dry-run') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - template: mockTemplate, - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - directoryContents: [], - }); - - expect(catalogClient.getEntityByRef).toHaveBeenCalledTimes(1); - - expect(catalogClient.getEntityByRef).toHaveBeenCalledWith( - 'user:default/mock', - expect.anything(), - ); - }); - }); - }); - - describe('providing an identity api', () => { - beforeEach(async () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - const databaseTaskStore = await DatabaseTaskStore.create({ - database: createDatabase(), - }); - taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); - - jest.spyOn(taskBroker, 'dispatch'); - jest.spyOn(taskBroker, 'get'); - jest.spyOn(taskBroker, 'list'); - jest.spyOn(taskBroker, 'event$'); - loggerSpy = jest.spyOn(logger, 'info'); - - const router = await createRouter({ - logger: logger, - config: new ConfigReader({}), - database: createDatabase(), - catalogClient, - reader: mockUrlReader, - taskBroker, - permissions: permissionApi, - auth, - httpAuth, - discovery, - }); - app = express().use(router); - - catalogClient.getEntityByRef.mockImplementation(async ref => { - const { kind } = parseEntityRef(ref); - - if (kind.toLocaleLowerCase() === 'template') { - return getMockTemplate(); - } - - if (kind.toLocaleLowerCase() === 'user') { - return mockUser; - } - throw new Error(`no mock found for kind: ${kind}`); - }); - - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - jest.spyOn(permissionApi, 'authorize').mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - ]); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - describe('GET /v2/actions', () => { - it('lists available actions', async () => { - const response = await request(app).get('/v2/actions').send(); - expect(response.status).toEqual(200); - expect(response.body[0].id).toBeDefined(); - expect(response.body.length).toBeGreaterThan(8); - }); - }); - - describe('GET /v2/templates/:namespace/:kind/:name/parameter-schema', () => { - it('returns the parameter schema', async () => { - const response = await request(app) - .get( - '/v2/templates/default/Template/create-react-app-template/parameter-schema', - ) - .send(); - - expect(response.status).toEqual(200); - expect(response.body).toEqual({ - title: 'Create React App Template', - description: 'Create a new CRA website project', - steps: [ - { - title: 'Please enter the following information', - schema: { - required: ['requiredParameter1'], - type: 'object', - properties: { - requiredParameter1: { - description: 'Required parameter 1', - type: 'string', - }, - }, - }, - }, - { - title: 'Please enter the following information', - schema: { - type: 'object', - required: ['requiredParameter2'], - 'backstage:permissions': { - tags: ['parameters-tag'], - }, - properties: { - requiredParameter2: { - type: 'string', - description: 'Required parameter 2', - }, - }, - }, - }, - ], - }); - }); - - it('filters parameters that the user is not authorized to see', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementationOnce(async () => [ - { - result: AuthorizeResult.DENY, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - const response = await request(app) - .get( - '/v2/templates/default/Template/create-react-app-template/parameter-schema', - ) - .send(); - expect(response.status).toEqual(200); - expect(response.body).toEqual({ - title: 'Create React App Template', - description: 'Create a new CRA website project', - steps: [], - }); - }); - - it('filters parameters that the user is not authorized to see in case of conditional decision', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementationOnce(async () => [ - { - conditions: { - resourceType: 'scaffolder-template', - rule: 'HAS_TAG', - params: { tag: 'parameters-tag' }, - }, - pluginId: 'scaffolder', - resourceType: 'scaffolder-template', - result: AuthorizeResult.CONDITIONAL, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - const response = await request(app) - .get( - '/v2/templates/default/Template/create-react-app-template/parameter-schema', - ) - .send(); - expect(response.status).toEqual(200); - expect(response.body).toEqual({ - title: 'Create React App Template', - description: 'Create a new CRA website project', - steps: [ - { - title: 'Please enter the following information', - schema: { - type: 'object', - required: ['requiredParameter2'], - 'backstage:permissions': { - tags: ['parameters-tag'], - }, - properties: { - requiredParameter2: { - type: 'string', - description: 'Required parameter 2', - }, - }, - }, - }, - ], - }); - }); - }); - - describe('POST /v2/tasks', () => { - it('rejects template values which do not match the template schema definition', async () => { - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - storePath: 'https://github.com/backstage/backstage', - }, - }); - - expect(response.status).toEqual(400); - }); - - it('filters steps that the user is not authorized to see', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - result: AuthorizeResult.DENY, - }, - ]); - - const broker = - taskBroker.dispatch as jest.Mocked['dispatch']; - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - backstageToken: token, - __initiatorCredentials: JSON.stringify(credentials), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: [], - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('filters steps that the user is not authorized to see in case of conditional decision', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - conditions: { - resourceType: 'scaffolder-template', - rule: 'HAS_TAG', - params: { tag: 'steps-tag' }, - }, - pluginId: 'scaffolder', - resourceType: 'scaffolder-template', - result: AuthorizeResult.CONDITIONAL, - }, - ]); - - const broker = - taskBroker.dispatch as jest.Mocked['dispatch']; - const mockTemplate = getMockTemplate(); - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - backstageToken: token, - __initiatorCredentials: JSON.stringify(credentials), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: [ - { - id: 'step-two', - name: 'Second log', - action: 'debug:log', - input: { - message: 'world', - }, - 'backstage:permissions': { - tags: ['steps-tag'], - }, - }, - ], - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('return the template id', async () => { - const broker = - taskBroker.dispatch as jest.Mocked['dispatch']; - broker.mockResolvedValue({ - taskId: 'a-random-id', - }); - - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(response.body.id).toBe('a-random-id'); - expect(response.status).toEqual(201); - }); - - it('should call the broker with a correct spec', async () => { - const broker = - taskBroker.dispatch as jest.Mocked['dispatch']; - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - backstageToken: token, - __initiatorCredentials: JSON.stringify(credentials), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: mockTemplate.spec.steps.map((step, index) => ({ - ...step, - id: step.id ?? `step-${index + 1}`, - name: step.name ?? step.action, - })), - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('should emit auditlog containing user identifier when backstage auth is passed', async () => { - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(loggerSpy).toHaveBeenCalledTimes(1); - expect(loggerSpy).toHaveBeenCalledWith( - 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', - ); - }); - }); - - describe('GET /v2/tasks', () => { - it('return all tasks', async () => { - ( - taskBroker.list as jest.Mocked>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get(`/v2/tasks`); - expect(taskBroker.list).toHaveBeenCalledWith({ - pagination: {}, - filters: {}, - }); - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - }); - - it('return filtered tasks', async () => { - ( - taskBroker.list as jest.Mocked>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get( - `/v2/tasks?createdBy=user:default/foo`, - ); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: { - createdBy: ['user:default/foo'], - }, - pagination: {}, - }); - - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - }); - }); - - describe('GET /v2/tasks/:taskId', () => { - it('does not divulge secrets', async () => { - (taskBroker.get as jest.Mocked['get']).mockResolvedValue({ - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - secrets: { - backstageToken: token, - __initiatorCredentials: JSON.stringify(credentials), - }, - createdBy: '', - }); - - const response = await request(app).get(`/v2/tasks/a-random-id`); - expect(response.status).toEqual(200); - expect(response.body.status).toBe('completed'); - expect(response.body.secrets).toBeUndefined(); - }); - }); - - describe('GET /v2/tasks/:taskId/eventstream', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ], - }); - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - // emit after this function returned - }); - - let statusCode: any = undefined; - let headers: any = {}; - const responseDataFn = jest.fn(); - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', chunk => { - responseDataFn(chunk.toString()); - - // the server expects the client to abort the request - if (chunk.includes('completion')) { - req.abort(); - } - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - expect(responseDataFn).toHaveBeenCalledTimes(2); - expect(responseDataFn).toHaveBeenCalledWith(`event: log -data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} - -`); - expect(responseDataFn).toHaveBeenCalledWith(`event: completion -data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} - -`); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); - }); - - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - }); - - let statusCode: any = undefined; - let headers: any = {}; - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .query({ after: 10 }) - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', () => { - // close immediately - req.abort(); - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('GET /v2/tasks/:taskId/events', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - - const response = await request(app).get('/v2/tasks/a-random-id/events'); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { - id: 0, - taskId: 'a-random-id', - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId: 'a-random-id', - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); - }); - - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver; - ( - taskBroker.event$ as jest.Mocked['event$'] - ).mockImplementation(() => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ events: [] }); - }); - }); - - const response = await request(app) - .get('/v2/tasks/a-random-id/events') - .query({ after: 10 }); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('GET /v2/autocomplete/:provider/:resource', () => { - let handleAutocompleteRequest: AutocompleteHandler; + }, + }; + describe('not providing an identity api', () => { beforeEach(async () => { - handleAutocompleteRequest = jest.fn().mockResolvedValue({ - results: [{ title: 'blob' }], + const logger = loggerToWinstonLogger(mockServices.logger.mock()); + const databaseTaskStore = await DatabaseTaskStore.create({ + database: createDatabase(), }); + taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); + + jest.spyOn(taskBroker, 'dispatch'); + jest.spyOn(taskBroker, 'get'); + jest.spyOn(taskBroker, 'list'); + jest.spyOn(taskBroker, 'event$'); + loggerSpy = jest.spyOn(logger, 'info'); const router = await createRouter({ - logger: loggerToWinstonLogger(mockServices.logger.mock()), + logger: logger, config: new ConfigReader({}), database: createDatabase(), catalogClient, @@ -1532,53 +310,1402 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ auth, httpAuth, discovery, - autocompleteHandlers: { - 'test-provider': handleAutocompleteRequest, - }, + events, + additionalTemplateFilters, + additionalTemplateGlobals, + }); + app = express().use(router); + + catalogClient.getEntityByRef.mockImplementation(async ref => { + const { kind } = parseEntityRef(ref); + + if (kind.toLocaleLowerCase() === 'template') { + return getMockTemplate(); + } + + if (kind.toLocaleLowerCase() === 'user') { + return mockUser; + } + + throw new Error(`no mock found for kind: ${kind}`); }); - app = express().use(router).use(mockErrorHandler()); - }); - - it('should throw an error when the provider is not registered', async () => { - const response = await request(app) - .post('/v2/autocomplete/unknown-provider/resource') - .send({ - token: 'token', - context: {}, - }); - - expect(response.status).toEqual(400); - expect(response.body).toEqual( - expect.objectContaining({ - error: { - message: 'Unsupported provider: unknown-provider', - name: 'InputError', + jest + .spyOn(permissionApi, 'authorizeConditional') + .mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, }, - }), - ); + { + result: AuthorizeResult.ALLOW, + }, + ]); + jest.spyOn(permissionApi, 'authorize').mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + ]); }); - it('should call the autocomplete handler', async () => { - const context = { mock: 'context' }; - const mockToken = 'mocktoken'; + afterEach(() => { + jest.clearAllMocks(); + }); - const response = await request(app) - .post('/v2/autocomplete/test-provider/resource') - .send({ - token: mockToken, - context, + describe('GET /v2/actions', () => { + it('lists available actions', async () => { + const response = await request(app).get('/v2/actions').send(); + expect(response.status).toEqual(200); + expect(response.body[0].id).toBeDefined(); + expect(response.body.length).toBeGreaterThan(8); + }); + }); + + describe('GET /v2/template-extensions', () => { + it('lists template filters and globals', async () => { + const response = await request(app) + .get('/v2/template-extensions') + .send(); + expect(response.status).toEqual(200); + const integrations = ScmIntegrations.fromConfig(config); + + expect(response.body).toMatchObject({ + filters: { + ...extractFilterMetadata(createDefaultFilters({ integrations })), + ...extractFilterMetadata(additionalTemplateFilters), + }, + globals: { + functions: extractGlobalFunctionMetadata( + additionalTemplateGlobals, + ), + values: extractGlobalValueMetadata(additionalTemplateGlobals), + }, + }); + }); + }); + + describe('POST /v2/tasks', () => { + it('rejects template values which do not match the template schema definition', async () => { + const response = await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + storePath: 'https://github.com/backstage/backstage', + }, + }); + + expect(response.status).toEqual(400); + }); + + it('return the template id', async () => { + const broker = + taskBroker.dispatch as jest.Mocked['dispatch']; + broker.mockResolvedValue({ + taskId: 'a-random-id', }); - expect(response.status).toEqual(200); + const response = await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); - expect(response.body).toEqual({ results: [{ title: 'blob' }] }); - expect(handleAutocompleteRequest).toHaveBeenCalledWith({ - token: mockToken, - context, - resource: 'resource', + expect(response.status).toEqual(201); + expect(response.body.id).toBe('a-random-id'); + }); + + it('should call the broker with a correct spec', async () => { + const broker = + taskBroker.dispatch as jest.Mocked['dispatch']; + const mockToken = mockCredentials.user.token(); + const mockTemplate = getMockTemplate(); + + await request(app) + .post('/v2/tasks') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + backstageToken: token, + __initiatorCredentials: JSON.stringify(credentials), + }, + + spec: { + apiVersion: mockTemplate.apiVersion, + steps: mockTemplate.spec.steps.map((step, index) => ({ + ...step, + id: step.id ?? `step-${index + 1}`, + name: step.name ?? step.action, + })), + output: mockTemplate.spec.output ?? {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + + it('should emit auditlog containing user identifier when backstage auth is passed', async () => { + const mockToken = mockCredentials.user.token(); + + await request(app) + .post('/v2/tasks') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + + expect(loggerSpy).toHaveBeenCalledTimes(1); + expect(loggerSpy).toHaveBeenCalledWith( + 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', + ); + }); + }); + + describe('GET /v2/tasks', () => { + it('return all tasks', async () => { + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + totalTasks: 1, + }); + + const response = await request(app).get(`/v2/tasks`); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: {}, + pagination: {}, + }); + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + totalTasks: 1, + }); + }); + + it('return filtered tasks', async () => { + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + totalTasks: 1, + }); + + const response = await request(app).get( + `/v2/tasks?createdBy=user:default/foo&createdBy=user:default/bar&status=completed&status=open&limit=1&offset=0&order=desc:created_at`, + ); + + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + totalTasks: 1, + }); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: { + createdBy: ['user:default/foo', 'user:default/bar'], + status: ['completed', 'open'], + }, + pagination: { + limit: 1, + offset: 0, + }, + order: [{ order: 'desc', field: 'created_at' }], + }); + }); + }); + + describe('GET /v2/tasks/:taskId', () => { + it('does not divulge secrets', async () => { + (taskBroker.get as jest.Mocked['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + backstageToken: token, + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + + const response = await request(app).get(`/v2/tasks/a-random-id`); + expect(response.status).toEqual(200); + expect(response.body.status).toBe('completed'); + expect(response.body.secrets).toBeUndefined(); + }); + }); + + describe('GET /v2/tasks/:taskId/eventstream', () => { + it('should return log messages', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + setImmediate(() => { + observer.next({ + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ], + }); + observer.next({ + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + // emit after this function returned + }); + + let statusCode: any = undefined; + let headers: any = {}; + const responseDataFn = jest.fn(); + + const req = request(app) + .get('/v2/tasks/a-random-id/eventstream') + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as any); + + res.on('data', chunk => { + responseDataFn(chunk.toString()); + + // the server expects the client to abort the request + if (chunk.includes('completion')) { + req.abort(); + } + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + expect(responseDataFn).toHaveBeenCalledTimes(2); + expect(responseDataFn).toHaveBeenCalledWith(`event: log +data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} + +`); + expect(responseDataFn).toHaveBeenCalledWith(`event: completion +data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} + +`); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + }); + expect(subscriber!.closed).toBe(true); + }); + + it('should return log messages with after query', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + setImmediate(() => { + observer.next({ + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + }); + + let statusCode: any = undefined; + let headers: any = {}; + + const req = request(app) + .get('/v2/tasks/a-random-id/eventstream') + .query({ after: 10 }) + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as any); + + res.on('data', () => { + // close immediately + req.abort(); + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + after: 10, + }); + + expect(subscriber!.closed).toBe(true); + }); + }); + + describe('GET /v2/tasks/:taskId/events', () => { + it('should return log messages', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + observer.next({ + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + + const response = await request(app).get( + '/v2/tasks/a-random-id/events', + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: 0, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + }); + expect(subscriber!.closed).toBe(true); + }); + + it('should return log messages with after query', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(() => { + return new ObservableImpl(observer => { + subscriber = observer; + observer.next({ events: [] }); + }); + }); + + const response = await request(app) + .get('/v2/tasks/a-random-id/events') + .query({ after: 10 }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + after: 10, + }); + expect(subscriber!.closed).toBe(true); + }); + }); + + describe('POST /v2/dry-run', () => { + it('should get user entity', async () => { + const mockToken = mockCredentials.user.token(); + const mockTemplate = getMockTemplate(); + + await request(app) + .post('/v2/dry-run') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + template: mockTemplate, + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + directoryContents: [], + }); + + expect(catalogClient.getEntityByRef).toHaveBeenCalledTimes(1); + + expect(catalogClient.getEntityByRef).toHaveBeenCalledWith( + 'user:default/mock', + expect.anything(), + ); }); }); }); - }); -}); + + describe('providing an identity api', () => { + beforeEach(async () => { + const logger = loggerToWinstonLogger(mockServices.logger.mock()); + const databaseTaskStore = await DatabaseTaskStore.create({ + database: createDatabase(), + }); + taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); + + jest.spyOn(taskBroker, 'dispatch'); + jest.spyOn(taskBroker, 'get'); + jest.spyOn(taskBroker, 'list'); + jest.spyOn(taskBroker, 'event$'); + loggerSpy = jest.spyOn(logger, 'info'); + + const router = await createRouter({ + logger: logger, + config: new ConfigReader({}), + database: createDatabase(), + catalogClient, + reader: mockUrlReader, + taskBroker, + permissions: permissionApi, + auth, + httpAuth, + discovery, + }); + app = express().use(router); + + catalogClient.getEntityByRef.mockImplementation(async ref => { + const { kind } = parseEntityRef(ref); + + if (kind.toLocaleLowerCase() === 'template') { + return getMockTemplate(); + } + + if (kind.toLocaleLowerCase() === 'user') { + return mockUser; + } + throw new Error(`no mock found for kind: ${kind}`); + }); + + jest + .spyOn(permissionApi, 'authorizeConditional') + .mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + { + result: AuthorizeResult.ALLOW, + }, + ]); + jest.spyOn(permissionApi, 'authorize').mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + ]); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /v2/actions', () => { + it('lists available actions', async () => { + const response = await request(app).get('/v2/actions').send(); + expect(response.status).toEqual(200); + expect(response.body[0].id).toBeDefined(); + expect(response.body.length).toBeGreaterThan(8); + }); + }); + + describe('GET /v2/templates/:namespace/:kind/:name/parameter-schema', () => { + it('returns the parameter schema', async () => { + const response = await request(app) + .get( + '/v2/templates/default/Template/create-react-app-template/parameter-schema', + ) + .send(); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + title: 'Create React App Template', + description: 'Create a new CRA website project', + steps: [ + { + title: 'Please enter the following information', + schema: { + required: ['requiredParameter1'], + type: 'object', + properties: { + requiredParameter1: { + description: 'Required parameter 1', + type: 'string', + }, + }, + }, + }, + { + title: 'Please enter the following information', + schema: { + type: 'object', + required: ['requiredParameter2'], + 'backstage:permissions': { + tags: ['parameters-tag'], + }, + properties: { + requiredParameter2: { + type: 'string', + description: 'Required parameter 2', + }, + }, + }, + }, + ], + }); + }); + + it('filters parameters that the user is not authorized to see', async () => { + jest + .spyOn(permissionApi, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + result: AuthorizeResult.DENY, + }, + { + result: AuthorizeResult.ALLOW, + }, + ]); + const response = await request(app) + .get( + '/v2/templates/default/Template/create-react-app-template/parameter-schema', + ) + .send(); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + title: 'Create React App Template', + description: 'Create a new CRA website project', + steps: [], + }); + }); + + it('filters parameters that the user is not authorized to see in case of conditional decision', async () => { + jest + .spyOn(permissionApi, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + conditions: { + resourceType: 'scaffolder-template', + rule: 'HAS_TAG', + params: { tag: 'parameters-tag' }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-template', + result: AuthorizeResult.CONDITIONAL, + }, + { + result: AuthorizeResult.ALLOW, + }, + ]); + const response = await request(app) + .get( + '/v2/templates/default/Template/create-react-app-template/parameter-schema', + ) + .send(); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + title: 'Create React App Template', + description: 'Create a new CRA website project', + steps: [ + { + title: 'Please enter the following information', + schema: { + type: 'object', + required: ['requiredParameter2'], + 'backstage:permissions': { + tags: ['parameters-tag'], + }, + properties: { + requiredParameter2: { + type: 'string', + description: 'Required parameter 2', + }, + }, + }, + }, + ], + }); + }); + }); + + describe('POST /v2/tasks', () => { + it('rejects template values which do not match the template schema definition', async () => { + const response = await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + storePath: 'https://github.com/backstage/backstage', + }, + }); + + expect(response.status).toEqual(400); + }); + + it('filters steps that the user is not authorized to see', async () => { + jest + .spyOn(permissionApi, 'authorizeConditional') + .mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + { + result: AuthorizeResult.DENY, + }, + ]); + + const broker = + taskBroker.dispatch as jest.Mocked['dispatch']; + const mockTemplate = getMockTemplate(); + + await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + backstageToken: token, + __initiatorCredentials: JSON.stringify(credentials), + }, + + spec: { + apiVersion: mockTemplate.apiVersion, + steps: [], + output: mockTemplate.spec.output ?? {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + + it('filters steps that the user is not authorized to see in case of conditional decision', async () => { + jest + .spyOn(permissionApi, 'authorizeConditional') + .mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + { + conditions: { + resourceType: 'scaffolder-template', + rule: 'HAS_TAG', + params: { tag: 'steps-tag' }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-template', + result: AuthorizeResult.CONDITIONAL, + }, + ]); + + const broker = + taskBroker.dispatch as jest.Mocked['dispatch']; + const mockTemplate = getMockTemplate(); + await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + backstageToken: token, + __initiatorCredentials: JSON.stringify(credentials), + }, + + spec: { + apiVersion: mockTemplate.apiVersion, + steps: [ + { + id: 'step-two', + name: 'Second log', + action: 'debug:log', + input: { + message: 'world', + }, + 'backstage:permissions': { + tags: ['steps-tag'], + }, + }, + ], + output: mockTemplate.spec.output ?? {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + + it('return the template id', async () => { + const broker = + taskBroker.dispatch as jest.Mocked['dispatch']; + broker.mockResolvedValue({ + taskId: 'a-random-id', + }); + + const response = await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + + expect(response.body.id).toBe('a-random-id'); + expect(response.status).toEqual(201); + }); + + it('should call the broker with a correct spec', async () => { + const broker = + taskBroker.dispatch as jest.Mocked['dispatch']; + const mockTemplate = getMockTemplate(); + + await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + backstageToken: token, + __initiatorCredentials: JSON.stringify(credentials), + }, + + spec: { + apiVersion: mockTemplate.apiVersion, + steps: mockTemplate.spec.steps.map((step, index) => ({ + ...step, + id: step.id ?? `step-${index + 1}`, + name: step.name ?? step.action, + })), + output: mockTemplate.spec.output ?? {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + + it('should emit auditlog containing user identifier when backstage auth is passed', async () => { + await request(app) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + + expect(loggerSpy).toHaveBeenCalledTimes(1); + expect(loggerSpy).toHaveBeenCalledWith( + 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', + ); + }); + }); + + describe('GET /v2/tasks', () => { + it('return all tasks', async () => { + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + totalTasks: 1, + }); + + const response = await request(app).get(`/v2/tasks`); + expect(taskBroker.list).toHaveBeenCalledWith({ + pagination: {}, + filters: {}, + }); + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + totalTasks: 1, + }); + }); + + it('return filtered tasks', async () => { + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + totalTasks: 1, + }); + + const response = await request(app).get( + `/v2/tasks?createdBy=user:default/foo`, + ); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: { + createdBy: ['user:default/foo'], + }, + pagination: {}, + }); + + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + totalTasks: 1, + }); + }); + }); + + describe('GET /v2/tasks/:taskId', () => { + it('does not divulge secrets', async () => { + (taskBroker.get as jest.Mocked['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + backstageToken: token, + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + + const response = await request(app).get(`/v2/tasks/a-random-id`); + expect(response.status).toEqual(200); + expect(response.body.status).toBe('completed'); + expect(response.body.secrets).toBeUndefined(); + }); + }); + + describe('GET /v2/tasks/:taskId/eventstream', () => { + it('should return log messages', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + setImmediate(() => { + observer.next({ + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ], + }); + observer.next({ + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + // emit after this function returned + }); + + let statusCode: any = undefined; + let headers: any = {}; + const responseDataFn = jest.fn(); + + const req = request(app) + .get('/v2/tasks/a-random-id/eventstream') + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as any); + + res.on('data', chunk => { + responseDataFn(chunk.toString()); + + // the server expects the client to abort the request + if (chunk.includes('completion')) { + req.abort(); + } + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + expect(responseDataFn).toHaveBeenCalledTimes(2); + expect(responseDataFn).toHaveBeenCalledWith(`event: log +data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} + +`); + expect(responseDataFn).toHaveBeenCalledWith(`event: completion +data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} + +`); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + }); + expect(subscriber!.closed).toBe(true); + }); + + it('should return log messages with after query', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + setImmediate(() => { + observer.next({ + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + }); + + let statusCode: any = undefined; + let headers: any = {}; + + const req = request(app) + .get('/v2/tasks/a-random-id/eventstream') + .query({ after: 10 }) + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as any); + + res.on('data', () => { + // close immediately + req.abort(); + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + after: 10, + }); + + expect(subscriber!.closed).toBe(true); + }); + }); + + describe('GET /v2/tasks/:taskId/events', () => { + it('should return log messages', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + observer.next({ + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + + const response = await request(app).get( + '/v2/tasks/a-random-id/events', + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: 0, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + }); + expect(subscriber!.closed).toBe(true); + }); + + it('should return log messages with after query', async () => { + let subscriber: ZenObservable.SubscriptionObserver; + ( + taskBroker.event$ as jest.Mocked['event$'] + ).mockImplementation(() => { + return new ObservableImpl(observer => { + subscriber = observer; + observer.next({ events: [] }); + }); + }); + + const response = await request(app) + .get('/v2/tasks/a-random-id/events') + .query({ after: 10 }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + after: 10, + }); + expect(subscriber!.closed).toBe(true); + }); + }); + + describe('GET /v2/autocomplete/:provider/:resource', () => { + let handleAutocompleteRequest: AutocompleteHandler; + + beforeEach(async () => { + handleAutocompleteRequest = jest.fn().mockResolvedValue({ + results: [{ title: 'blob' }], + }); + + const router = await createRouter({ + logger: loggerToWinstonLogger(mockServices.logger.mock()), + config: new ConfigReader({}), + database: createDatabase(), + catalogClient, + reader: mockUrlReader, + taskBroker, + permissions: permissionApi, + auth, + httpAuth, + discovery, + autocompleteHandlers: { + 'test-provider': handleAutocompleteRequest, + }, + }); + + app = express().use(router).use(mockErrorHandler()); + }); + + it('should throw an error when the provider is not registered', async () => { + const response = await request(app) + .post('/v2/autocomplete/unknown-provider/resource') + .send({ + token: 'token', + context: {}, + }); + + expect(response.status).toEqual(400); + expect(response.body).toEqual( + expect.objectContaining({ + error: { + message: 'Unsupported provider: unknown-provider', + name: 'InputError', + }, + }), + ); + }); + + it('should call the autocomplete handler', async () => { + const context = { mock: 'context' }; + const mockToken = 'mocktoken'; + + const response = await request(app) + .post('/v2/autocomplete/test-provider/resource') + .send({ + token: mockToken, + context, + }); + + expect(response.status).toEqual(200); + + expect(response.body).toEqual({ results: [{ title: 'blob' }] }); + expect(handleAutocompleteRequest).toHaveBeenCalledWith({ + token: mockToken, + context, + resource: 'resource', + }); + }); + }); + }); + }, +); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 8258d8c36f..def470252d 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -113,7 +113,11 @@ import { HostDiscovery } from '@backstage/backend-defaults/discovery'; import { convertFiltersToRecord, convertGlobalsToRecord, + extractFilterMetadata, + extractGlobalFunctionMetadata, + extractGlobalValueMetadata, } from '../util/templating'; +import { createDefaultFilters } from '../lib/templating/filters/createDefaultFilters'; /** * @@ -371,6 +375,7 @@ export async function createRouter( } const actionRegistry = new TemplateActionRegistry(); + const templateExtensions = { additionalTemplateFilters: convertFiltersToRecord( additionalTemplateFilters, @@ -1109,6 +1114,18 @@ export async function createRouter( }); res.status(200).json({ results }); + }) + .get('/v2/template-extensions', async (_req, res) => { + res.status(200).json({ + filters: { + ...extractFilterMetadata(createDefaultFilters({ integrations })), + ...extractFilterMetadata(additionalTemplateFilters), + }, + globals: { + functions: extractGlobalFunctionMetadata(additionalTemplateGlobals), + values: extractGlobalValueMetadata(additionalTemplateGlobals), + }, + }); }); const app = express(); diff --git a/plugins/scaffolder-backend/src/util/templating.ts b/plugins/scaffolder-backend/src/util/templating.ts index 2607d5bef6..3318b64fdf 100644 --- a/plugins/scaffolder-backend/src/util/templating.ts +++ b/plugins/scaffolder-backend/src/util/templating.ts @@ -197,10 +197,15 @@ export function extractGlobalFunctionMetadata( for (const global of globals) { if (isGlobalFunction(global)) { - const metadata: any = { - description: global.description, - examples: global.examples, - }; + const metadata: any = {}; + + if (global.description) { + metadata.description = global.description; + } + + if (global.examples) { + metadata.examples = global.examples; + } if (global.schema) { metadata.schema = convertZodFunctionToJsonSchema(global.schema(z)); diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index 42097d4520..d89806e658 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.1-next.0 + - @backstage/backend-test-utils@1.3.2-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/types@1.2.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 2dcb8b8bbf..8bf45ac0b4 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.2.0", + "version": "0.2.1-next.0", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 2cb1c30072..024b6df9bb 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-node +## 0.8.1-next.0 + +### Patch Changes + +- 497d47a: Document the internal built-in filters, and ensure that the types are validated when using `createTemplateFilter` and `createTemplateGlobalFunction` from the `zod` schema. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.10 + ## 0.8.0 ### Minor Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index a3c2921be9..a6d459f9ac 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.8.0", + "version": "0.8.1-next.0", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 0b16b94983..4525edc3ce 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-react +## 1.15.0-next.0 + +### Minor Changes + +- 5890016: add api to retrieve template extensions info from scaffolder-backend + +### Patch Changes + +- 6ed42b7: Scaffolding - Template card - button to show template entity detail +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-scaffolder-common@1.5.10 + ## 1.14.6 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 6247bbe1d3..4a47eb3c9f 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.14.6", + "version": "1.15.0-next.0", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder-react/report.api.md b/plugins/scaffolder-react/report.api.md index d62a2b43d4..23a8a5975d 100644 --- a/plugins/scaffolder-react/report.api.md +++ b/plugins/scaffolder-react/report.api.md @@ -55,11 +55,8 @@ export type Action = { examples?: ActionExample[]; }; -// @public -export type ActionExample = { - description: string; - example: string; -}; +// @public @deprecated +export type ActionExample = ScaffolderUsageExample; // @public export function createScaffolderFieldExtension< @@ -168,6 +165,15 @@ export type LayoutTemplate = NonNullable< // @public export type ListActionsResponse = Array; +// @public +export type ListTemplateExtensionsResponse = { + filters: Record; + globals: { + functions: Record; + values: Record; + }; +}; + // @public export type LogEvent = { type: 'log' | 'completion' | 'cancelled' | 'recovered'; @@ -241,6 +247,7 @@ export interface ScaffolderApi { tasks: ScaffolderTask[]; totalTasks?: number; }>; + listTemplateExtensions?(): Promise; retry?(taskId: string): Promise; scaffold( options: ScaffolderScaffoldOptions, @@ -492,6 +499,13 @@ export type ScaffolderTaskStatus = | 'processing' | 'skipped'; +// @public +export type ScaffolderUsageExample = { + description?: string; + example: string; + notes?: string; +}; + // @public export interface ScaffolderUseTemplateSecrets { // (undocumented) @@ -523,6 +537,33 @@ export type TaskStream = { output?: ScaffolderTaskOutput; }; +// @public +export type TemplateFilter = { + description?: string; + schema?: { + input?: JSONSchema7; + arguments?: JSONSchema7[]; + output?: JSONSchema7; + }; + examples?: ScaffolderUsageExample[]; +}; + +// @public +export type TemplateGlobalFunction = { + description?: string; + schema?: { + arguments?: JSONSchema7[]; + output?: JSONSchema7; + }; + examples?: ScaffolderUsageExample[]; +}; + +// @public +export type TemplateGlobalValue = { + description?: string; + value: JsonValue; +}; + // @public (undocumented) export type TemplateGroupFilter = { title?: React.ReactNode; diff --git a/plugins/scaffolder-react/src/api/types.ts b/plugins/scaffolder-react/src/api/types.ts index 8e4d989a55..cb73704010 100644 --- a/plugins/scaffolder-react/src/api/types.ts +++ b/plugins/scaffolder-react/src/api/types.ts @@ -45,15 +45,24 @@ export type ScaffolderTask = { }; /** - * A single action example + * A single scaffolder usage example * * @public */ -export type ActionExample = { - description: string; +export type ScaffolderUsageExample = { + description?: string; example: string; + notes?: string; }; +/** + * A single action example + * + * @public + * @deprecated in favor of ScaffolderUsageExample + */ +export type ActionExample = ScaffolderUsageExample; + /** * The response shape for a single action in the `listActions` call to the `scaffolder-backend` * @@ -76,6 +85,58 @@ export type Action = { */ export type ListActionsResponse = Array; +/** + * The response shape for a single filter in the `listTemplateExtensions` call to the `scaffolder-backend` + * + * @public + */ +export type TemplateFilter = { + description?: string; + schema?: { + input?: JSONSchema7; + arguments?: JSONSchema7[]; + output?: JSONSchema7; + }; + examples?: ScaffolderUsageExample[]; +}; + +/** + * The response shape for a single global function in the `listTemplateExtensions` call to the `scaffolder-backend` + * + * @public + */ +export type TemplateGlobalFunction = { + description?: string; + schema?: { + arguments?: JSONSchema7[]; + output?: JSONSchema7; + }; + examples?: ScaffolderUsageExample[]; +}; + +/** + * The response shape for a single global value in the `listTemplateExtensions` call to the `scaffolder-backend` + * + * @public + */ +export type TemplateGlobalValue = { + description?: string; + value: JsonValue; +}; + +/** + * The response shape for the `listTemplateExtensions` call to the `scaffolder-backend` + * + * @public + */ +export type ListTemplateExtensionsResponse = { + filters: Record; + globals: { + functions: Record; + values: Record; + }; +}; + /** @public */ export type ScaffolderOutputLink = { title?: string; @@ -236,6 +297,11 @@ export interface ScaffolderApi { */ listActions(): Promise; + /** + * Returns a structure describing the available templating extensions. + */ + listTemplateExtensions?(): Promise; + streamLogs(options: ScaffolderStreamLogsOptions): Observable; dryRun?(options: ScaffolderDryRunOptions): Promise; diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 2d02c91ec0..8ba31c4ab8 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-scaffolder +## 1.30.0-next.0 + +### Minor Changes + +- 5890016: add api to retrieve template extensions info from scaffolder-backend + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration@1.16.2 + - @backstage/integration-react@1.2.5 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-react@0.4.32 + - @backstage/plugin-scaffolder-common@1.5.10 + ## 1.29.0 ### Minor Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index f03f9e5a23..dc8cd0ebe3 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.29.0", + "version": "1.30.0-next.0", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder/report.api.md b/plugins/scaffolder/report.api.md index c51423ed83..379e6a98b9 100644 --- a/plugins/scaffolder/report.api.md +++ b/plugins/scaffolder/report.api.md @@ -25,6 +25,7 @@ import { JSX as JSX_2 } from 'react'; import { LayoutOptions as LayoutOptions_2 } from '@backstage/plugin-scaffolder-react'; import { LayoutTemplate as LayoutTemplate_2 } from '@backstage/plugin-scaffolder-react'; import { ListActionsResponse as ListActionsResponse_2 } from '@backstage/plugin-scaffolder-react'; +import { ListTemplateExtensionsResponse } from '@backstage/plugin-scaffolder-react'; import { LogEvent as LogEvent_2 } from '@backstage/plugin-scaffolder-react'; import { Observable } from '@backstage/types'; import { PathParams } from '@backstage/core-plugin-api'; @@ -561,6 +562,8 @@ export class ScaffolderClient implements ScaffolderApi_2 { totalTasks?: number; }>; // (undocumented) + listTemplateExtensions(): Promise; + // (undocumented) retry?(taskId: string): Promise; // (undocumented) scaffold( diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 01e163bb30..85450b012a 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -24,6 +24,7 @@ import { ResponseError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ListActionsResponse, + ListTemplateExtensionsResponse, LogEvent, ScaffolderApi, ScaffolderDryRunOptions, @@ -324,6 +325,17 @@ export class ScaffolderClient implements ScaffolderApi { return await response.json(); } + async listTemplateExtensions(): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); + const response = await this.fetchApi.fetch( + `${baseUrl}/v2/template-extensions`, + ); + if (!response.ok) { + throw ResponseError.fromResponse(response); + } + return response.json(); + } + async cancelTask(taskId: string): Promise { const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}/cancel`; diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 8700e8bc91..fbd205b1ea 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 0.4.0 ### Minor Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 9ca1630a7c..eab306e7ec 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.4.0", + "version": "0.4.1-next.0", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index ec5995601d..621ce2b7e0 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend +## 2.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-permission-node@0.9.0 + - @backstage/plugin-search-backend-node@1.3.9 + - @backstage/backend-openapi-utils@0.5.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 2.0.0 ### Major Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 91acb56855..33543eafc5 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "2.0.0", + "version": "2.0.1-next.0", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 92aa67b880..e6804c09d2 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search +## 1.4.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + ## 1.4.24 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 7e12ef21d0..373a7b7033 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.24", + "version": "1.4.25-next.0", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index b9bff49e81..658d70e232 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.47-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.29.0-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/plugin-techdocs@1.12.5-next.0 + - @backstage/core-app-api@1.16.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration-react@1.2.5 + - @backstage/test-utils@1.7.6 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-techdocs-react@1.2.15 + ## 1.0.46 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 9071a86a93..e9ed1850bc 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.46", + "version": "1.0.47-next.0", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 60246c4903..ef45686374 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-techdocs-backend +## 2.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.1-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-techdocs-common@0.1.0 + ## 2.0.0 ### Major Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 4f94659b99..1b0d3989b1 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "2.0.0", + "version": "2.0.1-next.0", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 1f561240ac..e9371862a5 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-node +## 1.13.2-next.0 + +### Patch Changes + +- 304a422: Update AWS file retrieval logic from storing file in buffer array to piping to res for better memory efficiency. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + ## 1.13.1 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 059039f7c0..3ff6dc7325 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.13.1", + "version": "1.13.2-next.0", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 9290407afb..77993703fd 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-techdocs +## 1.12.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration@1.16.2 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.15 + ## 1.12.4 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 6f4069e1bb..6a96f9144d 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.12.4", + "version": "1.12.5-next.0", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index c558bc4cdc..c543c24a04 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-user-settings-backend +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-auth-node@0.6.1 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.18 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.3.0 ### Minor Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index c5b31476d5..20e20c20a1 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.3.0", + "version": "0.3.1-next.0", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 5fe4af28b3..d3d089b122 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-user-settings +## 0.8.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.16.0 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/theme@0.6.4 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.11 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.8.20 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 2d36c291ea..b2b7836226 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.20", + "version": "0.8.21-next.0", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", diff --git a/yarn.lock b/yarn.lock index ae8f7ed17d..a230f0154a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3794,7 +3794,7 @@ __metadata: "@types/react-dom": ^18.0.0 chalk: ^5.4.1 clsx: ^2.1.1 - eslint-plugin-storybook: ^0.11.6 + eslint-plugin-storybook: ^0.11.4 glob: ^11.0.1 globals: ^15.11.0 lightningcss: ^1.29.1 @@ -3898,7 +3898,7 @@ __metadata: "@backstage/theme": "workspace:^" "@backstage/types": "workspace:^" "@manypkg/get-packages": ^1.1.3 - "@module-federation/enhanced": ^0.8.0 + "@module-federation/enhanced": ^0.9.0 "@octokit/graphql": ^5.0.0 "@octokit/graphql-schema": ^13.7.0 "@octokit/oauth-app": ^4.2.0 @@ -4140,6 +4140,27 @@ __metadata: languageName: unknown linkType: soft +"@backstage/core-compat-api@npm:^0.4.0": + version: 0.4.0 + resolution: "@backstage/core-compat-api@npm:0.4.0" + dependencies: + "@backstage/core-plugin-api": ^1.10.5 + "@backstage/frontend-plugin-api": ^0.10.0 + "@backstage/plugin-catalog-react": ^1.16.0 + "@backstage/version-bridge": ^1.0.11 + lodash: ^4.17.21 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 783ee24e7c9b7a495201049fc239de18328c6f02d192e8922d3adeb8c8e9a7bb13431f6b341a1464a885f88af4da7fbdff02f93061274597375fcfd32e54cb42 + languageName: node + linkType: hard + "@backstage/core-compat-api@workspace:^, @backstage/core-compat-api@workspace:packages/core-compat-api": version: 0.0.0-use.local resolution: "@backstage/core-compat-api@workspace:packages/core-compat-api" @@ -4177,6 +4198,84 @@ __metadata: languageName: unknown linkType: soft +"@backstage/core-components@^0.17.0, @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: + "@backstage/app-defaults": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@backstage/version-bridge": "workspace:^" + "@dagrejs/dagre": ^1.1.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": ^24.0.0 + "@testing-library/dom": ^10.0.0 + "@testing-library/jest-dom": ^6.0.0 + "@testing-library/react": ^16.0.0 + "@testing-library/user-event": ^14.0.0 + "@types/ansi-regex": ^5.0.0 + "@types/classnames": ^2.2.9 + "@types/d3-selection": ^3.0.1 + "@types/d3-shape": ^3.0.1 + "@types/d3-zoom": ^3.0.1 + "@types/google-protobuf": ^3.7.2 + "@types/react": ^18.0.0 + "@types/react-helmet": ^6.1.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-syntax-highlighter": ^15.0.0 + "@types/react-virtualized-auto-sizer": ^1.0.1 + "@types/react-window": ^1.8.5 + "@types/zen-observable": ^0.8.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + cross-fetch: ^4.0.0 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + history: ^5.0.0 + js-yaml: ^4.1.0 + linkify-react: 4.1.3 + linkifyjs: 4.1.3 + lodash: ^4.17.21 + msw: ^1.0.0 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react: ^18.0.2 + react-dom: ^18.0.2 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.7.2 + react-markdown: ^8.0.0 + react-router-dom: ^6.3.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + 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.22.4 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + languageName: unknown + linkType: soft + "@backstage/core-components@npm:^0.13.10": version: 0.13.10 resolution: "@backstage/core-components@npm:0.13.10" @@ -4280,85 +4379,7 @@ __metadata: languageName: node linkType: hard -"@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: - "@backstage/app-defaults": "workspace:^" - "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" - "@backstage/core-app-api": "workspace:^" - "@backstage/core-plugin-api": "workspace:^" - "@backstage/errors": "workspace:^" - "@backstage/test-utils": "workspace:^" - "@backstage/theme": "workspace:^" - "@backstage/version-bridge": "workspace:^" - "@dagrejs/dagre": ^1.1.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": ^24.0.0 - "@testing-library/dom": ^10.0.0 - "@testing-library/jest-dom": ^6.0.0 - "@testing-library/react": ^16.0.0 - "@testing-library/user-event": ^14.0.0 - "@types/ansi-regex": ^5.0.0 - "@types/classnames": ^2.2.9 - "@types/d3-selection": ^3.0.1 - "@types/d3-shape": ^3.0.1 - "@types/d3-zoom": ^3.0.1 - "@types/google-protobuf": ^3.7.2 - "@types/react": ^18.0.0 - "@types/react-helmet": ^6.1.0 - "@types/react-sparklines": ^1.7.0 - "@types/react-syntax-highlighter": ^15.0.0 - "@types/react-virtualized-auto-sizer": ^1.0.1 - "@types/react-window": ^1.8.5 - "@types/zen-observable": ^0.8.0 - ansi-regex: ^6.0.1 - classnames: ^2.2.6 - cross-fetch: ^4.0.0 - d3-selection: ^3.0.0 - d3-shape: ^3.0.0 - d3-zoom: ^3.0.0 - history: ^5.0.0 - js-yaml: ^4.1.0 - linkify-react: 4.1.3 - linkifyjs: 4.1.3 - lodash: ^4.17.21 - msw: ^1.0.0 - pluralize: ^8.0.0 - qs: ^6.9.4 - rc-progress: 3.5.1 - react: ^18.0.2 - react-dom: ^18.0.2 - react-helmet: 6.1.0 - react-hook-form: ^7.12.2 - react-idle-timer: 5.7.2 - react-markdown: ^8.0.0 - react-router-dom: ^6.3.0 - react-sparklines: ^1.7.0 - react-syntax-highlighter: ^15.4.5 - 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.22.4 - peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - react-router-dom: ^6.3.0 - peerDependenciesMeta: - "@types/react": - optional: true - languageName: unknown - linkType: soft - -"@backstage/core-plugin-api@^1.10.0, @backstage/core-plugin-api@^1.8.2, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": +"@backstage/core-plugin-api@^1.10.0, @backstage/core-plugin-api@^1.10.5, @backstage/core-plugin-api@^1.8.2, @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: @@ -4555,7 +4576,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/frontend-plugin-api@workspace:^, @backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api": +"@backstage/frontend-plugin-api@^0.10.0, @backstage/frontend-plugin-api@workspace:^, @backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api": version: 0.0.0-use.local resolution: "@backstage/frontend-plugin-api@workspace:packages/frontend-plugin-api" dependencies: @@ -4589,7 +4610,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/frontend-test-utils@workspace:^, @backstage/frontend-test-utils@workspace:packages/frontend-test-utils": +"@backstage/frontend-test-utils@^0.3.0, @backstage/frontend-test-utils@workspace:^, @backstage/frontend-test-utils@workspace:packages/frontend-test-utils": version: 0.0.0-use.local resolution: "@backstage/frontend-test-utils@workspace:packages/frontend-test-utils" dependencies: @@ -4638,7 +4659,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@^1.1.24, @backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": +"@backstage/integration-react@^1.1.24, @backstage/integration-react@^1.2.5, @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: @@ -5860,7 +5881,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-common@^1.0.20, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": +"@backstage/plugin-catalog-common@^1.0.20, @backstage/plugin-catalog-common@^1.1.3, @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: @@ -5984,7 +6005,48 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-react@^1.14.0, @backstage/plugin-catalog-react@^1.9.3, @backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": +"@backstage/plugin-catalog-react@npm:^1.14.0, @backstage/plugin-catalog-react@npm:^1.16.0, @backstage/plugin-catalog-react@npm:^1.9.3": + version: 1.16.0 + resolution: "@backstage/plugin-catalog-react@npm:1.16.0" + dependencies: + "@backstage/catalog-client": ^1.9.1 + "@backstage/catalog-model": ^1.7.3 + "@backstage/core-compat-api": ^0.4.0 + "@backstage/core-components": ^0.17.0 + "@backstage/core-plugin-api": ^1.10.5 + "@backstage/errors": ^1.2.7 + "@backstage/frontend-plugin-api": ^0.10.0 + "@backstage/frontend-test-utils": ^0.3.0 + "@backstage/integration-react": ^1.2.5 + "@backstage/plugin-catalog-common": ^1.1.3 + "@backstage/plugin-permission-common": ^0.8.4 + "@backstage/plugin-permission-react": ^0.4.32 + "@backstage/types": ^1.2.1 + "@backstage/version-bridge": ^1.0.11 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^24.0.0 + classnames: ^2.2.6 + 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: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 7ac3b023d53e4f4102d73cb5c29db7d6adc47432ec696b1fbc4107d1c44154ae2314ceb048d032e1174a0170251edca482710b1e7fd7ec9de5b55f18c6a3a1ea + languageName: node + linkType: hard + +"@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: @@ -6948,7 +7010,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": +"@backstage/plugin-permission-common@^0.8.4, @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: @@ -6987,7 +7049,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": +"@backstage/plugin-permission-react@^0.4.32, @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: @@ -8479,7 +8541,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/version-bridge@^1.0.10, @backstage/version-bridge@^1.0.7, @backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": +"@backstage/version-bridge@^1.0.10, @backstage/version-bridge@^1.0.11, @backstage/version-bridge@^1.0.7, @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: @@ -11561,46 +11623,46 @@ __metadata: languageName: node linkType: hard -"@module-federation/bridge-react-webpack-plugin@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.8.10" +"@module-federation/bridge-react-webpack-plugin@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.9.0" dependencies: - "@module-federation/sdk": 0.8.10 + "@module-federation/sdk": 0.9.0 "@types/semver": 7.5.8 semver: 7.6.3 - checksum: 4f56daa821d4c21bca79a6301567ed6d13096631d7f300bc45153e948c3f0527a0664ddd8dcca9869cfbe582b2f279717e9416ff51fa47a373adaff20d5b6762 + checksum: b63cf3cc4a5a77f62f3b181633892012caa4b9f409a051a2d2d5a87dd128994244839c680f82b5ddfcf7b590eb1da60dc0b55200a9b2b9346b203094764fe8d4 languageName: node linkType: hard -"@module-federation/data-prefetch@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/data-prefetch@npm:0.8.10" +"@module-federation/data-prefetch@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/data-prefetch@npm:0.9.0" dependencies: - "@module-federation/runtime": 0.8.10 - "@module-federation/sdk": 0.8.10 + "@module-federation/runtime": 0.9.0 + "@module-federation/sdk": 0.9.0 fs-extra: 9.1.0 peerDependencies: react: ">=16.9.0" react-dom: ">=16.9.0" - checksum: 4db6a9a02e732f67c2d9e0a7268ff39746a25432653b6acbd02482c88ba372a837a0e742c813ba632eb7f9e3c7de7ee3f13b2c3862bfab4ee0d53eaea34c68cd + checksum: 855cc95ee60fc7f1e942b16819dfa01201c945cd3b575d25de0426347e81326d3cffee05657af738f6709562d342129d1996fbd67942e9c55f7560ba82bb1fc2 languageName: node linkType: hard -"@module-federation/dts-plugin@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/dts-plugin@npm:0.8.10" +"@module-federation/dts-plugin@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/dts-plugin@npm:0.9.0" dependencies: - "@module-federation/error-codes": 0.8.10 - "@module-federation/managers": 0.8.10 - "@module-federation/sdk": 0.8.10 - "@module-federation/third-party-dts-extractor": 0.8.10 + "@module-federation/error-codes": 0.9.0 + "@module-federation/managers": 0.9.0 + "@module-federation/sdk": 0.9.0 + "@module-federation/third-party-dts-extractor": 0.9.0 adm-zip: ^0.5.10 ansi-colors: ^4.1.3 axios: ^1.7.4 chalk: 3.0.0 fs-extra: 9.1.0 isomorphic-ws: 5.0.0 - koa: 2.15.3 + koa: 2.15.4 lodash.clonedeepwith: 4.5.0 log4js: 6.9.1 node-schedule: 2.1.1 @@ -11612,24 +11674,24 @@ __metadata: peerDependenciesMeta: vue-tsc: optional: true - checksum: 35c41348113f054d09d3303ac5d6f0c9e65f453ba25c0319e584c5e4f779dc499e476ad84a5579238f0e34127dd6c018dc162334e0adc4c9fdabcc858432efa3 + checksum: 57fc4c6cd4709310e7cd2813a0de822acf230da2eb5d3c3edc67e022d513f51f69f99b14790dc83264be225f246a91b18490cfcddbdfacb549e1d7fa0f432d0f languageName: node linkType: hard -"@module-federation/enhanced@npm:^0.8.0": - version: 0.8.10 - resolution: "@module-federation/enhanced@npm:0.8.10" +"@module-federation/enhanced@npm:^0.9.0": + version: 0.9.0 + resolution: "@module-federation/enhanced@npm:0.9.0" dependencies: - "@module-federation/bridge-react-webpack-plugin": 0.8.10 - "@module-federation/data-prefetch": 0.8.10 - "@module-federation/dts-plugin": 0.8.10 - "@module-federation/error-codes": 0.8.10 - "@module-federation/inject-external-runtime-core-plugin": 0.8.10 - "@module-federation/managers": 0.8.10 - "@module-federation/manifest": 0.8.10 - "@module-federation/rspack": 0.8.10 - "@module-federation/runtime-tools": 0.8.10 - "@module-federation/sdk": 0.8.10 + "@module-federation/bridge-react-webpack-plugin": 0.9.0 + "@module-federation/data-prefetch": 0.9.0 + "@module-federation/dts-plugin": 0.9.0 + "@module-federation/error-codes": 0.9.0 + "@module-federation/inject-external-runtime-core-plugin": 0.9.0 + "@module-federation/managers": 0.9.0 + "@module-federation/manifest": 0.9.0 + "@module-federation/rspack": 0.9.0 + "@module-federation/runtime-tools": 0.9.0 + "@module-federation/sdk": 0.9.0 btoa: ^1.2.1 upath: 2.0.1 peerDependencies: @@ -11643,61 +11705,61 @@ __metadata: optional: true webpack: optional: true - checksum: 24f38c4e879d6dbbb1fe3975a990b1182da54259fe7eb6b11c028b31a779a84c291aca61f39c4d15f805071513fb3000a96504ae1c94103fafb0143793db4809 + checksum: cdb2d5b0355ffa8b681b8f66063fee06f8fe1589d9d1f7a20956d0f0dbace239087d97b327cacaf5e4327c15ca389e79b8d58a05ce7f7b02ebd3a5804e4872bb languageName: node linkType: hard -"@module-federation/error-codes@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/error-codes@npm:0.8.10" - checksum: e36bcab1afd1d32fcedb06e7a370a11d64dc9db02b8e84c4e770d1dfaef1c8e7f9fe67cbe12ddd95d823a36964469f213d2cd194c633e2f126d7ce09e8216fd9 +"@module-federation/error-codes@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/error-codes@npm:0.9.0" + checksum: 2319d5a6daa44735635feb7cd99bb2da781bbe7623cd435fad5dd9bd2b15d3ace055e989ae9411bf22aaeb91ea2790a4c5de08d033ee44beaece1fe74eedc3aa languageName: node linkType: hard -"@module-federation/inject-external-runtime-core-plugin@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/inject-external-runtime-core-plugin@npm:0.8.10" +"@module-federation/inject-external-runtime-core-plugin@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/inject-external-runtime-core-plugin@npm:0.9.0" peerDependencies: - "@module-federation/runtime-tools": 0.8.10 - checksum: 376d4e520ff9f032a3524d66181c17c4009218b8b04aac1ffa310e779cf555447a026dbaa428771551307f870589b9883a5211acc350fce729d7ca2d0a9a8ae5 + "@module-federation/runtime-tools": 0.9.0 + checksum: 7821cc6bc2cd865ec4b0496c1c53e009611e1bf7571ca6be7e852e79de418df3952ce02b02f7a6773d833638bccbc2b3b853ebd22869a6abdaec717dc5996a8a languageName: node linkType: hard -"@module-federation/managers@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/managers@npm:0.8.10" +"@module-federation/managers@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/managers@npm:0.9.0" dependencies: - "@module-federation/sdk": 0.8.10 + "@module-federation/sdk": 0.9.0 find-pkg: 2.0.0 fs-extra: 9.1.0 - checksum: d72654f3738b95e64379d8e5e5286f6788ce524ff4a10a37acac99a6196f193882d000a891f8412f550c1bc9e914e10b2b52529766ee303acaef1abbd9667ef1 + checksum: 5d0891cc22148a6e153b9309c656c8a00088cb7b787b5ee753b559edd5cf19005791aa181f365986bd979a2a52229b9969cee512ace5aeab33e29886732f7a97 languageName: node linkType: hard -"@module-federation/manifest@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/manifest@npm:0.8.10" +"@module-federation/manifest@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/manifest@npm:0.9.0" dependencies: - "@module-federation/dts-plugin": 0.8.10 - "@module-federation/managers": 0.8.10 - "@module-federation/sdk": 0.8.10 + "@module-federation/dts-plugin": 0.9.0 + "@module-federation/managers": 0.9.0 + "@module-federation/sdk": 0.9.0 chalk: 3.0.0 find-pkg: 2.0.0 - checksum: f4d2e0dadfc30b08ceb5624cf5fba05a2f282ded78ec36157642c103faac1b90973eb5b8856cfbc65a6c0d9210eeec6fd5b58d015382c9d75f240d6f1d96b8bd + checksum: 516b4bdfaaecee8d75c1a1bb7f0833d3b7f3fda228dad9879debf0ddb5a3550aebaa1b8d05ce9139ac2490a01d9d10d86b6881c1ed0d982963a91f353759143f languageName: node linkType: hard -"@module-federation/rspack@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/rspack@npm:0.8.10" +"@module-federation/rspack@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/rspack@npm:0.9.0" dependencies: - "@module-federation/bridge-react-webpack-plugin": 0.8.10 - "@module-federation/dts-plugin": 0.8.10 - "@module-federation/inject-external-runtime-core-plugin": 0.8.10 - "@module-federation/managers": 0.8.10 - "@module-federation/manifest": 0.8.10 - "@module-federation/runtime-tools": 0.8.10 - "@module-federation/sdk": 0.8.10 + "@module-federation/bridge-react-webpack-plugin": 0.9.0 + "@module-federation/dts-plugin": 0.9.0 + "@module-federation/inject-external-runtime-core-plugin": 0.9.0 + "@module-federation/managers": 0.9.0 + "@module-federation/manifest": 0.9.0 + "@module-federation/runtime-tools": 0.9.0 + "@module-federation/sdk": 0.9.0 peerDependencies: "@rspack/core": ">=0.7" typescript: ^4.9.0 || ^5.0.0 @@ -11707,17 +11769,17 @@ __metadata: optional: true vue-tsc: optional: true - checksum: c56c9abf0264a85889ee3a75987fc9ff79265dc8561cea80509b7128d0a53dfbdc2f908f54dc0d4a9bbfbf020620b8f6e7439a927b80cf3c162654d2f8f077d4 + checksum: 51da280d30b8fdaad01f01b88dfe4689f2d1d0a33c19d2c1a3e87c8c6781719cf7254d30ccf9542691bb9b1511ea13e2758ac5f33f7801753f583082c1880b86 languageName: node linkType: hard -"@module-federation/runtime-core@npm:0.6.18": - version: 0.6.18 - resolution: "@module-federation/runtime-core@npm:0.6.18" +"@module-federation/runtime-core@npm:0.6.21": + version: 0.6.21 + resolution: "@module-federation/runtime-core@npm:0.6.21" dependencies: - "@module-federation/error-codes": 0.8.10 - "@module-federation/sdk": 0.8.10 - checksum: d5e9cdc4066acd5127b010310bdd2f82e9e95b0e036d8841887b2c48986a76688e06d562f3095f8ed36a89468459695fb8b5124f9cac383f6e1f34972f3c3d7a + "@module-federation/error-codes": 0.9.0 + "@module-federation/sdk": 0.9.0 + checksum: 57cfbe06553bc7639472e2ddc3e6af11c208fedccd34f9c0b1f3d5378800fdfcdf832b233aa237d05516f94925a09d40edfa4f091e6d6a31657de39da166f803 languageName: node linkType: hard @@ -11731,13 +11793,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/runtime-tools@npm:0.8.10" +"@module-federation/runtime-tools@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/runtime-tools@npm:0.9.0" dependencies: - "@module-federation/runtime": 0.8.10 - "@module-federation/webpack-bundler-runtime": 0.8.10 - checksum: 58006c48130618fe51719f9e3ed82d4ebfbe421fabc69a6aa1b67808474fed860920ef0fc4fee63522e3b58cfa9cba787fc4ef513337745bc4d573749a7939cd + "@module-federation/runtime": 0.9.0 + "@module-federation/webpack-bundler-runtime": 0.9.0 + checksum: 0fa54a792c9b01afc102ee3effaf3836f76d79d78a731a57eeec9f3bfdedd1fb2f4aa0f4b8e6be703516c44516757b6741d723c32db7f5a1349859735bf15040 languageName: node linkType: hard @@ -11750,14 +11812,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/runtime@npm:0.8.10" +"@module-federation/runtime@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/runtime@npm:0.9.0" dependencies: - "@module-federation/error-codes": 0.8.10 - "@module-federation/runtime-core": 0.6.18 - "@module-federation/sdk": 0.8.10 - checksum: 80ae75e206e047a9889200bec428e647904e73dab0af861bb9800a7f75e7458174fdef8057fd7518b9de76db7b7083abeabc17e9362d51d65a6c7c1be9df2836 + "@module-federation/error-codes": 0.9.0 + "@module-federation/runtime-core": 0.6.21 + "@module-federation/sdk": 0.9.0 + checksum: 5c62d75a3b369849b608babce78b13b97a4846db3adb488e687e465b523f419758413037512d9f2661bbf785c1951d7b06afa7b4c0150eccc591e08f6fd2e384 languageName: node linkType: hard @@ -11768,23 +11830,23 @@ __metadata: languageName: node linkType: hard -"@module-federation/sdk@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/sdk@npm:0.8.10" +"@module-federation/sdk@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/sdk@npm:0.9.0" dependencies: isomorphic-rslog: 0.0.7 - checksum: e4652b4bbb7e77a52520fef9e09574881e60468120fec2f1110584b0280dd671c2efb24dd6a0bd75193599a7c9d6d0558334e5785eb6e5b506e592d115213eb9 + checksum: 032d0554c1613b4679569431d30f81593a62040e696cca370e27143d2230e801249c2e703dc468a3ecc1936ea7e72dba75e25dfa5e1fd983a3c47713d25aa982 languageName: node linkType: hard -"@module-federation/third-party-dts-extractor@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/third-party-dts-extractor@npm:0.8.10" +"@module-federation/third-party-dts-extractor@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/third-party-dts-extractor@npm:0.9.0" dependencies: find-pkg: 2.0.0 fs-extra: 9.1.0 resolve: 1.22.8 - checksum: a177d012e253cb711a766fbdca9394bc5575727d3752c8efc2e58187064bb7fef886c478bf2b2e13de454ce32af13891ea314119f23693cf4eb63b93a8987b31 + checksum: accece159cded982499f239b8e7cc7410249ee91b28584652aebb7cc13d07b540939fa9af7ac2609ca20c4b7d2bd113d7120ad535722a3178c404bf19b8ed048 languageName: node linkType: hard @@ -11798,13 +11860,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.8.10": - version: 0.8.10 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.8.10" +"@module-federation/webpack-bundler-runtime@npm:0.9.0": + version: 0.9.0 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.9.0" dependencies: - "@module-federation/runtime": 0.8.10 - "@module-federation/sdk": 0.8.10 - checksum: 062d6211a67541bcd503a6642cfa912fa62774328fdf2bffa449c8f45d33fe7b76fe15ac9fe582e8819223e45064032976dc43416b66390e94cbcc67472ba2bb + "@module-federation/runtime": 0.9.0 + "@module-federation/sdk": 0.9.0 + checksum: 40ddcca91830e5e0187254b5ad8de42e3caa0f3f0c044dab9af96038bd7c5bb12bad3f43c1927d3c1167a6764715c0f68644867478fb53019f042c608599625a languageName: node linkType: hard @@ -19318,10 +19380,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:1.0.6, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5, @types/estree@npm:^1.0.6": - version: 1.0.6 - resolution: "@types/estree@npm:1.0.6" - checksum: 8825d6e729e16445d9a1dd2fb1db2edc5ed400799064cd4d028150701031af012ba30d6d03fe9df40f4d7a437d0de6d2b256020152b7b09bde9f2e420afdffd9 +"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5, @types/estree@npm:^1.0.6": + version: 1.0.7 + resolution: "@types/estree@npm:1.0.7" + checksum: d9312b7075bdd08f3c9e1bb477102f5458aaa42a8eec31a169481ce314ca99ac716645cff4fca81ea65a2294b0276a0de63159d1baca0f8e7b5050a92de950ad languageName: node linkType: hard @@ -19332,6 +19394,13 @@ __metadata: languageName: node linkType: hard +"@types/estree@npm:1.0.6": + version: 1.0.6 + resolution: "@types/estree@npm:1.0.6" + checksum: 8825d6e729e16445d9a1dd2fb1db2edc5ed400799064cd4d028150701031af012ba30d6d03fe9df40f4d7a437d0de6d2b256020152b7b09bde9f2e420afdffd9 + languageName: node + linkType: hard + "@types/expect@npm:^1.20.4": version: 1.20.4 resolution: "@types/expect@npm:1.20.4" @@ -23222,13 +23291,13 @@ __metadata: linkType: hard "axios@npm:^1.0.0, axios@npm:^1.6.0, axios@npm:^1.7.4, axios@npm:^1.7.7": - version: 1.8.3 - resolution: "axios@npm:1.8.3" + version: 1.8.4 + resolution: "axios@npm:1.8.4" dependencies: follow-redirects: ^1.15.6 form-data: ^4.0.0 proxy-from-env: ^1.1.0 - checksum: 85fc8ad7d968e43ea9da5513310637d29654b181411012ee14cc0a4b3662782e6c81ac25eea40b5684f86ed2d8a01fa6fc20b9b48c4da14ef4eaee848fea43bc + checksum: e901dc1730bdcd769839b3d93ae6d6457a53d79b19a0eb623ebfea333441259ab51e63ca118baa47a5156567401466ac739f31087b4ee5e6770ab2e227484538 languageName: node linkType: hard @@ -28184,7 +28253,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-storybook@npm:^0.11.6": +"eslint-plugin-storybook@npm:^0.11.4": version: 0.11.6 resolution: "eslint-plugin-storybook@npm:0.11.6" dependencies: @@ -34362,9 +34431,9 @@ __metadata: languageName: node linkType: hard -"koa@npm:2.15.3": - version: 2.15.3 - resolution: "koa@npm:2.15.3" +"koa@npm:2.15.4": + version: 2.15.4 + resolution: "koa@npm:2.15.4" dependencies: accepts: ^1.3.5 cache-content-type: ^1.0.0 @@ -34389,7 +34458,7 @@ __metadata: statuses: ^1.5.0 type-is: ^1.6.16 vary: ^1.1.2 - checksum: 7c3537443b1a588cf5c3e5554b914ff2bad510323d22b41861d5e0c97d47e9c5997965f303ede8be8bd83d309a4eea1f82cd45d35d6838bc21bb1bb6a90d5d25 + checksum: cd2bf396fa5f575504d4c1b9e23ba64b9c58cbfa8276dbc4d99ce527ae4f238c38eafb811c22986c2f0f475a61734d05a8f9f5873b22432d78344b4517d83a69 languageName: node linkType: hard @@ -42504,7 +42573,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:7.8.1, rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": +"rxjs@npm:7.8.1": version: 7.8.1 resolution: "rxjs@npm:7.8.1" dependencies: @@ -42522,6 +42591,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" + dependencies: + tslib: ^2.1.0 + checksum: 2f233d7c832a6c255dabe0759014d7d9b1c9f1cb2f2f0d59690fd11c883c9826ea35a51740c06ab45b6ade0d9087bde9192f165cba20b6730d344b831ef80744 + languageName: node + linkType: hard + "sade@npm:^1.7.3": version: 1.8.1 resolution: "sade@npm:1.8.1"